[PATCH] Move all am-related reloption code into src/backend/access/[am-name] and get rid of relopt_kind for custom AM
(Please see Attribution notice at the bottom of the letter)
Hi! Some time ago I've send a proposal, about removing all am-related code
from src/backend/access/common/reloptions.c and move it into somewhere inside
am code. It was in
/messages/by-id/4636505.Bu9AKW1Kzc@nataraj-amd64
thread.
Now I have a patch that is ready
Before explaining what have been done, I need to define some concepts, so it
would be easy to speak about them in the further explanation.
Reloptions values in postgres actually exists in four representations:
DefList*, TextArray[], Bytea and so called Values.
- DefList representation is a list of DefElem, it is the representation in
which reloptions comes from syntax analyzer, when relation is created or
altered, and also this representation that is needed for pg_dump to dump a
create statement that creates this relation.
- TextArray[] representation is a way, how reloptions are stored in
pg_catalog. It is TEXT[] attribute in the pg_catalog table and from the poinf
o view of postgres code it is Datum array of TEXT Datums, when read into the
memory
- Bytea (or I will also call it Binary) representation, is a representation
with which relation code really works. It has C-structure inside in which
fixed-length options are sored, some space is reserved for storing varlen
values after the structure, and certainly it has BYTEA header in front of all
of it. It is cached, it is fast to access. It is a good stuff.
- Values (or in some cases RawValues) -- internal representation of option
parser. Technically it is List of option_value structures (old name for it is
relopt_value). This representation is used for parsing and validating options.
Actually all the convertations between representations listed above are dove
through Values representation. Values are called RawValues when Values List
were already created, but it was not parsed yet, and all option values are in
raw state: represented as a text.
DefList and TextArray representations are converted in both ways(always
through Values representation): we need DefList -> TextArray to create or
change entry in the pg_catalog, and need TextArray -> DefList for pg_dump
Bytea representation is converted from TextArray representation (also through
Values representation). When relation code, tries to get cached
Bytea representation for certain relation, and none is actually cached, then
cache code calls extractRelOptions function that fetches entry from pg_catalog
for that relation, gets reloptions TextArray[], converts it into Bytea and
cache it.
Each reloption has it's own definition. This definitions tells how this option
should be parsed, validated and converted. Before my patch this information
were stored in relopt_gen and relopt_parse_elt structures. In my patch all
this information were moved into option_definition_basic structure (that is
actually old relopt_gen + relopt_parse_elt)
The set of options definition I would call a options catalog. Before my patch
there was one global options catalog (actually it was divided into parts by
data types and has another part for dynamically added options, but
nevertheless, it was global). After my patch there would be a separate options
catalog for each relation type. For AM-relation this catalog would be
available via AM-handler. for other relation types it would be still kept in
reloption.c
Now I am ready to tell what actually have been done in this patch:
1. I moved options definitions from global lists from reloptions.c
[type name]RelOpts to lists that are kept inside AM code. One list per AM.
heap options, toast options and options for all other relation types that are
not accessible through AccessMethod, all were kept inside reloptions.c, but
now it is one list for each relation type, not a global one
2. Each AccessMethod had amoptions function that was responsible for
converting reloptions from TextArray[] representation into Bytea
representaions. This function used functions from reloptions.c and also had
an array of relopt_parse_elt that defines how parsed reloption data should be
packed inside Bytea chunk. I've moved data from relopt_parse_elt array into
option definitions that are stored in the catalog (since catalog are now
defined inside the AccessMethod we have access to a structure of Bytea
representation at the place where we define option catalog)
3. Speaking of amoptions function, I've completely removed it, since we do not
need it. For developers of Access Methods who still want to do some custom
action with just parsed data, I've added postprocess_fun to option catalog. If
this function is defined, it is called by convertation function right after
Bytea representation were created. In postprocess_fun developer can do some
custom validation, or change just parsed data. This feature is used in bloom
index.
4. Instead of amoptions function I've added amrelopt_catalog function to the
Access Method. This function returns option definition catalog for an Access
Method. The catalog is used for processing options.
5. As before, relation cache calls extractRelOptions when it needs options
that were not previously cached.
Before my patch, it created Bytea representations for non-AM relations, and
called amoptions AM function to get Bytea representation for AM relation.
In by patch, it now gets option catalog, (using local functions for non-AM
relations, and using amrelopt_catalog function for AM-relations), and then
use this catalog to convert options from TextArray into Bytea representation.
6. I've added some more featues:
- Now you can set OPTION_DEFINITION_FLAG_FORBID_ALTER flag in definition of the
option, and then postgres will refuse to change option value using ALTER
command. In many indexes, some options are used only for index creation. After
this it's value is saved in MetaPage, and used from there. Nevertheless user
is allowed to change option value, though it affects nothing, one need to
recreate index to really change such value. Now using this flag we can prevent
user from getting an illusion that he successfully changed option value.
- After applying my patch if you try to set toast. option for table that does
not have toast, you will get an error. Before postgres will accept toast
option for a table without a toast, but this value will be lost. This is bad
behavior, so now postgres will throw an error in this case.
- I've noticed that all string relation options in postgres are technically
enum options. So I've added enum option type. This will save some bytes of the
memory and some tacts of the CPU. I also left string option type (through it
is not used now). A. Korotkov said that it might be needed later for storing
patterns, xml/json paths or something like that.
- Added some more tests that will trigger more options code. Though I am
dreaming about more advanced test system, that will allow to check that
certain value is received somewhere deep in the code.
7. And now the most controversial change. I've got rid of StdRdOptions
structure, in which options for heap, toast, nbtree, hash and spgist were
stored in Bytea representation. In indexes only fillfactor value were actually
used from the whole big structure. So I've created a separate structure for
each relation type. HeapOptions and ToastOptions are very similar to each
other. So common part of creating these catalogs were moved to
add_autovacuum_options function that are called while creating each catalog.
Now to the controversial part: in src/include/utils/rel.h had a lot of
Relation* macroses that used StdRdOptions structure. I've changed them into
View* Heap* and Toast* analogues, and changed the code to use these macroses
instead of Relation* one. But this part of code I least sure of. I'd like to
ask experienced developers to double check it.
8. I've moved all options-abstract code into options.c and options.h file, and
left in reloptions.c and reloptions.h only the code that concerns relation
options. Actually the main idea of this patch was to get this abstract code in
order to use it for custom attribute options later. All the rest just a side
effects :-)
So, before adding this to commitfest I want somebody to give a general look at
it, if the code is OK.
Alvaro Herrera, you once said that you can review the patch...
The patch is available in the attachment. It can be applied to current master
You can also see latest version on my github
https://github.com/dhyannataraj/postgres/tree/reloption_fix
at the reloption_fix branch.
ATTRIBUTION NOTICE: I wrote this patch, when I was an employee in Postgres
Professional company. So this patch should be attributed as patch from
Postgres Pro (or something like that). For some reasons I did not manage to
commit this patch while I left that job. But I got a permission to commit it
even if I left the company.
So my contribution to this patch as a independent developer was final code
cleanup, and writing some more comments. All the rest was a work of Postgres
Pro employee Nikolay Shaplov.
--
Nikolay Shaplov, independent Perl & C/C++ developer. Available for hire.
Attachments:
reloptions6.difftext/x-patch; charset=UTF-8; name=reloptions6.diffDownload
diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
index a75a235..e9ccb56 100644
--- a/contrib/bloom/bloom.h
+++ b/contrib/bloom/bloom.h
@@ -203,7 +203,7 @@ extern IndexBulkDeleteResult *blbulkdelete(IndexVacuumInfo *info,
void *callback_state);
extern IndexBulkDeleteResult *blvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
-extern bytea *bloptions(Datum reloptions, bool validate);
+extern bytea *bloptions(Datum reloptions, bool validate, bool for_alter);
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 858798d..9bd322c 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.
@@ -128,7 +92,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;
@@ -145,6 +109,27 @@ 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.
*/
@@ -465,24 +450,38 @@ BloomInitMetapage(Relation index)
UnlockReleaseBuffer(metaBuffer);
}
-/*
- * Parse reloptions for bloom index, producing a BloomOptions struct.
- */
-bytea *
-bloptions(Datum reloptions, bool validate)
+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);
+ bl_relopt_catalog->struct_size = sizeof(BloomOptions);
+ 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 cbc50f7..7e3bbc0 100644
--- a/contrib/bloom/expected/bloom.out
+++ b/contrib/bloom/expected/bloom.out
@@ -210,3 +210,37 @@ ORDER BY 1;
text_ops | t
(2 rows)
+-- reloptions test
+DROP INDEX bloomidx;
+CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (length=7, col1 = 4);
+SELECT reloptions FROM pg_class WHERE oid = 'bloomidx'::regclass;
+ reloptions
+-------------------
+ {length=7,col1=4}
+(1 row)
+
+-- check for min and max values
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=0);
+ERROR: value 0 out of bounds for option "length"
+DETAIL: Valid values are between "1" and "4096".
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=4097);
+ERROR: value 4097 out of bounds for option "length"
+DETAIL: Valid values are between "1" and "4096".
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=0);
+ERROR: value 0 out of bounds for option "col1"
+DETAIL: Valid values are between "1" and "4095".
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=4096);
+ERROR: value 4096 out of bounds for option "col1"
+DETAIL: Valid values are between "1" and "4095".
+-- check post_validate for colN<lengh
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=10,col1=11);
+ERROR: col0 should not be grater than length
+-- check that ALTERing of reloptions is forbidden
+ALTER INDEX bloomidx SET (length=4);
+ERROR: changing parameter "length" is not allowed
+ALTER INDEX bloomidx SET (col1=4);
+ERROR: changing parameter "col1" is not allowed
+ALTER INDEX bloomidx RESET (length);
+ERROR: changing parameter "length" is not allowed
+ALTER INDEX bloomidx RESET (col1);
+ERROR: changing parameter "col1" is not allowed
diff --git a/contrib/bloom/sql/bloom.sql b/contrib/bloom/sql/bloom.sql
index 2227460..dbac21c 100644
--- a/contrib/bloom/sql/bloom.sql
+++ b/contrib/bloom/sql/bloom.sql
@@ -81,3 +81,23 @@ SELECT opcname, amvalidate(opc.oid)
FROM pg_opclass opc JOIN pg_am am ON am.oid = opcmethod
WHERE amname = 'bloom'
ORDER BY 1;
+
+-- reloptions test
+
+DROP INDEX bloomidx;
+CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (length=7, col1 = 4);
+SELECT reloptions FROM pg_class WHERE oid = 'bloomidx'::regclass;
+-- check for min and max values
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=0);
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=4097);
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=0);
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=4096);
+-- check post_validate for colN<lengh
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=10,col1=11);
+
+-- check that ALTERing of reloptions is forbidden
+
+ALTER INDEX bloomidx SET (length=4);
+ALTER INDEX bloomidx SET (col1=4);
+ALTER INDEX bloomidx RESET (length);
+ALTER INDEX bloomidx RESET (col1);
diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
index ac43c45..0952720 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 735b794..b07399f 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 e24db56..be15189 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -65,7 +65,7 @@ PG_FUNCTION_INFO_V1(postgres_fdw_validator);
Datum
postgres_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid catalog = PG_GETARG_OID(1);
ListCell *cell;
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index b2afdb7..bbd6e1a 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"
@@ -70,6 +70,7 @@ static void union_tuples(BrinDesc *bdesc, BrinMemTuple *a,
BrinTuple *b);
static void brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy);
+void *bringetreloptcatalog (void);
/*
* BRIN handler function: return IndexAmRoutine with access method parameters
@@ -102,7 +103,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;
@@ -112,6 +112,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;
@@ -750,36 +751,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)}
- };
-
- 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.
*/
@@ -1229,3 +1200,25 @@ brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy)
if (vacuum_fsm)
FreeSpaceMapVacuum(idxrel);
}
+
+static options_catalog * brin_relopt_catalog = NULL;
+
+void *
+bringetreloptcatalog (void)
+{
+ if (! brin_relopt_catalog)
+ {
+ brin_relopt_catalog = allocateOptionsCatalog(NULL);
+ brin_relopt_catalog->struct_size = sizeof(BrinOptions);
+
+ 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);
+ }
+ return brin_relopt_catalog;
+}
diff --git a/src/backend/access/common/Makefile b/src/backend/access/common/Makefile
index d4b8132..1ad3514 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 = heaptuple.o indextuple.o printsimple.o printtup.o reloptions.o \
+OBJS = heaptuple.o indextuple.o options.o printsimple.o printtup.o reloptions.o \
scankey.o tupconvert.o tupdesc.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/common/options.c b/src/backend/access/common/options.c
new file mode 100644
index 0000000..3b9a267
--- /dev/null
+++ b/src/backend/access/common/options.c
@@ -0,0 +1,1353 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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);
+void *optionsAllocateBytea(options_catalog *catalog, List *options);
+
+
+/*
+ * 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.
+ */
+
+
+/*
+ * 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.
+ */
+
+options_catalog *
+allocateOptionsCatalog(char *namespace)
+{
+ 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;
+
+ catalog->definitions = NULL;
+ catalog->num = 0;
+ catalog->num_allocated = 0;
+ catalog->struct_size = -1; /* Will Assert if not properly changed */
+ 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 + 1 >= catalog->num_allocated)
+ {
+ MemoryContext oldcxt;
+
+ 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->definitions[catalog->num + 1] = NULL;
+ 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
+ */
+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.
+ */
+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++)
+ {
+ text *option_src = DatumGetTextP(options[i]);
+ option_value * option_dst;
+ char *text_str = VARDATA(option_src);
+ int text_len = VARSIZE(option_src) - 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
+ */
+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.
+ */
+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.
+ */
+void *
+optionsAllocateBytea(options_catalog *catalog, List *options)
+{
+ Size size;
+ int i;
+ ListCell *cell;
+ int length;
+ void *res;
+
+ Assert(catalog->struct_size > 0); /* Make sure it was initialized */
+ 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.
+ *
+ */
+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 816483e..c872d6a 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
@@ -15,884 +18,16 @@
#include "postgres.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) don't forget to document the option
- *
- * Note that we don't handle "oids" in relOpts because it is handled by
- * interpretOidsOption().
- */
-
-static relopt_bool boolRelOpts[] =
-{
- {
- {
- "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,
- AccessExclusiveLock
- },
-#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,
- AccessExclusiveLock
- },
- -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,
- AccessExclusiveLock
- },
- -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,
- AccessExclusiveLock
- },
- -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,
- AccessExclusiveLock
- },
- 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,
- AccessExclusiveLock
- },
- 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++)
- {
- text *oldoption = DatumGetTextP(oldoptions[i]);
- char *text_str = VARDATA(oldoption);
- int text_len = VARSIZE(oldoption) - 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.
@@ -908,11 +43,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,
@@ -928,16 +64,19 @@ 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;
@@ -948,529 +87,293 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
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.
+ * 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
+ */
+
+/*
+ * 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.
*
- * If there are no options of the given kind, numrelopts is set to 0 and NULL
- * is returned.
+ * 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.
*
- * 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.
+ * 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
*/
-relopt_value *
-parseRelOptions(Datum options, bool validate, relopt_kind kind,
- int *numrelopts)
-{
- relopt_value *reloptions;
- 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)
- {
- *numrelopts = 0;
- return NULL;
- }
-
- 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++)
- {
- text *optiontext = DatumGetTextP(optiondatums[i]);
- char *text_str = VARDATA(optiontext);
- int text_len = VARSIZE(optiontext) - 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;
+void
+add_autovacuum_options(options_catalog *catalog, int base_offset,
+ bool for_toast)
+{
+ 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);
}
/*
- * Subroutine for parseRelOptions, to parse and validate a single option's
- * value
+ * get_heap_relopt_catalog
+ * Returns an options catalog for heap relation.
*/
-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)));
+static options_catalog * heap_relopt_catalog = NULL;
- 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)
+options_catalog *
+get_heap_relopt_catalog(void)
+{
+ if (! heap_relopt_catalog)
{
- 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;
+ heap_relopt_catalog = allocateOptionsCatalog(NULL);
+ heap_relopt_catalog->struct_size = sizeof(HeapOptions);
- 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;
+ 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);
- 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;
+ add_autovacuum_options(heap_relopt_catalog,
+ offsetof(HeapOptions, autovacuum), false);
- 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;
+ 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",
+ AccessExclusiveLock,
+ 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);
}
-
- if (parsed)
- option->isset = true;
- if (!nofree)
- pfree(value);
+ return heap_relopt_catalog;
}
/*
- * Given the result from parseRelOptions, allocate a struct that's of the
- * specified base size plus any extra space that's needed for string variables.
- *
- * "base" should be sizeof(struct) of the reloptions struct (StdRdOptions or
- * equivalent).
+ * get_toast_relopt_catalog
+ * Returns an options catalog for toast relation.
*/
-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;
+static options_catalog * toast_relopt_catalog = NULL;
- return palloc0(size);
-}
-
-/*
- * Given the result of parseRelOptions and a parsing table, fill in the
- * struct (previously allocated with allocateReloptStruct) with the parsed
- * values.
- *
- * rdopts is the pointer to the allocated struct to be filled.
- * basesize is the sizeof(struct) that was passed to allocateReloptStruct.
- * options, of length numoptions, is parseRelOptions' output.
- * elems, of length numelems, is the table describing the allowed options.
- * When validate is true, it is expected that all options appear in elems.
- */
-void
-fillRelOptions(void *rdopts, Size basesize,
- relopt_value *options, int numoptions,
- bool validate,
- const relopt_parse_elt *elems, int numelems)
+options_catalog *
+get_toast_relopt_catalog(void)
{
- int i;
- int offset = basesize;
-
- for (i = 0; i < numoptions; i++)
+ if (! toast_relopt_catalog)
{
- int j;
- bool found = false;
+ toast_relopt_catalog = allocateOptionsCatalog("toast");
+ toast_relopt_catalog->struct_size = sizeof(ToastOptions);
- 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);
+ add_autovacuum_options(toast_relopt_catalog,
+ offsetof(ToastOptions, autovacuum), true);
}
- SET_VARSIZE(rdopts, offset);
+ return toast_relopt_catalog;
}
-
/*
- * Option parser for anything that uses StdRdOptions.
+ * get_view_relopt_catalog
+ * Returns an options catalog for view 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)}
- };
-
- 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));
+static options_catalog * view_relopt_catalog = NULL;
- pfree(options);
-
- return (bytea *) rdopts;
-}
-
-/*
- * Option parser for views
- */
-bytea *
-view_reloptions(Datum reloptions, bool validate)
+options_catalog *
+get_view_relopt_catalog(void)
{
- 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);
+ static const char *enum_names[] = VIEW_OPTION_CHECK_OPTION_VALUE_NAMES;
- /* if none set, we're done */
- if (numoptions == 0)
- return NULL;
-
- vopts = allocateReloptStruct(sizeof(ViewOptions), options, numoptions);
-
- fillRelOptions((void *) vopts, sizeof(ViewOptions), options, numoptions,
- validate, tab, lengthof(tab));
-
- pfree(options);
-
- return (bytea *) vopts;
-}
+ if (! view_relopt_catalog)
+ {
+ view_relopt_catalog = allocateOptionsCatalog(NULL);
+ view_relopt_catalog->struct_size = sizeof(ViewOptions);
-/*
- * Parse options for heaps, views and toast tables.
- */
-bytea *
-heap_reloptions(char relkind, Datum reloptions, bool validate)
-{
- StdRdOptions *rdopts;
+ optionsCatalogAddItemBool(view_relopt_catalog, "security_barrier",
+ "View acts as a row security barrier",
+ AccessExclusiveLock,
+ 0, offsetof(ViewOptions, security_barrier), false);
- switch (relkind)
- {
- case RELKIND_TOASTVALUE:
- rdopts = (StdRdOptions *)
- default_reloptions(reloptions, validate, RELOPT_KIND_TOAST);
- if (rdopts != NULL)
- {
- /* adjust default-only parameters for TOAST relations */
- rdopts->fillfactor = 100;
- rdopts->autovacuum.analyze_threshold = -1;
- rdopts->autovacuum.analyze_scale_factor = -1;
- }
- return (bytea *) rdopts;
- case RELKIND_RELATION:
- case RELKIND_MATVIEW:
- case RELKIND_PARTITIONED_TABLE:
- return default_reloptions(reloptions, validate, RELOPT_KIND_HEAP);
- default:
- /* other relkinds are not supported */
- return NULL;
+ 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;
}
-
/*
- * Parse options for indexes.
- *
- * amoptions index AM's option parser function
- * reloptions options as text[] datum
- * validate error flag
+ * get_attribute_options_catalog
+ * Returns an options catalog for heap attributes
*/
-bytea *
-index_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
-{
- Assert(amoptions != NULL);
-
- /* Assume function is strict */
- if (!PointerIsValid(DatumGetPointer(reloptions)))
- return NULL;
-
- return amoptions(reloptions, validate);
-}
+static options_catalog * attribute_options_catalog = NULL;
-/*
- * Option parser for attribute reloptions
- */
-bytea *
-attribute_reloptions(Datum reloptions, bool validate)
+options_catalog *
+get_attribute_options_catalog(void)
{
- 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);
-
- fillRelOptions((void *) aopts, sizeof(AttributeOpts), options, numoptions,
- validate, tab, lengthof(tab));
+ if (! attribute_options_catalog)
+ {
+ attribute_options_catalog = allocateOptionsCatalog(NULL);
+ attribute_options_catalog->struct_size = sizeof(AttributeOpts);
- 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)",
+ AccessExclusiveLock,
+ 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",
+ AccessExclusiveLock,
+ 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;
+static options_catalog * tablespace_options_catalog = NULL;
- tsopts = allocateReloptStruct(sizeof(TableSpaceOpts), options, numoptions);
+options_catalog *
+get_tablespace_options_catalog(void)
+{
+ if (! tablespace_options_catalog)
+ {
+ tablespace_options_catalog = allocateOptionsCatalog(NULL);
+ tablespace_options_catalog->struct_size = sizeof(TableSpaceOpts);
- fillRelOptions((void *) tsopts, sizeof(TableSpaceOpts), options, numoptions,
- validate, tab, lengthof(tab));
+ optionsCatalogAddItemReal(tablespace_options_catalog,
+ "random_page_cost",
+ "Sets the planner's estimate of the cost of a nonsequentially fetched disk page",
+ AccessExclusiveLock,
+ 0, offsetof(TableSpaceOpts, random_page_cost), -1, 0.0, DBL_MAX);
- pfree(options);
+ optionsCatalogAddItemReal(tablespace_options_catalog, "seq_page_cost",
+ "Sets the planner's estimate of the cost of a sequentially fetched disk page",
+ AccessExclusiveLock,
+ 0, offsetof(TableSpaceOpts, seq_page_cost), -1, 0.0, DBL_MAX);
- return (bytea *) tsopts;
+ optionsCatalogAddItemInt(tablespace_options_catalog,
+ "effective_io_concurrency",
+ "Number of simultaneous requests that can be handled efficiently by the disk subsystem",
+ AccessExclusiveLock,
+ 0, offsetof(TableSpaceOpts, effective_io_concurrency),
+#ifdef USE_PREFETCH
+ -1, 0, MAX_IO_CONCURRENCY
+#else
+ 0, 0, 0
+#endif
+ );
+ }
+ return tablespace_options_catalog;
}
/*
@@ -1480,33 +383,51 @@ 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;
+ 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;
+ 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;
+ DefElem *def = (DefElem *) lfirst(cell);
- for (i = 0; relOpts[i]; i++)
+ int 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 02d920b..9d1149b 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -15,7 +15,7 @@
#include "postgres.h"
#include "access/gin_private.h"
-#include "access/reloptions.h"
+#include "access/options.h"
#include "access/xloginsert.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_type.h"
@@ -25,7 +25,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
@@ -58,7 +59,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;
@@ -68,6 +68,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;
@@ -591,35 +592,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
*
@@ -696,3 +668,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);
+ gin_relopt_catalog->struct_size = sizeof(GinOptions);
+
+ 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 c2247ad..bae95bc 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -79,7 +79,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;
@@ -89,6 +88,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;
@@ -1579,3 +1579,4 @@ gistvacuumpage(Relation rel, Page page, Buffer buffer)
* the page.
*/
}
+
diff --git a/src/backend/access/gist/gistbuild.c b/src/backend/access/gist/gistbuild.c
index b65926f..ada7488 100644
--- a/src/backend/access/gist/gistbuild.c
+++ b/src/backend/access/gist/gistbuild.c
@@ -125,11 +125,9 @@ 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;
@@ -233,25 +231,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 f92baed..ff8aaf9 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -17,14 +17,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.
*/
@@ -811,34 +810,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.
*
@@ -964,3 +935,32 @@ 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);
+ gist_relopt_catalog->struct_size = sizeof(GiSTOptions);
+
+ 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 ec8ed33..7ecea95 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -76,7 +76,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;
@@ -86,6 +85,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 69676eb..1542fd1 100644
--- a/src/backend/access/hash/hashpage.c
+++ b/src/backend/access/hash/hashpage.c
@@ -332,7 +332,7 @@ _hash_metapinit(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 c705531..478cb44 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"
@@ -219,12 +219,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
*/
@@ -446,3 +440,26 @@ _hash_get_newbucket_from_oldbucket(Relation rel, Bucket old_bucket,
return new_bucket;
}
+
+static options_catalog * hash_relopt_catalog = NULL;
+
+void *
+hashgetreloptcatalog(void)
+{
+ if (! hash_relopt_catalog)
+ {
+ hash_relopt_catalog = allocateOptionsCatalog(NULL);
+ hash_relopt_catalog->struct_size = sizeof(HashRelOptions);
+
+ 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 5fd7f1e..81f8984 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -2656,7 +2656,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 6529fe3..78ecc46 100644
--- a/src/backend/access/heap/hio.c
+++ b/src/backend/access/heap/hio.c
@@ -323,8 +323,11 @@ RelationGetBufferForTuple(Relation relation, Size len,
len, MaxHeapTupleSize)));
/* Compute desired extra freespace due to fillfactor option */
- saveFreeSpace = RelationGetTargetPageFreeSpace(relation,
- HEAP_DEFAULT_FILLFACTOR);
+ if (IsToastRelation(relation))
+ saveFreeSpace = ToastGetTargetPageFreeSpace();
+ else
+ saveFreeSpace = HeapGetTargetPageFreeSpace(relation,
+ HEAP_DEFAULT_FILLFACTOR);
if (otherBuffer != InvalidBuffer)
otherBlock = BufferGetBlockNumber(otherBuffer);
diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c
index d69a266..fef5e40 100644
--- a/src/backend/access/heap/pruneheap.c
+++ b/src/backend/access/heap/pruneheap.c
@@ -131,8 +131,12 @@ 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,
- HEAP_DEFAULT_FILLFACTOR);
+
+ if (IsToastRelation(relation))
+ minfree = ToastGetTargetPageFreeSpace();
+ else
+ minfree = HeapGetTargetPageFreeSpace(relation,
+ HEAP_DEFAULT_FILLFACTOR);
minfree = Max(minfree, BLCKSZ / 10);
if (PageIsFull(page) || PageGetHeapFreeSpace(page) < minfree)
diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c
index c7b283c..ee859e7 100644
--- a/src/backend/access/heap/rewriteheap.c
+++ b/src/backend/access/heap/rewriteheap.c
@@ -665,7 +665,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 883d70d..c66cdc8 100644
--- a/src/backend/access/nbtree/nbtinsert.c
+++ b/src/backend/access/nbtree/nbtinsert.c
@@ -1432,8 +1432,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 469e7ab..675066e 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 "storage/indexfsm.h"
@@ -32,7 +33,6 @@
#include "utils/index_selfuncs.h"
#include "utils/memutils.h"
-
/* Working state for btbuild and its callback */
typedef struct
{
@@ -75,8 +75,7 @@ static void btvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
BTCycleId cycleid);
static void btvacuumpage(BTVacState *vstate, BlockNumber blkno,
BlockNumber orig_blkno);
-
-
+void * btgetreloptcatalog (void);
/*
* Btree handler function: return IndexAmRoutine with access method parameters
* and callbacks.
@@ -108,7 +107,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;
@@ -118,6 +116,7 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amendscan = btendscan;
amroutine->ammarkpos = btmarkpos;
amroutine->amrestrpos = btrestrpos;
+ amroutine->amrelopt_catalog = btgetreloptcatalog;
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
@@ -1136,3 +1135,26 @@ btcanreturn(Relation index, int attno)
{
return true;
}
+
+
+static options_catalog * bt_relopt_catalog = NULL;
+
+void *
+btgetreloptcatalog (void)
+{
+ if (! bt_relopt_catalog)
+ {
+ bt_relopt_catalog = allocateOptionsCatalog(NULL);
+ bt_relopt_catalog->struct_size = sizeof(BTRelOptions);
+
+ 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 3d041c4..692c751 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 da0f330..8b3bd08 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 "access/options.h"
#include "access/relscan.h"
#include "miscadmin.h"
#include "utils/array.h"
@@ -2034,12 +2034,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 78846be..381bbd2 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"
@@ -58,7 +58,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;
@@ -68,6 +67,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;
@@ -371,8 +371,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 */
@@ -536,15 +536,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
@@ -910,3 +901,26 @@ 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);
+ spgist_relopt_catalog->struct_size = sizeof(SpGistRelOptions);
+
+ 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 e5f773d..d2cdb21 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 646a884..5020ee1 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 d5d40e6..7aa4576 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 f4814c0..e434dc4 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -323,7 +323,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;
@@ -525,7 +525,7 @@ DefineIndex(Oid relationId,
accessMethodName)));
amcanorder = amRoutine->amcanorder;
- amoptions = amRoutine->amoptions;
+ amoption_catalog = amRoutine->amrelopt_catalog;
pfree(amRoutine);
ReleaseSysCache(tuple);
@@ -539,10 +539,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 37a4c4a..cd52e9d 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -497,7 +497,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
Datum reloptions;
ListCell *listptr;
AttrNumber attnum;
- static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
Oid ofTypeId;
ObjectAddress address;
@@ -583,13 +582,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)
{
@@ -3058,7 +3067,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);
@@ -3100,13 +3110,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)
{
@@ -3315,7 +3327,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:
@@ -4071,7 +4086,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",
@@ -5910,11 +5925,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));
@@ -9911,7 +9926,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 */
@@ -9940,25 +9955,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,
@@ -9972,7 +10014,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;
@@ -10032,6 +10074,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);
@@ -10055,12 +10099,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));
@@ -10088,6 +10135,17 @@ 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 80515ba..7ee3250 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -333,10 +333,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
@@ -1024,10 +1025,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 fdb4f71..a32d376 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 7836e6b..123fef4 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -135,7 +135,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 69f4736..5225c73 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -2842,7 +2842,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 8d19394..e550125 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1394,7 +1394,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 0c5ffa0..bc21d81 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -2510,6 +2510,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 ||
@@ -2519,8 +2520,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 d3e44fb..a677923 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -1484,7 +1484,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 */
@@ -2988,7 +2988,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
@@ -3024,8 +3024,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 5d3be38..4a2e6d9 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -996,6 +996,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,
@@ -1016,18 +1018,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))
{
@@ -1093,8 +1101,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 f7f85b5..b5f3dce 100644
--- a/src/backend/utils/cache/attoptcache.c
+++ b/src/backend/utils/cache/attoptcache.c
@@ -150,7 +150,10 @@ get_attribute_options(Oid attrelid, int attnum)
opts = NULL;
else
{
- bytea *bytea_opts = attribute_reloptions(datum, false);
+ bytea *bytea_opts;
+
+ bytea_opts = optionsTextArrayToBytea(
+ get_attribute_options_catalog(), datum);
opts = MemoryContextAlloc(CacheMemoryContext,
VARSIZE(bytea_opts));
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 9001e20..19d7b44 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -459,7 +459,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 9dcb80c..74bf884 100644
--- a/src/backend/utils/cache/spccache.c
+++ b/src/backend/utils/cache/spccache.c
@@ -149,7 +149,9 @@ 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));
@@ -190,7 +192,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 e91e41d..5e6551a 100644
--- a/src/include/access/amapi.h
+++ b/src/include/access/amapi.h
@@ -96,10 +96,6 @@ typedef void (*amcostestimate_function) (struct PlannerInfo *root,
Selectivity *indexSelectivity,
double *indexCorrelation);
-/* 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,
@@ -137,6 +133,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.
*/
@@ -198,7 +197,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;
@@ -208,6 +206,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 896824a..c84d6d4 100644
--- a/src/include/access/brin.h
+++ b/src/include/access/brin.h
@@ -25,6 +25,9 @@ typedef struct BrinOptions
} BrinOptions;
#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 4031260..8829bc1 100644
--- a/src/include/access/brin_internal.h
+++ b/src/include/access/brin_internal.h
@@ -101,7 +101,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 d46274e..f1b5a5a 100644
--- a/src/include/access/gin_private.h
+++ b/src/include/access/gin_private.h
@@ -591,7 +591,7 @@ typedef struct ginxlogDeleteListPages
/* 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);
@@ -610,6 +610,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 60a770a..be18393 100644
--- a/src/include/access/gist_private.h
+++ b/src/include/access/gist_private.h
@@ -412,6 +412,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
*/
@@ -419,7 +437,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 */
@@ -486,7 +504,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);
@@ -536,6 +553,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,
@@ -554,7 +572,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 1a9b91f..ad66f0b 100644
--- a/src/include/access/hash.h
+++ b/src/include/access/hash.h
@@ -185,6 +185,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)
*/
@@ -279,7 +292,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);
@@ -368,4 +380,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 011a72e..4d95f12 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -651,6 +651,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)
+
/*
* prototypes for functions in nbtree.c (external entry points for btree)
*/
@@ -746,7 +757,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..1be9df0
--- /dev/null
+++ b/src/include/access/options.h
@@ -0,0 +1,224 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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 */
+ 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);
+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 List *optionsDefListToRawValues(List *defList, options_parse_mode
+ parse_mode);
+extern Datum optionsValuesToTextArray(List *options_values);
+extern List *optionsTextArrayToRawValues(Datum array_datum);
+extern List *optionsMergeOptionValues(List *old_options, List *new_options);
+extern void optionsDefListValdateNamespaces(List *defList,
+ char **allowed_namespaces);
+extern List *optionsDefListFilterNamespaces(List *defList, char *namespace);
+extern List *optionsTextArrayToDefList(Datum options);
+extern List *optionsParseRawValues(List * raw_values, options_catalog *catalog,
+ options_parse_mode mode);
+extern bytea *optionsValuesToBytea(List * options, options_catalog *catalog);
+
+/*
+ * 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 861977a..78c45db 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,263 +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),
- /* if you add a new kind, make sure you update "last_default" too */
- RELOPT_KIND_LAST_DEFAULT = RELOPT_KIND_BRIN,
- /* 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 aaf78bc..1eb6de4 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
@@ -183,7 +179,7 @@ typedef struct spgLeafConsistentOut
/* spgutils.c */
-extern bytea *spgoptions(Datum reloptions, bool validate);
+extern Datum spghandler(PG_FUNCTION_ARGS);
/* spginsert.c */
extern IndexBuildResult *spgbuild(Relation heap, Relation index,
diff --git a/src/include/access/spgist_private.h b/src/include/access/spgist_private.h
index b2979a9..62811f0 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 */
@@ -610,6 +623,11 @@ typedef struct spgxlogVacuumRedirect
#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);
@@ -637,6 +655,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 db7f145..d6464a7 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 f3a9701..33f5782 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -31,7 +31,7 @@ extern Oid AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode);
extern void AlterTable(Oid relid, LOCKMODE lockmode, AlterTableStmt *stmt);
-extern LOCKMODE AlterTableGetLockLevel(List *cmds);
+extern LOCKMODE AlterTableGetLockLevel(Oid relid, List *cmds);
extern void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index a617a7c..28113ef 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -269,7 +269,7 @@ typedef struct AutoVacOpts
float8 analyze_scale_factor;
} AutoVacOpts;
-typedef struct StdRdOptions
+typedef struct HeapOptions
{
int32 vl_len_; /* varlena header (do not touch directly!) */
int fillfactor; /* page fill factor in percent (0..100) */
@@ -277,107 +277,115 @@ typedef struct StdRdOptions
bool user_catalog_table; /* use as an additional catalog
* relation */
int parallel_workers; /* max number of parallel workers */
-} StdRdOptions;
+} HeapOptions;
+
+typedef struct ToastOptions
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ AutoVacOpts autovacuum; /* autovacuum-related options */
+} ToastOptions;
#define HEAP_MIN_FILLFACTOR 10
#define HEAP_DEFAULT_FILLFACTOR 100
+#define TOAST_DEFAULT_FILLFACTOR 100 /* Only default is actually used */
+
/*
- * RelationGetFillFactor
+ * HeapGetFillFactor
* Returns the relation's fillfactor. Note multiple eval of argument!
*/
-#define RelationGetFillFactor(relation, defaultff) \
+#define HeapGetFillFactor(relation, defaultff) \
((relation)->rd_options ? \
- ((StdRdOptions *) (relation)->rd_options)->fillfactor : (defaultff))
+ ((HeapOptions *) (relation)->rd_options)->fillfactor : (defaultff))
/*
- * RelationGetTargetPageUsage
- * Returns the relation's desired space usage per page in bytes.
- */
-#define RelationGetTargetPageUsage(relation, defaultff) \
- (BLCKSZ * RelationGetFillFactor(relation, defaultff) / 100)
-
-/*
- * RelationGetTargetPageFreeSpace
+ * HeapGetTargetPageFreeSpace
* Returns the relation's desired freespace per page in bytes.
*/
-#define RelationGetTargetPageFreeSpace(relation, defaultff) \
- (BLCKSZ * (100 - RelationGetFillFactor(relation, defaultff)) / 100)
+#define HeapGetTargetPageFreeSpace(relation, defaultff) \
+ (BLCKSZ * (100 - HeapGetFillFactor(relation, defaultff)) / 100)
/*
- * RelationIsUsedAsCatalogTable
+ * HeapIsUsedAsCatalogTable
* Returns whether the relation should be treated as a catalog table
* from the pov of logical decoding. Note multiple eval of argument!
*/
-#define RelationIsUsedAsCatalogTable(relation) \
+#define HeapIsUsedAsCatalogTable(relation) \
((relation)->rd_options && \
((relation)->rd_rel->relkind == RELKIND_RELATION || \
(relation)->rd_rel->relkind == RELKIND_MATVIEW) ? \
- ((StdRdOptions *) (relation)->rd_options)->user_catalog_table : false)
+ ((HeapOptions *) (relation)->rd_options)->user_catalog_table : false)
/*
- * RelationGetParallelWorkers
+ * HeapGetParallelWorkers
* Returns the relation's parallel_workers reloption setting.
* Note multiple eval of argument!
*/
-#define RelationGetParallelWorkers(relation, defaultpw) \
+#define HeapGetParallelWorkers(relation, defaultpw) \
((relation)->rd_options ? \
- ((StdRdOptions *) (relation)->rd_options)->parallel_workers : (defaultpw))
+ ((HeapOptions *) (relation)->rd_options)->parallel_workers : (defaultpw))
+#define ToastGetTargetPageFreeSpace() \
+ (BLCKSZ * (100 - TOAST_DEFAULT_FILLFACTOR) / 100)
/*
* ViewOptions
* Contents of rd_options for views
*/
+
+/*
+ * Definition of items of enum type. Names and codes. To add or modify item
+ * edit both lists
+ */
+#define VIEW_OPTION_CHECK_OPTION_VALUE_NAMES { \
+ "local", \
+ "cascaded", \
+ (const char *) NULL \
+}
+
+typedef enum view_option_check_option_value_numbers
+{
+ VIEW_OPTION_CHECK_OPTION_NOT_SET = -1,
+ VIEW_OPTION_CHECK_OPTION_LOCAL = 0,
+ VIEW_OPTION_CHECK_OPTION_CASCADED = 1,
+} view_option_check_option_value_numbers;
+
typedef struct ViewOptions
{
int32 vl_len_; /* varlena header (do not touch directly!) */
bool security_barrier;
- int check_option_offset;
+ int check_option;
} ViewOptions;
-
/*
- * RelationIsSecurityView
- * Returns whether the relation is security view, or not. Note multiple
- * eval of argument!
+ * ViewIsSecurityView
+ * Returns whether the view is security view, or not. Note multiple eval
+ * of argument!
*/
-#define RelationIsSecurityView(relation) \
+#define ViewIsSecurityView(relation) \
((relation)->rd_options ? \
- ((ViewOptions *) (relation)->rd_options)->security_barrier : false)
+ ((ViewOptions *) (relation)->rd_options)->security_barrier : false)
/*
- * RelationHasCheckOption
- * Returns true if the relation is a view defined with either the local
- * or the cascaded check option. Note multiple eval of argument!
+ * ViewHasCheckOption
+ * Returns true if view is defined with either the local or the cascaded
+ * check option. Note multiple eval of argument!
*/
-#define RelationHasCheckOption(relation) \
- ((relation)->rd_options && \
- ((ViewOptions *) (relation)->rd_options)->check_option_offset != 0)
-/*
- * RelationHasLocalCheckOption
- * Returns true if the relation is a view defined with the local check
- * option. Note multiple eval of argument!
- */
-#define RelationHasLocalCheckOption(relation) \
- ((relation)->rd_options && \
- ((ViewOptions *) (relation)->rd_options)->check_option_offset != 0 ? \
- strcmp((char *) (relation)->rd_options + \
- ((ViewOptions *) (relation)->rd_options)->check_option_offset, \
- "local") == 0 : false)
+#define ViewHasCheckOption(relation) \
+((relation)->rd_options && \
+ ((ViewOptions *) (relation)->rd_options)->check_option != \
+ VIEW_OPTION_CHECK_OPTION_NOT_SET)
/*
- * RelationHasCascadedCheckOption
- * Returns true if the relation is a view defined with the cascaded check
- * option. Note multiple eval of argument!
+ * ViewHasCascadedCheckOption
+ * Returns true if the view is defined with the cascaded check option.
+ * Note multiple eval of argument!
*/
-#define RelationHasCascadedCheckOption(relation) \
- ((relation)->rd_options && \
- ((ViewOptions *) (relation)->rd_options)->check_option_offset != 0 ? \
- strcmp((char *) (relation)->rd_options + \
- ((ViewOptions *) (relation)->rd_options)->check_option_offset, \
- "cascaded") == 0 : false)
+#define ViewHasCascadedCheckOption(relation) \
+((relation)->rd_options && \
+ ((ViewOptions *) (relation)->rd_options)->check_option == \
+ VIEW_OPTION_CHECK_OPTION_CASCADED)
/*
* RelationIsValid
@@ -556,7 +564,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 d8e7b61..88ecca4 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -2285,7 +2285,9 @@ ERROR: unrecognized parameter "autovacuum_enabled"
alter view my_locks set (autovacuum_enabled = false);
ERROR: unrecognized parameter "autovacuum_enabled"
alter table my_locks reset (autovacuum_enabled);
+ERROR: unrecognized parameter "autovacuum_enabled"
alter view my_locks reset (autovacuum_enabled);
+ERROR: unrecognized parameter "autovacuum_enabled"
begin;
alter view my_locks set (security_barrier=off);
select * from my_locks order by 1;
@@ -3364,3 +3366,10 @@ alter table p attach partition p1 for values from (1, 2) to (1, 10);
ERROR: partition constraint is violated by some row
-- cleanup: avoid using CASCADE
drop table p, p1, p11;
+-- test alter column options
+CREATE TABLE tmp(i integer);
+INSERT INTO tmp VALUES (1);
+ALTER TABLE tmp ALTER COLUMN i SET (n_distinct = 1, n_distinct_inherited = 2);
+ALTER TABLE tmp ALTER COLUMN i RESET (n_distinct_inherited);
+ANALYZE tmp;
+DROP TABLE tmp;
diff --git a/src/test/regress/expected/brin.out b/src/test/regress/expected/brin.out
index 21676e5..36b1cc4 100644
--- a/src/test/regress/expected/brin.out
+++ b/src/test/regress/expected/brin.out
@@ -418,3 +418,8 @@ SELECT brin_summarize_new_values('brinidx'); -- ok, no change expected
0
(1 row)
+-- 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/create_index.out b/src/test/regress/expected/create_index.out
index e519fdb..2ff134e 100644
--- a/src/test/regress/expected/create_index.out
+++ b/src/test/regress/expected/create_index.out
@@ -2340,7 +2340,7 @@ CREATE INDEX hash_name_index ON hash_name_heap USING hash (random name_ops);
WARNING: hash indexes are not WAL-logged and their use is discouraged
CREATE INDEX hash_txt_index ON hash_txt_heap USING hash (random text_ops);
WARNING: hash indexes are not WAL-logged and their use is discouraged
-CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_ops);
+CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=60);
WARNING: hash indexes are not WAL-logged and their use is discouraged
CREATE UNLOGGED TABLE unlogged_hash_table (id int4);
CREATE INDEX unlogged_hash_index ON unlogged_hash_table USING hash (id int4_ops);
diff --git a/src/test/regress/expected/gist.out b/src/test/regress/expected/gist.out
index c7181b0..19f1881 100644
--- a/src/test/regress/expected/gist.out
+++ b/src/test/regress/expected/gist.out
@@ -12,6 +12,20 @@ insert into gist_point_tbl (id, p)
select g+100000, point(g*10+1, g*10+1) from generate_series(1, 10000) g;
-- To test vacuum, delete some entries from all over the index.
delete from gist_point_tbl where id % 2 = 1;
+-- Test buffering and fillfactor reloption takes valid values
+create index gist_pointidx2 on gist_point_tbl using gist(p) with (buffering = on, fillfactor=50);
+create index gist_pointidx3 on gist_point_tbl using gist(p) with (buffering = off);
+create index gist_pointidx4 on gist_point_tbl using gist(p) with (buffering = auto);
+--Test buffering and fillfactor for refising invalid values
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (buffering = invalid_value);
+ERROR: invalid value for "buffering" option
+DETAIL: Valid values are "on", "off", "auto".
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfactor=9);
+ERROR: value 9 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfactor=101);
+ERROR: value 101 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
-- And also delete some concentration of values. (GiST doesn't currently
-- attempt to delete pages even when they become empty, but if it did, this
-- would exercise it)
diff --git a/src/test/regress/expected/hash_index.out b/src/test/regress/expected/hash_index.out
index f8b9f02..9f9352d 100644
--- a/src/test/regress/expected/hash_index.out
+++ b/src/test/regress/expected/hash_index.out
@@ -232,3 +232,14 @@ INSERT INTO hash_heap_float4 VALUES (1.1,1);
CREATE INDEX hash_idx ON hash_heap_float4 USING hash (x);
WARNING: hash indexes are not WAL-logged and their use is discouraged
DROP TABLE hash_heap_float4 CASCADE;
+-- Check reloptions min max values and that it is not allowed to ALTER
+CREATE INDEX hash_f8_index2 ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=9);
+WARNING: hash indexes are not WAL-logged and their use is discouraged
+ERROR: value 9 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+CREATE INDEX hash_f8_index2 ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=101);
+WARNING: hash indexes are not WAL-logged and their use is discouraged
+ERROR: value 101 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+ALTER INDEX hash_f8_index SET (fillfactor=99);
+ERROR: changing parameter "fillfactor" is not allowed
diff --git a/src/test/regress/expected/reloptions.out b/src/test/regress/expected/reloptions.out
new file mode 100644
index 0000000..835d72f
--- /dev/null
+++ b/src/test/regress/expected/reloptions.out
@@ -0,0 +1,193 @@
+-- Simple create
+CREATE TABLE reloptions_test(i INT) WITH (fillfactor=30,autovacuum_enabled = false, autovacuum_analyze_scale_factor = 0.2);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+ reloptions
+------------------------------------------------------------------------------
+ {fillfactor=30,autovacuum_enabled=false,autovacuum_analyze_scale_factor=0.2}
+(1 row)
+
+-- Test сase insensitive
+CREATE TABLE reloptions_test3(i INT) WITH (FiLlFaCtoR=40);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test3'::regclass;
+ reloptions
+-----------------
+ {fillfactor=40}
+(1 row)
+
+-- Fail on min/max values check
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=2);
+ERROR: value 2 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=110);
+ERROR: value 110 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor = -10.0);
+ERROR: value -10.0 out of bounds for option "autovacuum_analyze_scale_factor"
+DETAIL: Valid values are between "0.000000" and "100.000000".
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor = 110.0);
+ERROR: value 110.0 out of bounds for option "autovacuum_analyze_scale_factor"
+DETAIL: Valid values are between "0.000000" and "100.000000".
+-- Fail when option and namespase do not exist
+CREATE TABLE reloptions_test2(i INT) WITH (not_existing_option=2);
+ERROR: unrecognized parameter "not_existing_option"
+CREATE TABLE reloptions_test2(i INT) WITH (not_existing_namespace.fillfactor=2);
+ERROR: unrecognized parameter namespace "not_existing_namespace"
+-- Fail while setting unproper value
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=30.5);
+ERROR: invalid value for integer option "fillfactor": 30.5
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor='string');
+ERROR: invalid value for integer option "fillfactor": string
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=true);
+ERROR: invalid value for integer option "fillfactor": true
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled=12);
+ERROR: invalid value for boolean option "autovacuum_enabled": 12
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled=30.5);
+ERROR: invalid value for boolean option "autovacuum_enabled": 30.5
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled='string');
+ERROR: invalid value for boolean option "autovacuum_enabled": string
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor='string');
+ERROR: invalid value for floating point option "autovacuum_analyze_scale_factor": string
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor=true);
+ERROR: invalid value for floating point option "autovacuum_analyze_scale_factor": true
+-- Specifing name only should fail as if there was =ture after it
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor);
+ERROR: invalid value for integer option "fillfactor": true
+-- Simple ALTER TABLE
+ALTER TABLE reloptions_test SET (fillfactor=31, autovacuum_analyze_scale_factor = 0.3);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+ reloptions
+------------------------------------------------------------------------------
+ {autovacuum_enabled=false,fillfactor=31,autovacuum_analyze_scale_factor=0.3}
+(1 row)
+
+-- Check that we cat set boolean option to true just by mentioning it
+ALTER TABLE reloptions_test SET (autovacuum_enabled, fillfactor=32);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+ reloptions
+-----------------------------------------------------------------------------
+ {autovacuum_analyze_scale_factor=0.3,autovacuum_enabled=true,fillfactor=32}
+(1 row)
+
+-- Check that RESET works well
+ALTER TABLE reloptions_test RESET (fillfactor);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+ reloptions
+---------------------------------------------------------------
+ {autovacuum_analyze_scale_factor=0.3,autovacuum_enabled=true}
+(1 row)
+
+-- Check that RESETting all values make NULL reloptions record in pg_class
+ALTER TABLE reloptions_test RESET (autovacuum_enabled, autovacuum_analyze_scale_factor);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass AND reloptions IS NULL;
+ reloptions
+------------
+
+(1 row)
+
+-- Check RESET fails on att=value
+ALTER TABLE reloptions_test RESET (fillfactor=12);
+ERROR: RESET must not include values for parameters
+-- Check oids options is ignored
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test(i INT) WITH (fillfactor=20, oids=true);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+ reloptions
+-----------------
+ {fillfactor=20}
+(1 row)
+
+-- Now testing toast.* options
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test (s VARCHAR) WITH (toast.autovacuum_vacuum_cost_delay = 23 );
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+ reloptions
+-----------------------------------
+ {autovacuum_vacuum_cost_delay=23}
+(1 row)
+
+ALTER TABLE reloptions_test SET (toast.autovacuum_vacuum_cost_delay = 24);
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+ reloptions
+-----------------------------------
+ {autovacuum_vacuum_cost_delay=24}
+(1 row)
+
+ALTER TABLE reloptions_test RESET (toast.autovacuum_vacuum_cost_delay);
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+ reloptions
+------------
+
+(1 row)
+
+-- Fail on unexisting options in toast namespace
+CREATE TABLE reloptions_test2 (i int) WITH (toast.not_existing_option = 42 );
+ERROR: unrecognized parameter "toast.not_existing_option"
+-- Fail on setting reloption to a table that does not have a TOAST relation
+CREATE TABLE reloptions_test2 (i int) WITH (toast.autovacuum_vacuum_cost_delay = 23 );
+ERROR: no TOAST relation was created for a table. Can't set toast.* storage parameters
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test(i INT);
+ALTER TABLE reloptions_test SET (toast.autovacuum_vacuum_cost_delay = 23);
+ERROR: TOAST relation for this table does not exist. Can't change toast.* storage parameters
+ALTER TABLE reloptions_test RESET (toast.autovacuum_vacuum_cost_delay);
+ERROR: TOAST relation for this table does not exist. Can't change toast.* storage parameters
+-- autovacuum_analyze_scale_factor and autovacuum_analyze_threshold should be
+-- accepted by heap but rejected by toast (special case)
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test (s VARCHAR) WITH (autovacuum_analyze_scale_factor=1, autovacuum_analyze_threshold=1);
+CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analyze_scale_factor=1);
+ERROR: unrecognized parameter "toast.autovacuum_analyze_scale_factor"
+CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analyze_threshold=1);
+ERROR: unrecognized parameter "toast.autovacuum_analyze_threshold"
+-- And now mixed toast + heap
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test (s VARCHAR) WITH (toast.autovacuum_vacuum_cost_delay = 23, autovacuum_vacuum_cost_delay = 24, fillfactor = 40);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+ reloptions
+-------------------------------------------------
+ {autovacuum_vacuum_cost_delay=24,fillfactor=40}
+(1 row)
+
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+ reloptions
+-----------------------------------
+ {autovacuum_vacuum_cost_delay=23}
+(1 row)
+
+-- Same FOR CREATE and ALTER INDEX for btree indexes
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (fillfactor=30);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx'::regclass;
+ reloptions
+-----------------
+ {fillfactor=30}
+(1 row)
+
+-- Fail when option and namespase do not exist
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_existing_option=2);
+ERROR: unrecognized parameter "not_existing_option"
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_existing_option.fillfactor=2);
+ERROR: unrecognized parameter namespace "not_existing_option"
+-- Check ranges
+CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfactor=1);
+ERROR: value 1 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfactor=130);
+ERROR: value 130 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+-- Check alter
+ALTER INDEX reloptions_test_idx SET (fillfactor=40);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx'::regclass;
+ reloptions
+-----------------
+ {fillfactor=40}
+(1 row)
+
+-- Check alter on empty relop list
+CREATE INDEX reloptions_test_idx3 ON reloptions_test (s);
+ALTER INDEX reloptions_test_idx3 SET (fillfactor=40);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx3'::regclass;
+ reloptions
+-----------------
+ {fillfactor=40}
+(1 row)
+
diff --git a/src/test/regress/expected/spgist.out b/src/test/regress/expected/spgist.out
index 0691e91..bb58fb7 100644
--- a/src/test/regress/expected/spgist.out
+++ b/src/test/regress/expected/spgist.out
@@ -4,7 +4,7 @@
-- There are other tests to test different SP-GiST opclasses. This is for
-- testing SP-GiST code itself.
create table spgist_point_tbl(id int4, p point);
-create index spgist_point_idx on spgist_point_tbl using spgist(p);
+create index spgist_point_idx on spgist_point_tbl using spgist(p) with (fillfactor = 75);
-- Test vacuum-root operation. It gets invoked when the root is also a leaf,
-- i.e. the index is very small.
insert into spgist_point_tbl (id, p)
@@ -37,3 +37,12 @@ select g, 'baaaaaaaaaaaaaar' || g from generate_series(1, 1000) g;
-- tuple to be moved to another page.
insert into spgist_text_tbl (id, t)
select -g, 'f' || repeat('o', 100-g) || 'surprise' from generate_series(1, 100) g;
+-- Check reloptions min, max values
+create index spgist_point_idx2 on spgist_point_tbl using spgist(p) with (fillfactor = 9);
+ERROR: value 9 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+create index spgist_point_idx2 on spgist_point_tbl using spgist(p) with (fillfactor = 101);
+ERROR: value 101 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+-- ALTER reloption should work
+alter index spgist_point_idx set (fillfactor = 76);
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out
index aa06d1d..287dd84 100644
--- a/src/test/regress/expected/updatable_views.out
+++ b/src/test/regress/expected/updatable_views.out
@@ -1551,7 +1551,7 @@ SELECT * FROM base_tbl;
ALTER VIEW rw_view1 SET (check_option=here); -- invalid
ERROR: invalid value for "check_option" option
-DETAIL: Valid values are "local" and "cascaded".
+DETAIL: Valid values are "local", "cascaded".
ALTER VIEW rw_view1 SET (check_option=local);
INSERT INTO rw_view2 VALUES (-20); -- should fail
ERROR: new row violates check option for view "rw_view1"
diff --git a/src/test/regress/input/tablespace.source b/src/test/regress/input/tablespace.source
index 743c4b9..0bb5299 100644
--- a/src/test/regress/input/tablespace.source
+++ b/src/test/regress/input/tablespace.source
@@ -12,10 +12,10 @@ DROP TABLESPACE regress_tblspacewith;
CREATE TABLESPACE regress_tblspace LOCATION '@testtablespace@';
-- try setting and resetting some properties for the new tablespace
-ALTER TABLESPACE regress_tblspace SET (random_page_cost = 1.0);
+ALTER TABLESPACE regress_tblspace SET (random_page_cost = 1.0, seq_page_cost = 1.1);
ALTER TABLESPACE regress_tblspace SET (some_nonexistent_parameter = true); -- fail
ALTER TABLESPACE regress_tblspace RESET (random_page_cost = 2.0); -- fail
-ALTER TABLESPACE regress_tblspace RESET (random_page_cost, seq_page_cost); -- ok
+ALTER TABLESPACE regress_tblspace RESET (random_page_cost, effective_io_concurrency); -- ok
-- create a schema we can use
CREATE SCHEMA testschema;
diff --git a/src/test/regress/output/tablespace.source b/src/test/regress/output/tablespace.source
index 31f2ac0..ccf4bee 100644
--- a/src/test/regress/output/tablespace.source
+++ b/src/test/regress/output/tablespace.source
@@ -14,12 +14,12 @@ DROP TABLESPACE regress_tblspacewith;
-- create a tablespace we can use
CREATE TABLESPACE regress_tblspace LOCATION '@testtablespace@';
-- try setting and resetting some properties for the new tablespace
-ALTER TABLESPACE regress_tblspace SET (random_page_cost = 1.0);
+ALTER TABLESPACE regress_tblspace SET (random_page_cost = 1.0, seq_page_cost = 1.1);
ALTER TABLESPACE regress_tblspace SET (some_nonexistent_parameter = true); -- fail
ERROR: unrecognized parameter "some_nonexistent_parameter"
ALTER TABLESPACE regress_tblspace RESET (random_page_cost = 2.0); -- fail
ERROR: RESET must not include values for parameters
-ALTER TABLESPACE regress_tblspace RESET (random_page_cost, seq_page_cost); -- ok
+ALTER TABLESPACE regress_tblspace RESET (random_page_cost, effective_io_concurrency); -- ok
-- create a schema we can use
CREATE SCHEMA testschema;
-- try a table
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index edeb2d6..f70cdf3 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -109,7 +109,7 @@ test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combo
# NB: temp.sql does a reconnect which transiently uses 2 connections,
# so keep this parallel group to at most 19 tests
# ----------
-test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml
+test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml reloptions
# event triggers cannot run concurrently with any test that runs DDL
test: event_trigger
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 27a46d7..b37f3b7 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -170,5 +170,6 @@ test: returning
test: largeobject
test: with
test: xml
+test: reloptions
test: event_trigger
test: stats
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index 1f551ec..c7f2d9f 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -2219,3 +2219,11 @@ alter table p attach partition p1 for values from (1, 2) to (1, 10);
-- cleanup: avoid using CASCADE
drop table p, p1, p11;
+
+-- test alter column options
+CREATE TABLE tmp(i integer);
+INSERT INTO tmp VALUES (1);
+ALTER TABLE tmp ALTER COLUMN i SET (n_distinct = 1, n_distinct_inherited = 2);
+ALTER TABLE tmp ALTER COLUMN i RESET (n_distinct_inherited);
+ANALYZE tmp;
+DROP TABLE tmp;
diff --git a/src/test/regress/sql/brin.sql b/src/test/regress/sql/brin.sql
index e7f6f77..2701e76 100644
--- a/src/test/regress/sql/brin.sql
+++ b/src/test/regress/sql/brin.sql
@@ -421,3 +421,7 @@ UPDATE brintest SET textcol = '' WHERE textcol IS NOT NULL;
SELECT brin_summarize_new_values('brintest'); -- error, not an index
SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index
SELECT brin_summarize_new_values('brinidx'); -- ok, no change expected
+
+-- Check that changing reloptions for brin index is not allowed
+ALTER INDEX brinidx SET (pages_per_range = 10);
+ALTER INDEX brinidx RESET (pages_per_range);
diff --git a/src/test/regress/sql/create_index.sql b/src/test/regress/sql/create_index.sql
index 1648072..1f8d101 100644
--- a/src/test/regress/sql/create_index.sql
+++ b/src/test/regress/sql/create_index.sql
@@ -682,7 +682,7 @@ CREATE INDEX hash_name_index ON hash_name_heap USING hash (random name_ops);
CREATE INDEX hash_txt_index ON hash_txt_heap USING hash (random text_ops);
-CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_ops);
+CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=60);
CREATE UNLOGGED TABLE unlogged_hash_table (id int4);
CREATE INDEX unlogged_hash_index ON unlogged_hash_table USING hash (id int4_ops);
diff --git a/src/test/regress/sql/gist.sql b/src/test/regress/sql/gist.sql
index 9d4ff1e..ce1ac1a 100644
--- a/src/test/regress/sql/gist.sql
+++ b/src/test/regress/sql/gist.sql
@@ -17,6 +17,17 @@ select g+100000, point(g*10+1, g*10+1) from generate_series(1, 10000) g;
-- To test vacuum, delete some entries from all over the index.
delete from gist_point_tbl where id % 2 = 1;
+-- Test buffering and fillfactor reloption takes valid values
+create index gist_pointidx2 on gist_point_tbl using gist(p) with (buffering = on, fillfactor=50);
+create index gist_pointidx3 on gist_point_tbl using gist(p) with (buffering = off);
+create index gist_pointidx4 on gist_point_tbl using gist(p) with (buffering = auto);
+--Test buffering and fillfactor for refising invalid values
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (buffering = invalid_value);
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfactor=9);
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfactor=101);
+
+
+
-- And also delete some concentration of values. (GiST doesn't currently
-- attempt to delete pages even when they become empty, but if it did, this
-- would exercise it)
diff --git a/src/test/regress/sql/hash_index.sql b/src/test/regress/sql/hash_index.sql
index 15a3b06..b034efb 100644
--- a/src/test/regress/sql/hash_index.sql
+++ b/src/test/regress/sql/hash_index.sql
@@ -194,3 +194,8 @@ CREATE TABLE hash_heap_float4 (x float4, y int);
INSERT INTO hash_heap_float4 VALUES (1.1,1);
CREATE INDEX hash_idx ON hash_heap_float4 USING hash (x);
DROP TABLE hash_heap_float4 CASCADE;
+
+-- Check reloptions min max values and that it is not allowed to ALTER
+CREATE INDEX hash_f8_index2 ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=9);
+CREATE INDEX hash_f8_index2 ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=101);
+ALTER INDEX hash_f8_index SET (fillfactor=99);
\ No newline at end of file
diff --git a/src/test/regress/sql/reloptions.sql b/src/test/regress/sql/reloptions.sql
new file mode 100644
index 0000000..0bfec28
--- /dev/null
+++ b/src/test/regress/sql/reloptions.sql
@@ -0,0 +1,153 @@
+
+-- Simple create
+CREATE TABLE reloptions_test(i INT) WITH (fillfactor=30,autovacuum_enabled = false, autovacuum_analyze_scale_factor = 0.2);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+
+-- Test сase insensitive
+CREATE TABLE reloptions_test3(i INT) WITH (FiLlFaCtoR=40);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test3'::regclass;
+
+-- Fail on min/max values check
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=2);
+
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=110);
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor = -10.0);
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor = 110.0);
+
+-- Fail when option and namespase do not exist
+
+CREATE TABLE reloptions_test2(i INT) WITH (not_existing_option=2);
+
+CREATE TABLE reloptions_test2(i INT) WITH (not_existing_namespace.fillfactor=2);
+
+-- Fail while setting unproper value
+
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=30.5);
+
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor='string');
+
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=true);
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled=12);
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled=30.5);
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled='string');
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor='string');
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor=true);
+
+-- Specifing name only should fail as if there was =ture after it
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor);
+
+-- Simple ALTER TABLE
+
+ALTER TABLE reloptions_test SET (fillfactor=31, autovacuum_analyze_scale_factor = 0.3);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+
+-- Check that we cat set boolean option to true just by mentioning it
+
+ALTER TABLE reloptions_test SET (autovacuum_enabled, fillfactor=32);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+
+-- Check that RESET works well
+
+ALTER TABLE reloptions_test RESET (fillfactor);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+
+-- Check that RESETting all values make NULL reloptions record in pg_class
+ALTER TABLE reloptions_test RESET (autovacuum_enabled, autovacuum_analyze_scale_factor);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass AND reloptions IS NULL;
+
+-- Check RESET fails on att=value
+
+ALTER TABLE reloptions_test RESET (fillfactor=12);
+
+-- Check oids options is ignored
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test(i INT) WITH (fillfactor=20, oids=true);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+
+-- Now testing toast.* options
+DROP TABLE reloptions_test;
+
+CREATE TABLE reloptions_test (s VARCHAR) WITH (toast.autovacuum_vacuum_cost_delay = 23 );
+
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+
+ALTER TABLE reloptions_test SET (toast.autovacuum_vacuum_cost_delay = 24);
+
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+
+ALTER TABLE reloptions_test RESET (toast.autovacuum_vacuum_cost_delay);
+
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+
+-- Fail on unexisting options in toast namespace
+CREATE TABLE reloptions_test2 (i int) WITH (toast.not_existing_option = 42 );
+
+-- Fail on setting reloption to a table that does not have a TOAST relation
+CREATE TABLE reloptions_test2 (i int) WITH (toast.autovacuum_vacuum_cost_delay = 23 );
+DROP TABLE reloptions_test;
+
+CREATE TABLE reloptions_test(i INT);
+ALTER TABLE reloptions_test SET (toast.autovacuum_vacuum_cost_delay = 23);
+ALTER TABLE reloptions_test RESET (toast.autovacuum_vacuum_cost_delay);
+
+-- autovacuum_analyze_scale_factor and autovacuum_analyze_threshold should be
+-- accepted by heap but rejected by toast (special case)
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test (s VARCHAR) WITH (autovacuum_analyze_scale_factor=1, autovacuum_analyze_threshold=1);
+
+CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analyze_scale_factor=1);
+CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analyze_threshold=1);
+
+-- And now mixed toast + heap
+DROP TABLE reloptions_test;
+
+CREATE TABLE reloptions_test (s VARCHAR) WITH (toast.autovacuum_vacuum_cost_delay = 23, autovacuum_vacuum_cost_delay = 24, fillfactor = 40);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+
+-- Same FOR CREATE and ALTER INDEX for btree indexes
+
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (fillfactor=30);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx'::regclass;
+
+-- Fail when option and namespase do not exist
+
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_existing_option=2);
+
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_existing_option.fillfactor=2);
+
+-- Check ranges
+
+CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfactor=1);
+
+CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfactor=130);
+
+-- Check alter
+
+ALTER INDEX reloptions_test_idx SET (fillfactor=40);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx'::regclass;
+
+-- Check alter on empty relop list
+
+CREATE INDEX reloptions_test_idx3 ON reloptions_test (s);
+
+ALTER INDEX reloptions_test_idx3 SET (fillfactor=40);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx3'::regclass;
+
+
diff --git a/src/test/regress/sql/spgist.sql b/src/test/regress/sql/spgist.sql
index 5896b50..8d26e49 100644
--- a/src/test/regress/sql/spgist.sql
+++ b/src/test/regress/sql/spgist.sql
@@ -5,7 +5,7 @@
-- testing SP-GiST code itself.
create table spgist_point_tbl(id int4, p point);
-create index spgist_point_idx on spgist_point_tbl using spgist(p);
+create index spgist_point_idx on spgist_point_tbl using spgist(p) with (fillfactor = 75);
-- Test vacuum-root operation. It gets invoked when the root is also a leaf,
-- i.e. the index is very small.
@@ -48,3 +48,10 @@ select g, 'baaaaaaaaaaaaaar' || g from generate_series(1, 1000) g;
-- tuple to be moved to another page.
insert into spgist_text_tbl (id, t)
select -g, 'f' || repeat('o', 100-g) || 'surprise' from generate_series(1, 100) g;
+
+-- Check reloptions min, max values
+create index spgist_point_idx2 on spgist_point_tbl using spgist(p) with (fillfactor = 9);
+create index spgist_point_idx2 on spgist_point_tbl using spgist(p) with (fillfactor = 101);
+
+-- ALTER reloption should work
+alter index spgist_point_idx set (fillfactor = 76);
В письме от 6 февраля 2017 23:30:03 пользователь Nikolay Shaplov написал:
I've rebased the patch, so it can be applied to current master. See
attachment.
Alvaro in private letter told that he will review the patch some time later.
So I am waiting.
Also added this patch to commitfest:
https://commitfest.postgresql.org/13/992/
(Please see Attribution notice at the bottom of the letter)
Hi! Some time ago I've send a proposal, about removing all am-related code
from src/backend/access/common/reloptions.c and move it into somewhere
inside am code. It was in
/messages/by-id/4636505.Bu9AKW1Kzc@nataraj-amd64
thread.Now I have a patch that is ready
Before explaining what have been done, I need to define some concepts, so it
would be easy to speak about them in the further explanation.Reloptions values in postgres actually exists in four representations:
DefList*, TextArray[], Bytea and so called Values.- DefList representation is a list of DefElem, it is the representation in
which reloptions comes from syntax analyzer, when relation is created or
altered, and also this representation that is needed for pg_dump to dump a
create statement that creates this relation.- TextArray[] representation is a way, how reloptions are stored in
pg_catalog. It is TEXT[] attribute in the pg_catalog table and from the
poinf o view of postgres code it is Datum array of TEXT Datums, when read
into the memory- Bytea (or I will also call it Binary) representation, is a representation
with which relation code really works. It has C-structure inside in which
fixed-length options are sored, some space is reserved for storing varlen
values after the structure, and certainly it has BYTEA header in front of
all of it. It is cached, it is fast to access. It is a good stuff.- Values (or in some cases RawValues) -- internal representation of option
parser. Technically it is List of option_value structures (old name for it
is relopt_value). This representation is used for parsing and validating
options. Actually all the convertations between representations listed
above are dove through Values representation. Values are called RawValues
when Values List were already created, but it was not parsed yet, and all
option values are in raw state: represented as a text.DefList and TextArray representations are converted in both ways(always
through Values representation): we need DefList -> TextArray to create or
change entry in the pg_catalog, and need TextArray -> DefList for pg_dumpBytea representation is converted from TextArray representation (also
through Values representation). When relation code, tries to get cached
Bytea representation for certain relation, and none is actually cached, then
cache code calls extractRelOptions function that fetches entry from
pg_catalog for that relation, gets reloptions TextArray[], converts it
into Bytea and cache it.Each reloption has it's own definition. This definitions tells how this
option should be parsed, validated and converted. Before my patch this
information were stored in relopt_gen and relopt_parse_elt structures. In
my patch all this information were moved into option_definition_basic
structure (that is actually old relopt_gen + relopt_parse_elt)The set of options definition I would call a options catalog. Before my
patch there was one global options catalog (actually it was divided into
parts by data types and has another part for dynamically added options, but
nevertheless, it was global). After my patch there would be a separate
options catalog for each relation type. For AM-relation this catalog would
be available via AM-handler. for other relation types it would be still
kept in reloption.cNow I am ready to tell what actually have been done in this patch:
1. I moved options definitions from global lists from reloptions.c
[type name]RelOpts to lists that are kept inside AM code. One list per AM.
heap options, toast options and options for all other relation types that
are not accessible through AccessMethod, all were kept inside
reloptions.c, but now it is one list for each relation type, not a global
one2. Each AccessMethod had amoptions function that was responsible for
converting reloptions from TextArray[] representation into Bytea
representaions. This function used functions from reloptions.c and also had
an array of relopt_parse_elt that defines how parsed reloption data should
be packed inside Bytea chunk. I've moved data from relopt_parse_elt array
into option definitions that are stored in the catalog (since catalog are
now defined inside the AccessMethod we have access to a structure of Bytea
representation at the place where we define option catalog)3. Speaking of amoptions function, I've completely removed it, since we do
not need it. For developers of Access Methods who still want to do some
custom action with just parsed data, I've added postprocess_fun to option
catalog. If this function is defined, it is called by convertation function
right after Bytea representation were created. In postprocess_fun developer
can do some custom validation, or change just parsed data. This feature is
used in bloom index.4. Instead of amoptions function I've added amrelopt_catalog function to the
Access Method. This function returns option definition catalog for an
Access Method. The catalog is used for processing options.5. As before, relation cache calls extractRelOptions when it needs options
that were not previously cached.
Before my patch, it created Bytea representations for non-AM relations, and
called amoptions AM function to get Bytea representation for AM relation.
In by patch, it now gets option catalog, (using local functions for non-AM
relations, and using amrelopt_catalog function for AM-relations), and then
use this catalog to convert options from TextArray into Bytea
representation.6. I've added some more featues:
- Now you can set OPTION_DEFINITION_FLAG_FORBID_ALTER flag in definition of
the option, and then postgres will refuse to change option value using
ALTER command. In many indexes, some options are used only for index
creation. After this it's value is saved in MetaPage, and used from there.
Nevertheless user is allowed to change option value, though it affects
nothing, one need to recreate index to really change such value. Now using
this flag we can prevent user from getting an illusion that he successfully
changed option value.- After applying my patch if you try to set toast. option for table that
does not have toast, you will get an error. Before postgres will accept
toast option for a table without a toast, but this value will be lost. This
is bad behavior, so now postgres will throw an error in this case.- I've noticed that all string relation options in postgres are technically
enum options. So I've added enum option type. This will save some bytes of
the memory and some tacts of the CPU. I also left string option type
(through it is not used now). A. Korotkov said that it might be needed
later for storing patterns, xml/json paths or something like that.- Added some more tests that will trigger more options code. Though I am
dreaming about more advanced test system, that will allow to check that
certain value is received somewhere deep in the code.7. And now the most controversial change. I've got rid of StdRdOptions
structure, in which options for heap, toast, nbtree, hash and spgist were
stored in Bytea representation. In indexes only fillfactor value were
actually used from the whole big structure. So I've created a separate
structure for each relation type. HeapOptions and ToastOptions are very
similar to each other. So common part of creating these catalogs were moved
to
add_autovacuum_options function that are called while creating each catalog.Now to the controversial part: in src/include/utils/rel.h had a lot of
Relation* macroses that used StdRdOptions structure. I've changed them into
View* Heap* and Toast* analogues, and changed the code to use these macroses
instead of Relation* one. But this part of code I least sure of. I'd like
to ask experienced developers to double check it.8. I've moved all options-abstract code into options.c and options.h file,
and left in reloptions.c and reloptions.h only the code that concerns
relation options. Actually the main idea of this patch was to get this
abstract code in order to use it for custom attribute options later. All
the rest just a side effects :-)So, before adding this to commitfest I want somebody to give a general look
at it, if the code is OK.Alvaro Herrera, you once said that you can review the patch...
The patch is available in the attachment. It can be applied to current
masterYou can also see latest version on my github
https://github.com/dhyannataraj/postgres/tree/reloption_fix
at the reloption_fix branch.ATTRIBUTION NOTICE: I wrote this patch, when I was an employee in Postgres
Professional company. So this patch should be attributed as patch from
Postgres Pro (or something like that). For some reasons I did not manage to
commit this patch while I left that job. But I got a permission to commit it
even if I left the company.
So my contribution to this patch as a independent developer was final code
cleanup, and writing some more comments. All the rest was a work of Postgres
Pro employee Nikolay Shaplov.
--
Nikolay Shaplov, independent Perl & C/C++ developer. Available for hire.
Attachments:
reloptions6a.difftext/x-patch; charset=UTF-8; name=reloptions6a.diffDownload
diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
index 0cfe49a..21e59df 100644
--- a/contrib/bloom/bloom.h
+++ b/contrib/bloom/bloom.h
@@ -204,7 +204,7 @@ extern IndexBulkDeleteResult *blbulkdelete(IndexVacuumInfo *info,
void *callback_state);
extern IndexBulkDeleteResult *blvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
-extern bytea *bloptions(Datum reloptions, bool validate);
+extern bytea *bloptions(Datum reloptions, bool validate, bool for_alter);
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..93014af 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,27 @@ 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 +451,38 @@ BloomInitMetapage(Relation index)
UnlockReleaseBuffer(metaBuffer);
}
-/*
- * Parse reloptions for bloom index, producing a BloomOptions struct.
- */
-bytea *
-bloptions(Datum reloptions, bool validate)
+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);
+ bl_relopt_catalog->struct_size = sizeof(BloomOptions);
+ 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 cbc50f7..7e3bbc0 100644
--- a/contrib/bloom/expected/bloom.out
+++ b/contrib/bloom/expected/bloom.out
@@ -210,3 +210,37 @@ ORDER BY 1;
text_ops | t
(2 rows)
+-- reloptions test
+DROP INDEX bloomidx;
+CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (length=7, col1 = 4);
+SELECT reloptions FROM pg_class WHERE oid = 'bloomidx'::regclass;
+ reloptions
+-------------------
+ {length=7,col1=4}
+(1 row)
+
+-- check for min and max values
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=0);
+ERROR: value 0 out of bounds for option "length"
+DETAIL: Valid values are between "1" and "4096".
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=4097);
+ERROR: value 4097 out of bounds for option "length"
+DETAIL: Valid values are between "1" and "4096".
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=0);
+ERROR: value 0 out of bounds for option "col1"
+DETAIL: Valid values are between "1" and "4095".
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=4096);
+ERROR: value 4096 out of bounds for option "col1"
+DETAIL: Valid values are between "1" and "4095".
+-- check post_validate for colN<lengh
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=10,col1=11);
+ERROR: col0 should not be grater than length
+-- check that ALTERing of reloptions is forbidden
+ALTER INDEX bloomidx SET (length=4);
+ERROR: changing parameter "length" is not allowed
+ALTER INDEX bloomidx SET (col1=4);
+ERROR: changing parameter "col1" is not allowed
+ALTER INDEX bloomidx RESET (length);
+ERROR: changing parameter "length" is not allowed
+ALTER INDEX bloomidx RESET (col1);
+ERROR: changing parameter "col1" is not allowed
diff --git a/contrib/bloom/sql/bloom.sql b/contrib/bloom/sql/bloom.sql
index 2227460..dbac21c 100644
--- a/contrib/bloom/sql/bloom.sql
+++ b/contrib/bloom/sql/bloom.sql
@@ -81,3 +81,23 @@ SELECT opcname, amvalidate(opc.oid)
FROM pg_opclass opc JOIN pg_am am ON am.oid = opcmethod
WHERE amname = 'bloom'
ORDER BY 1;
+
+-- reloptions test
+
+DROP INDEX bloomidx;
+CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (length=7, col1 = 4);
+SELECT reloptions FROM pg_class WHERE oid = 'bloomidx'::regclass;
+-- check for min and max values
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=0);
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=4097);
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=0);
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=4096);
+-- check post_validate for colN<lengh
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=10,col1=11);
+
+-- check that ALTERing of reloptions is forbidden
+
+ALTER INDEX bloomidx SET (length=4);
+ALTER INDEX bloomidx SET (col1=4);
+ALTER INDEX bloomidx RESET (length);
+ALTER INDEX bloomidx RESET (col1);
diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
index ac43c45..0952720 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 735b794..b07399f 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 e24db56..be15189 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -65,7 +65,7 @@ PG_FUNCTION_INFO_V1(postgres_fdw_validator);
Datum
postgres_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid catalog = PG_GETARG_OID(1);
ListCell *cell;
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index b22563b..f69bcbd 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"
@@ -70,6 +70,7 @@ static void union_tuples(BrinDesc *bdesc, BrinMemTuple *a,
BrinTuple *b);
static void brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy);
+void *bringetreloptcatalog (void);
/*
* BRIN handler function: return IndexAmRoutine with access method parameters
@@ -103,7 +104,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;
@@ -113,6 +113,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;
@@ -756,36 +757,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)}
- };
-
- 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.
*/
@@ -1235,3 +1206,25 @@ brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy)
if (vacuum_fsm)
FreeSpaceMapVacuum(idxrel);
}
+
+static options_catalog * brin_relopt_catalog = NULL;
+
+void *
+bringetreloptcatalog (void)
+{
+ if (! brin_relopt_catalog)
+ {
+ brin_relopt_catalog = allocateOptionsCatalog(NULL);
+ brin_relopt_catalog->struct_size = sizeof(BrinOptions);
+
+ 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);
+ }
+ return brin_relopt_catalog;
+}
diff --git a/src/backend/access/common/Makefile b/src/backend/access/common/Makefile
index fb27944..c9fb063 100644
--- a/src/backend/access/common/Makefile
+++ b/src/backend/access/common/Makefile
@@ -12,7 +12,7 @@ subdir = src/backend/access/common
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
-OBJS = bufmask.o heaptuple.o indextuple.o printsimple.o printtup.o \
+OBJS = bufmask.o heaptuple.o indextuple.o options.o printsimple.o printtup.o \
reloptions.o scankey.o tupconvert.o tupdesc.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/common/options.c b/src/backend/access/common/options.c
new file mode 100644
index 0000000..3b9a267
--- /dev/null
+++ b/src/backend/access/common/options.c
@@ -0,0 +1,1353 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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);
+void *optionsAllocateBytea(options_catalog *catalog, List *options);
+
+
+/*
+ * 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.
+ */
+
+
+/*
+ * 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.
+ */
+
+options_catalog *
+allocateOptionsCatalog(char *namespace)
+{
+ 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;
+
+ catalog->definitions = NULL;
+ catalog->num = 0;
+ catalog->num_allocated = 0;
+ catalog->struct_size = -1; /* Will Assert if not properly changed */
+ 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 + 1 >= catalog->num_allocated)
+ {
+ MemoryContext oldcxt;
+
+ 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->definitions[catalog->num + 1] = NULL;
+ 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
+ */
+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.
+ */
+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++)
+ {
+ text *option_src = DatumGetTextP(options[i]);
+ option_value * option_dst;
+ char *text_str = VARDATA(option_src);
+ int text_len = VARSIZE(option_src) - 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
+ */
+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.
+ */
+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.
+ */
+void *
+optionsAllocateBytea(options_catalog *catalog, List *options)
+{
+ Size size;
+ int i;
+ ListCell *cell;
+ int length;
+ void *res;
+
+ Assert(catalog->struct_size > 0); /* Make sure it was initialized */
+ 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.
+ *
+ */
+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 816483e..c872d6a 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
@@ -15,884 +18,16 @@
#include "postgres.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) don't forget to document the option
- *
- * Note that we don't handle "oids" in relOpts because it is handled by
- * interpretOidsOption().
- */
-
-static relopt_bool boolRelOpts[] =
-{
- {
- {
- "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,
- AccessExclusiveLock
- },
-#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,
- AccessExclusiveLock
- },
- -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,
- AccessExclusiveLock
- },
- -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,
- AccessExclusiveLock
- },
- -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,
- AccessExclusiveLock
- },
- 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,
- AccessExclusiveLock
- },
- 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++)
- {
- text *oldoption = DatumGetTextP(oldoptions[i]);
- char *text_str = VARDATA(oldoption);
- int text_len = VARSIZE(oldoption) - 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.
@@ -908,11 +43,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,
@@ -928,16 +64,19 @@ 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;
@@ -948,529 +87,293 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
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.
+ * 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
+ */
+
+/*
+ * 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.
*
- * If there are no options of the given kind, numrelopts is set to 0 and NULL
- * is returned.
+ * 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.
*
- * 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.
+ * 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
*/
-relopt_value *
-parseRelOptions(Datum options, bool validate, relopt_kind kind,
- int *numrelopts)
-{
- relopt_value *reloptions;
- 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)
- {
- *numrelopts = 0;
- return NULL;
- }
-
- 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++)
- {
- text *optiontext = DatumGetTextP(optiondatums[i]);
- char *text_str = VARDATA(optiontext);
- int text_len = VARSIZE(optiontext) - 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;
+void
+add_autovacuum_options(options_catalog *catalog, int base_offset,
+ bool for_toast)
+{
+ 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);
}
/*
- * Subroutine for parseRelOptions, to parse and validate a single option's
- * value
+ * get_heap_relopt_catalog
+ * Returns an options catalog for heap relation.
*/
-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)));
+static options_catalog * heap_relopt_catalog = NULL;
- 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)
+options_catalog *
+get_heap_relopt_catalog(void)
+{
+ if (! heap_relopt_catalog)
{
- 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;
+ heap_relopt_catalog = allocateOptionsCatalog(NULL);
+ heap_relopt_catalog->struct_size = sizeof(HeapOptions);
- 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;
+ 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);
- 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;
+ add_autovacuum_options(heap_relopt_catalog,
+ offsetof(HeapOptions, autovacuum), false);
- 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;
+ 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",
+ AccessExclusiveLock,
+ 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);
}
-
- if (parsed)
- option->isset = true;
- if (!nofree)
- pfree(value);
+ return heap_relopt_catalog;
}
/*
- * Given the result from parseRelOptions, allocate a struct that's of the
- * specified base size plus any extra space that's needed for string variables.
- *
- * "base" should be sizeof(struct) of the reloptions struct (StdRdOptions or
- * equivalent).
+ * get_toast_relopt_catalog
+ * Returns an options catalog for toast relation.
*/
-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;
+static options_catalog * toast_relopt_catalog = NULL;
- return palloc0(size);
-}
-
-/*
- * Given the result of parseRelOptions and a parsing table, fill in the
- * struct (previously allocated with allocateReloptStruct) with the parsed
- * values.
- *
- * rdopts is the pointer to the allocated struct to be filled.
- * basesize is the sizeof(struct) that was passed to allocateReloptStruct.
- * options, of length numoptions, is parseRelOptions' output.
- * elems, of length numelems, is the table describing the allowed options.
- * When validate is true, it is expected that all options appear in elems.
- */
-void
-fillRelOptions(void *rdopts, Size basesize,
- relopt_value *options, int numoptions,
- bool validate,
- const relopt_parse_elt *elems, int numelems)
+options_catalog *
+get_toast_relopt_catalog(void)
{
- int i;
- int offset = basesize;
-
- for (i = 0; i < numoptions; i++)
+ if (! toast_relopt_catalog)
{
- int j;
- bool found = false;
+ toast_relopt_catalog = allocateOptionsCatalog("toast");
+ toast_relopt_catalog->struct_size = sizeof(ToastOptions);
- 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);
+ add_autovacuum_options(toast_relopt_catalog,
+ offsetof(ToastOptions, autovacuum), true);
}
- SET_VARSIZE(rdopts, offset);
+ return toast_relopt_catalog;
}
-
/*
- * Option parser for anything that uses StdRdOptions.
+ * get_view_relopt_catalog
+ * Returns an options catalog for view 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)}
- };
-
- 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));
+static options_catalog * view_relopt_catalog = NULL;
- pfree(options);
-
- return (bytea *) rdopts;
-}
-
-/*
- * Option parser for views
- */
-bytea *
-view_reloptions(Datum reloptions, bool validate)
+options_catalog *
+get_view_relopt_catalog(void)
{
- 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);
+ static const char *enum_names[] = VIEW_OPTION_CHECK_OPTION_VALUE_NAMES;
- /* if none set, we're done */
- if (numoptions == 0)
- return NULL;
-
- vopts = allocateReloptStruct(sizeof(ViewOptions), options, numoptions);
-
- fillRelOptions((void *) vopts, sizeof(ViewOptions), options, numoptions,
- validate, tab, lengthof(tab));
-
- pfree(options);
-
- return (bytea *) vopts;
-}
+ if (! view_relopt_catalog)
+ {
+ view_relopt_catalog = allocateOptionsCatalog(NULL);
+ view_relopt_catalog->struct_size = sizeof(ViewOptions);
-/*
- * Parse options for heaps, views and toast tables.
- */
-bytea *
-heap_reloptions(char relkind, Datum reloptions, bool validate)
-{
- StdRdOptions *rdopts;
+ optionsCatalogAddItemBool(view_relopt_catalog, "security_barrier",
+ "View acts as a row security barrier",
+ AccessExclusiveLock,
+ 0, offsetof(ViewOptions, security_barrier), false);
- switch (relkind)
- {
- case RELKIND_TOASTVALUE:
- rdopts = (StdRdOptions *)
- default_reloptions(reloptions, validate, RELOPT_KIND_TOAST);
- if (rdopts != NULL)
- {
- /* adjust default-only parameters for TOAST relations */
- rdopts->fillfactor = 100;
- rdopts->autovacuum.analyze_threshold = -1;
- rdopts->autovacuum.analyze_scale_factor = -1;
- }
- return (bytea *) rdopts;
- case RELKIND_RELATION:
- case RELKIND_MATVIEW:
- case RELKIND_PARTITIONED_TABLE:
- return default_reloptions(reloptions, validate, RELOPT_KIND_HEAP);
- default:
- /* other relkinds are not supported */
- return NULL;
+ 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;
}
-
/*
- * Parse options for indexes.
- *
- * amoptions index AM's option parser function
- * reloptions options as text[] datum
- * validate error flag
+ * get_attribute_options_catalog
+ * Returns an options catalog for heap attributes
*/
-bytea *
-index_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
-{
- Assert(amoptions != NULL);
-
- /* Assume function is strict */
- if (!PointerIsValid(DatumGetPointer(reloptions)))
- return NULL;
-
- return amoptions(reloptions, validate);
-}
+static options_catalog * attribute_options_catalog = NULL;
-/*
- * Option parser for attribute reloptions
- */
-bytea *
-attribute_reloptions(Datum reloptions, bool validate)
+options_catalog *
+get_attribute_options_catalog(void)
{
- 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);
-
- fillRelOptions((void *) aopts, sizeof(AttributeOpts), options, numoptions,
- validate, tab, lengthof(tab));
+ if (! attribute_options_catalog)
+ {
+ attribute_options_catalog = allocateOptionsCatalog(NULL);
+ attribute_options_catalog->struct_size = sizeof(AttributeOpts);
- 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)",
+ AccessExclusiveLock,
+ 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",
+ AccessExclusiveLock,
+ 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;
+static options_catalog * tablespace_options_catalog = NULL;
- tsopts = allocateReloptStruct(sizeof(TableSpaceOpts), options, numoptions);
+options_catalog *
+get_tablespace_options_catalog(void)
+{
+ if (! tablespace_options_catalog)
+ {
+ tablespace_options_catalog = allocateOptionsCatalog(NULL);
+ tablespace_options_catalog->struct_size = sizeof(TableSpaceOpts);
- fillRelOptions((void *) tsopts, sizeof(TableSpaceOpts), options, numoptions,
- validate, tab, lengthof(tab));
+ optionsCatalogAddItemReal(tablespace_options_catalog,
+ "random_page_cost",
+ "Sets the planner's estimate of the cost of a nonsequentially fetched disk page",
+ AccessExclusiveLock,
+ 0, offsetof(TableSpaceOpts, random_page_cost), -1, 0.0, DBL_MAX);
- pfree(options);
+ optionsCatalogAddItemReal(tablespace_options_catalog, "seq_page_cost",
+ "Sets the planner's estimate of the cost of a sequentially fetched disk page",
+ AccessExclusiveLock,
+ 0, offsetof(TableSpaceOpts, seq_page_cost), -1, 0.0, DBL_MAX);
- return (bytea *) tsopts;
+ optionsCatalogAddItemInt(tablespace_options_catalog,
+ "effective_io_concurrency",
+ "Number of simultaneous requests that can be handled efficiently by the disk subsystem",
+ AccessExclusiveLock,
+ 0, offsetof(TableSpaceOpts, effective_io_concurrency),
+#ifdef USE_PREFETCH
+ -1, 0, MAX_IO_CONCURRENCY
+#else
+ 0, 0, 0
+#endif
+ );
+ }
+ return tablespace_options_catalog;
}
/*
@@ -1480,33 +383,51 @@ 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;
+ 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;
+ 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;
+ DefElem *def = (DefElem *) lfirst(cell);
- for (i = 0; relOpts[i]; i++)
+ int 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 d03d59d..50ed21c 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -16,7 +16,7 @@
#include "access/gin_private.h"
#include "access/ginxlog.h"
-#include "access/reloptions.h"
+#include "access/options.h"
#include "access/xloginsert.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_type.h"
@@ -26,7 +26,8 @@
#include "utils/builtins.h"
#include "utils/index_selfuncs.h"
#include "utils/typcache.h"
-
+#include "utils/memutils.h"
+#include "utils/guc.h"
/*
* GIN handler function: return IndexAmRoutine with access method parameters
@@ -60,7 +61,6 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amvacuumcleanup = ginvacuumcleanup;
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = gincostestimate;
- amroutine->amoptions = ginoptions;
amroutine->amproperty = NULL;
amroutine->amvalidate = ginvalidate;
amroutine->ambeginscan = ginbeginscan;
@@ -70,6 +70,7 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amendscan = ginendscan;
amroutine->ammarkpos = NULL;
amroutine->amrestrpos = NULL;
+ amroutine->amrelopt_catalog = gingetreloptcatalog;
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
@@ -593,35 +594,6 @@ ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
return entries;
}
-bytea *
-ginoptions(Datum reloptions, bool validate)
-{
- relopt_value *options;
- GinOptions *rdopts;
- int numoptions;
- static const relopt_parse_elt tab[] = {
- {"fastupdate", RELOPT_TYPE_BOOL, offsetof(GinOptions, useFastUpdate)},
- {"gin_pending_list_limit", RELOPT_TYPE_INT, offsetof(GinOptions,
- pendingListCleanupSize)}
- };
-
- options = parseRelOptions(reloptions, validate, RELOPT_KIND_GIN,
- &numoptions);
-
- /* if none set, we're done */
- if (numoptions == 0)
- return NULL;
-
- rdopts = allocateReloptStruct(sizeof(GinOptions), options, numoptions);
-
- fillRelOptions((void *) rdopts, sizeof(GinOptions), options, numoptions,
- validate, tab, lengthof(tab));
-
- pfree(options);
-
- return (bytea *) rdopts;
-}
-
/*
* Fetch index's statistical data into *stats
*
@@ -698,3 +670,32 @@ ginUpdateStats(Relation index, const GinStatsData *stats)
END_CRIT_SECTION();
}
+
+
+static options_catalog * gin_relopt_catalog = NULL;
+
+void *
+gingetreloptcatalog (void)
+{
+ if (! gin_relopt_catalog)
+ {
+ gin_relopt_catalog = allocateOptionsCatalog(NULL);
+ gin_relopt_catalog->struct_size = sizeof(GinOptions);
+
+ 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 6593771..0a2cd59 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;
@@ -1583,3 +1583,4 @@ gistvacuumpage(Relation rel, Page page, Buffer buffer)
* the page.
*/
}
+
diff --git a/src/backend/access/gist/gistbuild.c b/src/backend/access/gist/gistbuild.c
index f1f08bb..936d4d0 100644
--- a/src/backend/access/gist/gistbuild.c
+++ b/src/backend/access/gist/gistbuild.c
@@ -126,11 +126,9 @@ 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 +232,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 f92baed..ff8aaf9 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -17,14 +17,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.
*/
@@ -811,34 +810,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.
*
@@ -964,3 +935,32 @@ 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);
+ gist_relopt_catalog->struct_size = sizeof(GiSTOptions);
+
+ 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 24510e7..1980b21 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -77,7 +77,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;
@@ -87,6 +86,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 9485978..103601d 100644
--- a/src/backend/access/hash/hashpage.c
+++ b/src/backend/access/hash/hashpage.c
@@ -332,7 +332,7 @@ _hash_metapinit(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 c705531..478cb44 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"
@@ -219,12 +219,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
*/
@@ -446,3 +440,26 @@ _hash_get_newbucket_from_oldbucket(Relation rel, Bucket old_bucket,
return new_bucket;
}
+
+static options_catalog * hash_relopt_catalog = NULL;
+
+void *
+hashgetreloptcatalog(void)
+{
+ if (! hash_relopt_catalog)
+ {
+ hash_relopt_catalog = allocateOptionsCatalog(NULL);
+ hash_relopt_catalog->struct_size = sizeof(HashRelOptions);
+
+ 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 af25836..e2ffc95 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -2657,7 +2657,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 6529fe3..78ecc46 100644
--- a/src/backend/access/heap/hio.c
+++ b/src/backend/access/heap/hio.c
@@ -323,8 +323,11 @@ RelationGetBufferForTuple(Relation relation, Size len,
len, MaxHeapTupleSize)));
/* Compute desired extra freespace due to fillfactor option */
- saveFreeSpace = RelationGetTargetPageFreeSpace(relation,
- HEAP_DEFAULT_FILLFACTOR);
+ if (IsToastRelation(relation))
+ saveFreeSpace = ToastGetTargetPageFreeSpace();
+ else
+ saveFreeSpace = HeapGetTargetPageFreeSpace(relation,
+ HEAP_DEFAULT_FILLFACTOR);
if (otherBuffer != InvalidBuffer)
otherBlock = BufferGetBlockNumber(otherBuffer);
diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c
index d69a266..fef5e40 100644
--- a/src/backend/access/heap/pruneheap.c
+++ b/src/backend/access/heap/pruneheap.c
@@ -131,8 +131,12 @@ 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,
- HEAP_DEFAULT_FILLFACTOR);
+
+ if (IsToastRelation(relation))
+ minfree = ToastGetTargetPageFreeSpace();
+ else
+ minfree = HeapGetTargetPageFreeSpace(relation,
+ HEAP_DEFAULT_FILLFACTOR);
minfree = Max(minfree, BLCKSZ / 10);
if (PageIsFull(page) || PageGetHeapFreeSpace(page) < minfree)
diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c
index c7b283c..ee859e7 100644
--- a/src/backend/access/heap/rewriteheap.c
+++ b/src/backend/access/heap/rewriteheap.c
@@ -665,7 +665,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 6dca810..6b12304 100644
--- a/src/backend/access/nbtree/nbtinsert.c
+++ b/src/backend/access/nbtree/nbtinsert.c
@@ -1433,8 +1433,7 @@ _bt_findsplitloc(Relation rel,
state.is_rightmost = P_RIGHTMOST(opaque);
state.have_split = false;
if (state.is_leaf)
- state.fillfactor = RelationGetFillFactor(rel,
- BTREE_DEFAULT_FILLFACTOR);
+ state.fillfactor = BTGetFillFactor(rel);
else
state.fillfactor = BTREE_NONLEAF_FILLFACTOR;
state.newitemonleft = false; /* these just to keep compiler quiet */
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index 775f2ff..9d1b8ac 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -21,6 +21,7 @@
#include "access/nbtree.h"
#include "access/relscan.h"
#include "access/xlog.h"
+#include "access/reloptions.h"
#include "catalog/index.h"
#include "commands/vacuum.h"
#include "pgstat.h"
@@ -34,7 +35,6 @@
#include "utils/index_selfuncs.h"
#include "utils/memutils.h"
-
/* Working state for btbuild and its callback */
typedef struct
{
@@ -116,8 +116,7 @@ static void btvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
BTCycleId cycleid);
static void btvacuumpage(BTVacState *vstate, BlockNumber blkno,
BlockNumber orig_blkno);
-
-
+void * btgetreloptcatalog (void);
/*
* Btree handler function: return IndexAmRoutine with access method parameters
* and callbacks.
@@ -150,7 +149,6 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amvacuumcleanup = btvacuumcleanup;
amroutine->amcanreturn = btcanreturn;
amroutine->amcostestimate = btcostestimate;
- amroutine->amoptions = btoptions;
amroutine->amproperty = btproperty;
amroutine->amvalidate = btvalidate;
amroutine->ambeginscan = btbeginscan;
@@ -160,6 +158,7 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amendscan = btendscan;
amroutine->ammarkpos = btmarkpos;
amroutine->amrestrpos = btrestrpos;
+ amroutine->amrelopt_catalog = btgetreloptcatalog;
amroutine->amestimateparallelscan = btestimateparallelscan;
amroutine->aminitparallelscan = btinitparallelscan;
amroutine->amparallelrescan = btparallelrescan;
@@ -1391,3 +1390,26 @@ btcanreturn(Relation index, int attno)
{
return true;
}
+
+
+static options_catalog * bt_relopt_catalog = NULL;
+
+void *
+btgetreloptcatalog (void)
+{
+ if (! bt_relopt_catalog)
+ {
+ bt_relopt_catalog = allocateOptionsCatalog(NULL);
+ bt_relopt_catalog->struct_size = sizeof(BTRelOptions);
+
+ 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 3d041c4..692c751 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 5b259a3..3efb05c 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 "access/options.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 e57ac49..b24178c 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,26 @@ 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);
+ spgist_relopt_catalog->struct_size = sizeof(SpGistRelOptions);
+
+ 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 0e42316..838b357 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 646a884..5020ee1 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 d5d40e6..7aa4576 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 72bb06c..071bf64 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -325,7 +325,7 @@ DefineIndex(Oid relationId,
Form_pg_am accessMethodForm;
IndexAmRoutine *amRoutine;
bool amcanorder;
- amoptions_function amoptions;
+ amrelopt_catalog_function amoption_catalog;
Datum reloptions;
int16 *coloptions;
IndexInfo *indexInfo;
@@ -528,7 +528,7 @@ DefineIndex(Oid relationId,
accessMethodName)));
amcanorder = amRoutine->amcanorder;
- amoptions = amRoutine->amoptions;
+ amoption_catalog = amRoutine->amrelopt_catalog;
pfree(amRoutine);
ReleaseSysCache(tuple);
@@ -542,10 +542,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 3cea220..f18fca7 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -497,7 +497,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
Datum reloptions;
ListCell *listptr;
AttrNumber attnum;
- static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
Oid ofTypeId;
ObjectAddress address;
@@ -583,13 +582,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)
{
@@ -3053,7 +3062,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);
@@ -3095,13 +3105,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)
{
@@ -3310,7 +3322,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:
@@ -4066,7 +4081,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",
@@ -5909,11 +5924,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));
@@ -9910,7 +9925,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 */
@@ -9939,25 +9954,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,
@@ -9971,7 +10013,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;
@@ -10031,6 +10073,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);
@@ -10054,12 +10098,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));
@@ -10087,6 +10134,17 @@ 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 80515ba..7ee3250 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -333,10 +333,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
@@ -1024,10 +1025,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 fdb4f71..a32d376 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 4ed2705..4b77141 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -135,7 +135,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 fecc1d6..648d986 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -2841,7 +2841,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 ff2bab6..89d9fb9 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1393,7 +1393,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 0c5ffa0..bc21d81 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -2510,6 +2510,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 ||
@@ -2519,8 +2520,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 20bbb37..caae058 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -1484,7 +1484,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 */
@@ -2985,7 +2985,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
@@ -3021,8 +3021,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 3bc0ae5..7e7dec6 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -996,6 +996,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,
@@ -1016,18 +1018,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))
{
@@ -1093,8 +1101,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 f7f85b5..b5f3dce 100644
--- a/src/backend/utils/cache/attoptcache.c
+++ b/src/backend/utils/cache/attoptcache.c
@@ -150,7 +150,10 @@ get_attribute_options(Oid attrelid, int attnum)
opts = NULL;
else
{
- bytea *bytea_opts = attribute_reloptions(datum, false);
+ bytea *bytea_opts;
+
+ bytea_opts = optionsTextArrayToBytea(
+ get_attribute_options_catalog(), datum);
opts = MemoryContextAlloc(CacheMemoryContext,
VARSIZE(bytea_opts));
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 9001e20..19d7b44 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -459,7 +459,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 9dcb80c..74bf884 100644
--- a/src/backend/utils/cache/spccache.c
+++ b/src/backend/utils/cache/spccache.c
@@ -149,7 +149,9 @@ 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));
@@ -190,7 +192,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 f919cf8..9f3db93 100644
--- a/src/include/access/amapi.h
+++ b/src/include/access/amapi.h
@@ -98,10 +98,6 @@ typedef void (*amcostestimate_function) (struct PlannerInfo *root,
double *indexCorrelation,
double *indexPages);
-/* parse index reloptions */
-typedef bytea *(*amoptions_function) (Datum reloptions,
- bool validate);
-
/* report AM, index, or index column property */
typedef bool (*amproperty_function) (Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
@@ -139,6 +135,9 @@ typedef void (*ammarkpos_function) (IndexScanDesc scan);
/* restore marked scan position */
typedef void (*amrestrpos_function) (IndexScanDesc scan);
+/* get catalog of reloptions definitions */
+typedef void *(*amrelopt_catalog_function) ();
+
/*
* Callback function signatures - for parallel index scans.
*/
@@ -202,7 +201,6 @@ typedef struct IndexAmRoutine
amvacuumcleanup_function amvacuumcleanup;
amcanreturn_function amcanreturn; /* can be NULL */
amcostestimate_function amcostestimate;
- amoptions_function amoptions;
amproperty_function amproperty; /* can be NULL */
amvalidate_function amvalidate;
ambeginscan_function ambeginscan;
@@ -212,6 +210,7 @@ typedef struct IndexAmRoutine
amendscan_function amendscan;
ammarkpos_function ammarkpos; /* can be NULL */
amrestrpos_function amrestrpos; /* can be NULL */
+ amrelopt_catalog_function amrelopt_catalog; /* can be NULL */
/* interface functions to support parallel index scans */
amestimateparallelscan_function amestimateparallelscan; /* can be NULL */
diff --git a/src/include/access/brin.h b/src/include/access/brin.h
index 896824a..c84d6d4 100644
--- a/src/include/access/brin.h
+++ b/src/include/access/brin.h
@@ -25,6 +25,9 @@ typedef struct BrinOptions
} BrinOptions;
#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 abe8877..4c32dd1 100644
--- a/src/include/access/brin_internal.h
+++ b/src/include/access/brin_internal.h
@@ -102,7 +102,6 @@ extern IndexBulkDeleteResult *brinbulkdelete(IndexVacuumInfo *info,
void *callback_state);
extern IndexBulkDeleteResult *brinvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
-extern bytea *brinoptions(Datum reloptions, bool validate);
/* brin_validate.c */
extern bool brinvalidate(Oid opclassoid);
diff --git a/src/include/access/gin_private.h b/src/include/access/gin_private.h
index 34e7339..89559ab 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 5b33030..04fa840 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 3bf587b..02f33e9 100644
--- a/src/include/access/hash.h
+++ b/src/include/access/hash.h
@@ -196,6 +196,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)
*/
@@ -291,7 +304,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);
@@ -385,4 +397,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 6289ffa..38f0257 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..1be9df0
--- /dev/null
+++ b/src/include/access/options.h
@@ -0,0 +1,224 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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 */
+ 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);
+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 List *optionsDefListToRawValues(List *defList, options_parse_mode
+ parse_mode);
+extern Datum optionsValuesToTextArray(List *options_values);
+extern List *optionsTextArrayToRawValues(Datum array_datum);
+extern List *optionsMergeOptionValues(List *old_options, List *new_options);
+extern void optionsDefListValdateNamespaces(List *defList,
+ char **allowed_namespaces);
+extern List *optionsDefListFilterNamespaces(List *defList, char *namespace);
+extern List *optionsTextArrayToDefList(Datum options);
+extern List *optionsParseRawValues(List * raw_values, options_catalog *catalog,
+ options_parse_mode mode);
+extern bytea *optionsValuesToBytea(List * options, options_catalog *catalog);
+
+/*
+ * 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 861977a..78c45db 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,263 +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),
- /* if you add a new kind, make sure you update "last_default" too */
- RELOPT_KIND_LAST_DEFAULT = RELOPT_KIND_BRIN,
- /* 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 9dca8fd..a3b0a5e 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
@@ -183,7 +179,7 @@ typedef struct spgLeafConsistentOut
/* spgutils.c */
-extern bytea *spgoptions(Datum reloptions, bool validate);
+extern Datum spghandler(PG_FUNCTION_ARGS);
/* spginsert.c */
extern IndexBuildResult *spgbuild(Relation heap, Relation index,
diff --git a/src/include/access/spgist_private.h b/src/include/access/spgist_private.h
index e42079b..d5ab114 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 db7f145..d6464a7 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 f3a9701..33f5782 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -31,7 +31,7 @@ extern Oid AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode);
extern void AlterTable(Oid relid, LOCKMODE lockmode, AlterTableStmt *stmt);
-extern LOCKMODE AlterTableGetLockLevel(List *cmds);
+extern LOCKMODE AlterTableGetLockLevel(Oid relid, List *cmds);
extern void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index a617a7c..28113ef 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -269,7 +269,7 @@ typedef struct AutoVacOpts
float8 analyze_scale_factor;
} AutoVacOpts;
-typedef struct StdRdOptions
+typedef struct HeapOptions
{
int32 vl_len_; /* varlena header (do not touch directly!) */
int fillfactor; /* page fill factor in percent (0..100) */
@@ -277,107 +277,115 @@ typedef struct StdRdOptions
bool user_catalog_table; /* use as an additional catalog
* relation */
int parallel_workers; /* max number of parallel workers */
-} StdRdOptions;
+} HeapOptions;
+
+typedef struct ToastOptions
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ AutoVacOpts autovacuum; /* autovacuum-related options */
+} ToastOptions;
#define HEAP_MIN_FILLFACTOR 10
#define HEAP_DEFAULT_FILLFACTOR 100
+#define TOAST_DEFAULT_FILLFACTOR 100 /* Only default is actually used */
+
/*
- * RelationGetFillFactor
+ * HeapGetFillFactor
* Returns the relation's fillfactor. Note multiple eval of argument!
*/
-#define RelationGetFillFactor(relation, defaultff) \
+#define HeapGetFillFactor(relation, defaultff) \
((relation)->rd_options ? \
- ((StdRdOptions *) (relation)->rd_options)->fillfactor : (defaultff))
+ ((HeapOptions *) (relation)->rd_options)->fillfactor : (defaultff))
/*
- * RelationGetTargetPageUsage
- * Returns the relation's desired space usage per page in bytes.
- */
-#define RelationGetTargetPageUsage(relation, defaultff) \
- (BLCKSZ * RelationGetFillFactor(relation, defaultff) / 100)
-
-/*
- * RelationGetTargetPageFreeSpace
+ * HeapGetTargetPageFreeSpace
* Returns the relation's desired freespace per page in bytes.
*/
-#define RelationGetTargetPageFreeSpace(relation, defaultff) \
- (BLCKSZ * (100 - RelationGetFillFactor(relation, defaultff)) / 100)
+#define HeapGetTargetPageFreeSpace(relation, defaultff) \
+ (BLCKSZ * (100 - HeapGetFillFactor(relation, defaultff)) / 100)
/*
- * RelationIsUsedAsCatalogTable
+ * HeapIsUsedAsCatalogTable
* Returns whether the relation should be treated as a catalog table
* from the pov of logical decoding. Note multiple eval of argument!
*/
-#define RelationIsUsedAsCatalogTable(relation) \
+#define HeapIsUsedAsCatalogTable(relation) \
((relation)->rd_options && \
((relation)->rd_rel->relkind == RELKIND_RELATION || \
(relation)->rd_rel->relkind == RELKIND_MATVIEW) ? \
- ((StdRdOptions *) (relation)->rd_options)->user_catalog_table : false)
+ ((HeapOptions *) (relation)->rd_options)->user_catalog_table : false)
/*
- * RelationGetParallelWorkers
+ * HeapGetParallelWorkers
* Returns the relation's parallel_workers reloption setting.
* Note multiple eval of argument!
*/
-#define RelationGetParallelWorkers(relation, defaultpw) \
+#define HeapGetParallelWorkers(relation, defaultpw) \
((relation)->rd_options ? \
- ((StdRdOptions *) (relation)->rd_options)->parallel_workers : (defaultpw))
+ ((HeapOptions *) (relation)->rd_options)->parallel_workers : (defaultpw))
+#define ToastGetTargetPageFreeSpace() \
+ (BLCKSZ * (100 - TOAST_DEFAULT_FILLFACTOR) / 100)
/*
* ViewOptions
* Contents of rd_options for views
*/
+
+/*
+ * Definition of items of enum type. Names and codes. To add or modify item
+ * edit both lists
+ */
+#define VIEW_OPTION_CHECK_OPTION_VALUE_NAMES { \
+ "local", \
+ "cascaded", \
+ (const char *) NULL \
+}
+
+typedef enum view_option_check_option_value_numbers
+{
+ VIEW_OPTION_CHECK_OPTION_NOT_SET = -1,
+ VIEW_OPTION_CHECK_OPTION_LOCAL = 0,
+ VIEW_OPTION_CHECK_OPTION_CASCADED = 1,
+} view_option_check_option_value_numbers;
+
typedef struct ViewOptions
{
int32 vl_len_; /* varlena header (do not touch directly!) */
bool security_barrier;
- int check_option_offset;
+ int check_option;
} ViewOptions;
-
/*
- * RelationIsSecurityView
- * Returns whether the relation is security view, or not. Note multiple
- * eval of argument!
+ * ViewIsSecurityView
+ * Returns whether the view is security view, or not. Note multiple eval
+ * of argument!
*/
-#define RelationIsSecurityView(relation) \
+#define ViewIsSecurityView(relation) \
((relation)->rd_options ? \
- ((ViewOptions *) (relation)->rd_options)->security_barrier : false)
+ ((ViewOptions *) (relation)->rd_options)->security_barrier : false)
/*
- * RelationHasCheckOption
- * Returns true if the relation is a view defined with either the local
- * or the cascaded check option. Note multiple eval of argument!
+ * ViewHasCheckOption
+ * Returns true if view is defined with either the local or the cascaded
+ * check option. Note multiple eval of argument!
*/
-#define RelationHasCheckOption(relation) \
- ((relation)->rd_options && \
- ((ViewOptions *) (relation)->rd_options)->check_option_offset != 0)
-/*
- * RelationHasLocalCheckOption
- * Returns true if the relation is a view defined with the local check
- * option. Note multiple eval of argument!
- */
-#define RelationHasLocalCheckOption(relation) \
- ((relation)->rd_options && \
- ((ViewOptions *) (relation)->rd_options)->check_option_offset != 0 ? \
- strcmp((char *) (relation)->rd_options + \
- ((ViewOptions *) (relation)->rd_options)->check_option_offset, \
- "local") == 0 : false)
+#define ViewHasCheckOption(relation) \
+((relation)->rd_options && \
+ ((ViewOptions *) (relation)->rd_options)->check_option != \
+ VIEW_OPTION_CHECK_OPTION_NOT_SET)
/*
- * RelationHasCascadedCheckOption
- * Returns true if the relation is a view defined with the cascaded check
- * option. Note multiple eval of argument!
+ * ViewHasCascadedCheckOption
+ * Returns true if the view is defined with the cascaded check option.
+ * Note multiple eval of argument!
*/
-#define RelationHasCascadedCheckOption(relation) \
- ((relation)->rd_options && \
- ((ViewOptions *) (relation)->rd_options)->check_option_offset != 0 ? \
- strcmp((char *) (relation)->rd_options + \
- ((ViewOptions *) (relation)->rd_options)->check_option_offset, \
- "cascaded") == 0 : false)
+#define ViewHasCascadedCheckOption(relation) \
+((relation)->rd_options && \
+ ((ViewOptions *) (relation)->rd_options)->check_option == \
+ VIEW_OPTION_CHECK_OPTION_CASCADED)
/*
* RelationIsValid
@@ -556,7 +564,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 9885fcb..7238f18 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -2285,7 +2285,9 @@ ERROR: unrecognized parameter "autovacuum_enabled"
alter view my_locks set (autovacuum_enabled = false);
ERROR: unrecognized parameter "autovacuum_enabled"
alter table my_locks reset (autovacuum_enabled);
+ERROR: unrecognized parameter "autovacuum_enabled"
alter view my_locks reset (autovacuum_enabled);
+ERROR: unrecognized parameter "autovacuum_enabled"
begin;
alter view my_locks set (security_barrier=off);
select * from my_locks order by 1;
@@ -3373,3 +3375,10 @@ alter table p attach partition p1 for values from (1, 2) to (1, 10);
ERROR: partition constraint is violated by some row
-- cleanup: avoid using CASCADE
drop table p, p1, p11;
+-- test alter column options
+CREATE TABLE tmp(i integer);
+INSERT INTO tmp VALUES (1);
+ALTER TABLE tmp ALTER COLUMN i SET (n_distinct = 1, n_distinct_inherited = 2);
+ALTER TABLE tmp ALTER COLUMN i RESET (n_distinct_inherited);
+ANALYZE tmp;
+DROP TABLE tmp;
diff --git a/src/test/regress/expected/brin.out b/src/test/regress/expected/brin.out
index f0008dd3..c52b022 100644
--- a/src/test/regress/expected/brin.out
+++ b/src/test/regress/expected/brin.out
@@ -406,3 +406,8 @@ SELECT brin_summarize_new_values('brinidx'); -- ok, no change expected
0
(1 row)
+-- 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/create_index.out b/src/test/regress/expected/create_index.out
index e519fdb..2ff134e 100644
--- a/src/test/regress/expected/create_index.out
+++ b/src/test/regress/expected/create_index.out
@@ -2340,7 +2340,7 @@ CREATE INDEX hash_name_index ON hash_name_heap USING hash (random name_ops);
WARNING: hash indexes are not WAL-logged and their use is discouraged
CREATE INDEX hash_txt_index ON hash_txt_heap USING hash (random text_ops);
WARNING: hash indexes are not WAL-logged and their use is discouraged
-CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_ops);
+CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=60);
WARNING: hash indexes are not WAL-logged and their use is discouraged
CREATE UNLOGGED TABLE unlogged_hash_table (id int4);
CREATE INDEX unlogged_hash_index ON unlogged_hash_table USING hash (id int4_ops);
diff --git a/src/test/regress/expected/gist.out b/src/test/regress/expected/gist.out
index c7181b0..19f1881 100644
--- a/src/test/regress/expected/gist.out
+++ b/src/test/regress/expected/gist.out
@@ -12,6 +12,20 @@ insert into gist_point_tbl (id, p)
select g+100000, point(g*10+1, g*10+1) from generate_series(1, 10000) g;
-- To test vacuum, delete some entries from all over the index.
delete from gist_point_tbl where id % 2 = 1;
+-- Test buffering and fillfactor reloption takes valid values
+create index gist_pointidx2 on gist_point_tbl using gist(p) with (buffering = on, fillfactor=50);
+create index gist_pointidx3 on gist_point_tbl using gist(p) with (buffering = off);
+create index gist_pointidx4 on gist_point_tbl using gist(p) with (buffering = auto);
+--Test buffering and fillfactor for refising invalid values
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (buffering = invalid_value);
+ERROR: invalid value for "buffering" option
+DETAIL: Valid values are "on", "off", "auto".
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfactor=9);
+ERROR: value 9 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfactor=101);
+ERROR: value 101 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
-- And also delete some concentration of values. (GiST doesn't currently
-- attempt to delete pages even when they become empty, but if it did, this
-- would exercise it)
diff --git a/src/test/regress/expected/hash_index.out b/src/test/regress/expected/hash_index.out
index f8b9f02..9f9352d 100644
--- a/src/test/regress/expected/hash_index.out
+++ b/src/test/regress/expected/hash_index.out
@@ -232,3 +232,14 @@ INSERT INTO hash_heap_float4 VALUES (1.1,1);
CREATE INDEX hash_idx ON hash_heap_float4 USING hash (x);
WARNING: hash indexes are not WAL-logged and their use is discouraged
DROP TABLE hash_heap_float4 CASCADE;
+-- Check reloptions min max values and that it is not allowed to ALTER
+CREATE INDEX hash_f8_index2 ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=9);
+WARNING: hash indexes are not WAL-logged and their use is discouraged
+ERROR: value 9 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+CREATE INDEX hash_f8_index2 ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=101);
+WARNING: hash indexes are not WAL-logged and their use is discouraged
+ERROR: value 101 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+ALTER INDEX hash_f8_index SET (fillfactor=99);
+ERROR: changing parameter "fillfactor" is not allowed
diff --git a/src/test/regress/expected/reloptions.out b/src/test/regress/expected/reloptions.out
new file mode 100644
index 0000000..835d72f
--- /dev/null
+++ b/src/test/regress/expected/reloptions.out
@@ -0,0 +1,193 @@
+-- Simple create
+CREATE TABLE reloptions_test(i INT) WITH (fillfactor=30,autovacuum_enabled = false, autovacuum_analyze_scale_factor = 0.2);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+ reloptions
+------------------------------------------------------------------------------
+ {fillfactor=30,autovacuum_enabled=false,autovacuum_analyze_scale_factor=0.2}
+(1 row)
+
+-- Test сase insensitive
+CREATE TABLE reloptions_test3(i INT) WITH (FiLlFaCtoR=40);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test3'::regclass;
+ reloptions
+-----------------
+ {fillfactor=40}
+(1 row)
+
+-- Fail on min/max values check
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=2);
+ERROR: value 2 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=110);
+ERROR: value 110 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor = -10.0);
+ERROR: value -10.0 out of bounds for option "autovacuum_analyze_scale_factor"
+DETAIL: Valid values are between "0.000000" and "100.000000".
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor = 110.0);
+ERROR: value 110.0 out of bounds for option "autovacuum_analyze_scale_factor"
+DETAIL: Valid values are between "0.000000" and "100.000000".
+-- Fail when option and namespase do not exist
+CREATE TABLE reloptions_test2(i INT) WITH (not_existing_option=2);
+ERROR: unrecognized parameter "not_existing_option"
+CREATE TABLE reloptions_test2(i INT) WITH (not_existing_namespace.fillfactor=2);
+ERROR: unrecognized parameter namespace "not_existing_namespace"
+-- Fail while setting unproper value
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=30.5);
+ERROR: invalid value for integer option "fillfactor": 30.5
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor='string');
+ERROR: invalid value for integer option "fillfactor": string
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=true);
+ERROR: invalid value for integer option "fillfactor": true
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled=12);
+ERROR: invalid value for boolean option "autovacuum_enabled": 12
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled=30.5);
+ERROR: invalid value for boolean option "autovacuum_enabled": 30.5
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled='string');
+ERROR: invalid value for boolean option "autovacuum_enabled": string
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor='string');
+ERROR: invalid value for floating point option "autovacuum_analyze_scale_factor": string
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor=true);
+ERROR: invalid value for floating point option "autovacuum_analyze_scale_factor": true
+-- Specifing name only should fail as if there was =ture after it
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor);
+ERROR: invalid value for integer option "fillfactor": true
+-- Simple ALTER TABLE
+ALTER TABLE reloptions_test SET (fillfactor=31, autovacuum_analyze_scale_factor = 0.3);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+ reloptions
+------------------------------------------------------------------------------
+ {autovacuum_enabled=false,fillfactor=31,autovacuum_analyze_scale_factor=0.3}
+(1 row)
+
+-- Check that we cat set boolean option to true just by mentioning it
+ALTER TABLE reloptions_test SET (autovacuum_enabled, fillfactor=32);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+ reloptions
+-----------------------------------------------------------------------------
+ {autovacuum_analyze_scale_factor=0.3,autovacuum_enabled=true,fillfactor=32}
+(1 row)
+
+-- Check that RESET works well
+ALTER TABLE reloptions_test RESET (fillfactor);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+ reloptions
+---------------------------------------------------------------
+ {autovacuum_analyze_scale_factor=0.3,autovacuum_enabled=true}
+(1 row)
+
+-- Check that RESETting all values make NULL reloptions record in pg_class
+ALTER TABLE reloptions_test RESET (autovacuum_enabled, autovacuum_analyze_scale_factor);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass AND reloptions IS NULL;
+ reloptions
+------------
+
+(1 row)
+
+-- Check RESET fails on att=value
+ALTER TABLE reloptions_test RESET (fillfactor=12);
+ERROR: RESET must not include values for parameters
+-- Check oids options is ignored
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test(i INT) WITH (fillfactor=20, oids=true);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+ reloptions
+-----------------
+ {fillfactor=20}
+(1 row)
+
+-- Now testing toast.* options
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test (s VARCHAR) WITH (toast.autovacuum_vacuum_cost_delay = 23 );
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+ reloptions
+-----------------------------------
+ {autovacuum_vacuum_cost_delay=23}
+(1 row)
+
+ALTER TABLE reloptions_test SET (toast.autovacuum_vacuum_cost_delay = 24);
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+ reloptions
+-----------------------------------
+ {autovacuum_vacuum_cost_delay=24}
+(1 row)
+
+ALTER TABLE reloptions_test RESET (toast.autovacuum_vacuum_cost_delay);
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+ reloptions
+------------
+
+(1 row)
+
+-- Fail on unexisting options in toast namespace
+CREATE TABLE reloptions_test2 (i int) WITH (toast.not_existing_option = 42 );
+ERROR: unrecognized parameter "toast.not_existing_option"
+-- Fail on setting reloption to a table that does not have a TOAST relation
+CREATE TABLE reloptions_test2 (i int) WITH (toast.autovacuum_vacuum_cost_delay = 23 );
+ERROR: no TOAST relation was created for a table. Can't set toast.* storage parameters
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test(i INT);
+ALTER TABLE reloptions_test SET (toast.autovacuum_vacuum_cost_delay = 23);
+ERROR: TOAST relation for this table does not exist. Can't change toast.* storage parameters
+ALTER TABLE reloptions_test RESET (toast.autovacuum_vacuum_cost_delay);
+ERROR: TOAST relation for this table does not exist. Can't change toast.* storage parameters
+-- autovacuum_analyze_scale_factor and autovacuum_analyze_threshold should be
+-- accepted by heap but rejected by toast (special case)
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test (s VARCHAR) WITH (autovacuum_analyze_scale_factor=1, autovacuum_analyze_threshold=1);
+CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analyze_scale_factor=1);
+ERROR: unrecognized parameter "toast.autovacuum_analyze_scale_factor"
+CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analyze_threshold=1);
+ERROR: unrecognized parameter "toast.autovacuum_analyze_threshold"
+-- And now mixed toast + heap
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test (s VARCHAR) WITH (toast.autovacuum_vacuum_cost_delay = 23, autovacuum_vacuum_cost_delay = 24, fillfactor = 40);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+ reloptions
+-------------------------------------------------
+ {autovacuum_vacuum_cost_delay=24,fillfactor=40}
+(1 row)
+
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+ reloptions
+-----------------------------------
+ {autovacuum_vacuum_cost_delay=23}
+(1 row)
+
+-- Same FOR CREATE and ALTER INDEX for btree indexes
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (fillfactor=30);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx'::regclass;
+ reloptions
+-----------------
+ {fillfactor=30}
+(1 row)
+
+-- Fail when option and namespase do not exist
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_existing_option=2);
+ERROR: unrecognized parameter "not_existing_option"
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_existing_option.fillfactor=2);
+ERROR: unrecognized parameter namespace "not_existing_option"
+-- Check ranges
+CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfactor=1);
+ERROR: value 1 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfactor=130);
+ERROR: value 130 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+-- Check alter
+ALTER INDEX reloptions_test_idx SET (fillfactor=40);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx'::regclass;
+ reloptions
+-----------------
+ {fillfactor=40}
+(1 row)
+
+-- Check alter on empty relop list
+CREATE INDEX reloptions_test_idx3 ON reloptions_test (s);
+ALTER INDEX reloptions_test_idx3 SET (fillfactor=40);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx3'::regclass;
+ reloptions
+-----------------
+ {fillfactor=40}
+(1 row)
+
diff --git a/src/test/regress/expected/spgist.out b/src/test/regress/expected/spgist.out
index 0691e91..bb58fb7 100644
--- a/src/test/regress/expected/spgist.out
+++ b/src/test/regress/expected/spgist.out
@@ -4,7 +4,7 @@
-- There are other tests to test different SP-GiST opclasses. This is for
-- testing SP-GiST code itself.
create table spgist_point_tbl(id int4, p point);
-create index spgist_point_idx on spgist_point_tbl using spgist(p);
+create index spgist_point_idx on spgist_point_tbl using spgist(p) with (fillfactor = 75);
-- Test vacuum-root operation. It gets invoked when the root is also a leaf,
-- i.e. the index is very small.
insert into spgist_point_tbl (id, p)
@@ -37,3 +37,12 @@ select g, 'baaaaaaaaaaaaaar' || g from generate_series(1, 1000) g;
-- tuple to be moved to another page.
insert into spgist_text_tbl (id, t)
select -g, 'f' || repeat('o', 100-g) || 'surprise' from generate_series(1, 100) g;
+-- Check reloptions min, max values
+create index spgist_point_idx2 on spgist_point_tbl using spgist(p) with (fillfactor = 9);
+ERROR: value 9 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+create index spgist_point_idx2 on spgist_point_tbl using spgist(p) with (fillfactor = 101);
+ERROR: value 101 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+-- ALTER reloption should work
+alter index spgist_point_idx set (fillfactor = 76);
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out
index aa06d1d..287dd84 100644
--- a/src/test/regress/expected/updatable_views.out
+++ b/src/test/regress/expected/updatable_views.out
@@ -1551,7 +1551,7 @@ SELECT * FROM base_tbl;
ALTER VIEW rw_view1 SET (check_option=here); -- invalid
ERROR: invalid value for "check_option" option
-DETAIL: Valid values are "local" and "cascaded".
+DETAIL: Valid values are "local", "cascaded".
ALTER VIEW rw_view1 SET (check_option=local);
INSERT INTO rw_view2 VALUES (-20); -- should fail
ERROR: new row violates check option for view "rw_view1"
diff --git a/src/test/regress/input/tablespace.source b/src/test/regress/input/tablespace.source
index 03a62bd..7f7934b 100644
--- a/src/test/regress/input/tablespace.source
+++ b/src/test/regress/input/tablespace.source
@@ -12,10 +12,10 @@ DROP TABLESPACE regress_tblspacewith;
CREATE TABLESPACE regress_tblspace LOCATION '@testtablespace@';
-- try setting and resetting some properties for the new tablespace
-ALTER TABLESPACE regress_tblspace SET (random_page_cost = 1.0);
+ALTER TABLESPACE regress_tblspace SET (random_page_cost = 1.0, seq_page_cost = 1.1);
ALTER TABLESPACE regress_tblspace SET (some_nonexistent_parameter = true); -- fail
ALTER TABLESPACE regress_tblspace RESET (random_page_cost = 2.0); -- fail
-ALTER TABLESPACE regress_tblspace RESET (random_page_cost, seq_page_cost); -- ok
+ALTER TABLESPACE regress_tblspace RESET (random_page_cost, effective_io_concurrency); -- ok
-- create a schema we can use
CREATE SCHEMA testschema;
diff --git a/src/test/regress/output/tablespace.source b/src/test/regress/output/tablespace.source
index aaedf5f..2443511 100644
--- a/src/test/regress/output/tablespace.source
+++ b/src/test/regress/output/tablespace.source
@@ -14,12 +14,12 @@ DROP TABLESPACE regress_tblspacewith;
-- create a tablespace we can use
CREATE TABLESPACE regress_tblspace LOCATION '@testtablespace@';
-- try setting and resetting some properties for the new tablespace
-ALTER TABLESPACE regress_tblspace SET (random_page_cost = 1.0);
+ALTER TABLESPACE regress_tblspace SET (random_page_cost = 1.0, seq_page_cost = 1.1);
ALTER TABLESPACE regress_tblspace SET (some_nonexistent_parameter = true); -- fail
ERROR: unrecognized parameter "some_nonexistent_parameter"
ALTER TABLESPACE regress_tblspace RESET (random_page_cost = 2.0); -- fail
ERROR: RESET must not include values for parameters
-ALTER TABLESPACE regress_tblspace RESET (random_page_cost, seq_page_cost); -- ok
+ALTER TABLESPACE regress_tblspace RESET (random_page_cost, effective_io_concurrency); -- ok
-- create a schema we can use
CREATE SCHEMA testschema;
-- try a table
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index edeb2d6..f70cdf3 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -109,7 +109,7 @@ test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combo
# NB: temp.sql does a reconnect which transiently uses 2 connections,
# so keep this parallel group to at most 19 tests
# ----------
-test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml
+test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml reloptions
# event triggers cannot run concurrently with any test that runs DDL
test: event_trigger
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 27a46d7..b37f3b7 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -170,5 +170,6 @@ test: returning
test: largeobject
test: with
test: xml
+test: reloptions
test: event_trigger
test: stats
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index f7b754f..bada2a1 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -2229,3 +2229,11 @@ alter table p attach partition p1 for values from (1, 2) to (1, 10);
-- cleanup: avoid using CASCADE
drop table p, p1, p11;
+
+-- test alter column options
+CREATE TABLE tmp(i integer);
+INSERT INTO tmp VALUES (1);
+ALTER TABLE tmp ALTER COLUMN i SET (n_distinct = 1, n_distinct_inherited = 2);
+ALTER TABLE tmp ALTER COLUMN i RESET (n_distinct_inherited);
+ANALYZE tmp;
+DROP TABLE tmp;
diff --git a/src/test/regress/sql/brin.sql b/src/test/regress/sql/brin.sql
index 5bf5387..6353c82 100644
--- a/src/test/regress/sql/brin.sql
+++ b/src/test/regress/sql/brin.sql
@@ -409,3 +409,7 @@ UPDATE brintest SET textcol = '' WHERE textcol IS NOT NULL;
SELECT brin_summarize_new_values('brintest'); -- error, not an index
SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index
SELECT brin_summarize_new_values('brinidx'); -- ok, no change expected
+
+-- Check that changing reloptions for brin index is not allowed
+ALTER INDEX brinidx SET (pages_per_range = 10);
+ALTER INDEX brinidx RESET (pages_per_range);
diff --git a/src/test/regress/sql/create_index.sql b/src/test/regress/sql/create_index.sql
index 1648072..1f8d101 100644
--- a/src/test/regress/sql/create_index.sql
+++ b/src/test/regress/sql/create_index.sql
@@ -682,7 +682,7 @@ CREATE INDEX hash_name_index ON hash_name_heap USING hash (random name_ops);
CREATE INDEX hash_txt_index ON hash_txt_heap USING hash (random text_ops);
-CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_ops);
+CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=60);
CREATE UNLOGGED TABLE unlogged_hash_table (id int4);
CREATE INDEX unlogged_hash_index ON unlogged_hash_table USING hash (id int4_ops);
diff --git a/src/test/regress/sql/gist.sql b/src/test/regress/sql/gist.sql
index 9d4ff1e..ce1ac1a 100644
--- a/src/test/regress/sql/gist.sql
+++ b/src/test/regress/sql/gist.sql
@@ -17,6 +17,17 @@ select g+100000, point(g*10+1, g*10+1) from generate_series(1, 10000) g;
-- To test vacuum, delete some entries from all over the index.
delete from gist_point_tbl where id % 2 = 1;
+-- Test buffering and fillfactor reloption takes valid values
+create index gist_pointidx2 on gist_point_tbl using gist(p) with (buffering = on, fillfactor=50);
+create index gist_pointidx3 on gist_point_tbl using gist(p) with (buffering = off);
+create index gist_pointidx4 on gist_point_tbl using gist(p) with (buffering = auto);
+--Test buffering and fillfactor for refising invalid values
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (buffering = invalid_value);
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfactor=9);
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfactor=101);
+
+
+
-- And also delete some concentration of values. (GiST doesn't currently
-- attempt to delete pages even when they become empty, but if it did, this
-- would exercise it)
diff --git a/src/test/regress/sql/hash_index.sql b/src/test/regress/sql/hash_index.sql
index 15a3b06..b034efb 100644
--- a/src/test/regress/sql/hash_index.sql
+++ b/src/test/regress/sql/hash_index.sql
@@ -194,3 +194,8 @@ CREATE TABLE hash_heap_float4 (x float4, y int);
INSERT INTO hash_heap_float4 VALUES (1.1,1);
CREATE INDEX hash_idx ON hash_heap_float4 USING hash (x);
DROP TABLE hash_heap_float4 CASCADE;
+
+-- Check reloptions min max values and that it is not allowed to ALTER
+CREATE INDEX hash_f8_index2 ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=9);
+CREATE INDEX hash_f8_index2 ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=101);
+ALTER INDEX hash_f8_index SET (fillfactor=99);
\ No newline at end of file
diff --git a/src/test/regress/sql/reloptions.sql b/src/test/regress/sql/reloptions.sql
new file mode 100644
index 0000000..0bfec28
--- /dev/null
+++ b/src/test/regress/sql/reloptions.sql
@@ -0,0 +1,153 @@
+
+-- Simple create
+CREATE TABLE reloptions_test(i INT) WITH (fillfactor=30,autovacuum_enabled = false, autovacuum_analyze_scale_factor = 0.2);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+
+-- Test сase insensitive
+CREATE TABLE reloptions_test3(i INT) WITH (FiLlFaCtoR=40);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test3'::regclass;
+
+-- Fail on min/max values check
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=2);
+
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=110);
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor = -10.0);
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor = 110.0);
+
+-- Fail when option and namespase do not exist
+
+CREATE TABLE reloptions_test2(i INT) WITH (not_existing_option=2);
+
+CREATE TABLE reloptions_test2(i INT) WITH (not_existing_namespace.fillfactor=2);
+
+-- Fail while setting unproper value
+
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=30.5);
+
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor='string');
+
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=true);
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled=12);
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled=30.5);
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled='string');
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor='string');
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor=true);
+
+-- Specifing name only should fail as if there was =ture after it
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor);
+
+-- Simple ALTER TABLE
+
+ALTER TABLE reloptions_test SET (fillfactor=31, autovacuum_analyze_scale_factor = 0.3);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+
+-- Check that we cat set boolean option to true just by mentioning it
+
+ALTER TABLE reloptions_test SET (autovacuum_enabled, fillfactor=32);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+
+-- Check that RESET works well
+
+ALTER TABLE reloptions_test RESET (fillfactor);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+
+-- Check that RESETting all values make NULL reloptions record in pg_class
+ALTER TABLE reloptions_test RESET (autovacuum_enabled, autovacuum_analyze_scale_factor);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass AND reloptions IS NULL;
+
+-- Check RESET fails on att=value
+
+ALTER TABLE reloptions_test RESET (fillfactor=12);
+
+-- Check oids options is ignored
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test(i INT) WITH (fillfactor=20, oids=true);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+
+-- Now testing toast.* options
+DROP TABLE reloptions_test;
+
+CREATE TABLE reloptions_test (s VARCHAR) WITH (toast.autovacuum_vacuum_cost_delay = 23 );
+
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+
+ALTER TABLE reloptions_test SET (toast.autovacuum_vacuum_cost_delay = 24);
+
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+
+ALTER TABLE reloptions_test RESET (toast.autovacuum_vacuum_cost_delay);
+
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+
+-- Fail on unexisting options in toast namespace
+CREATE TABLE reloptions_test2 (i int) WITH (toast.not_existing_option = 42 );
+
+-- Fail on setting reloption to a table that does not have a TOAST relation
+CREATE TABLE reloptions_test2 (i int) WITH (toast.autovacuum_vacuum_cost_delay = 23 );
+DROP TABLE reloptions_test;
+
+CREATE TABLE reloptions_test(i INT);
+ALTER TABLE reloptions_test SET (toast.autovacuum_vacuum_cost_delay = 23);
+ALTER TABLE reloptions_test RESET (toast.autovacuum_vacuum_cost_delay);
+
+-- autovacuum_analyze_scale_factor and autovacuum_analyze_threshold should be
+-- accepted by heap but rejected by toast (special case)
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test (s VARCHAR) WITH (autovacuum_analyze_scale_factor=1, autovacuum_analyze_threshold=1);
+
+CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analyze_scale_factor=1);
+CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analyze_threshold=1);
+
+-- And now mixed toast + heap
+DROP TABLE reloptions_test;
+
+CREATE TABLE reloptions_test (s VARCHAR) WITH (toast.autovacuum_vacuum_cost_delay = 23, autovacuum_vacuum_cost_delay = 24, fillfactor = 40);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+
+-- Same FOR CREATE and ALTER INDEX for btree indexes
+
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (fillfactor=30);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx'::regclass;
+
+-- Fail when option and namespase do not exist
+
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_existing_option=2);
+
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_existing_option.fillfactor=2);
+
+-- Check ranges
+
+CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfactor=1);
+
+CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfactor=130);
+
+-- Check alter
+
+ALTER INDEX reloptions_test_idx SET (fillfactor=40);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx'::regclass;
+
+-- Check alter on empty relop list
+
+CREATE INDEX reloptions_test_idx3 ON reloptions_test (s);
+
+ALTER INDEX reloptions_test_idx3 SET (fillfactor=40);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx3'::regclass;
+
+
diff --git a/src/test/regress/sql/spgist.sql b/src/test/regress/sql/spgist.sql
index 5896b50..8d26e49 100644
--- a/src/test/regress/sql/spgist.sql
+++ b/src/test/regress/sql/spgist.sql
@@ -5,7 +5,7 @@
-- testing SP-GiST code itself.
create table spgist_point_tbl(id int4, p point);
-create index spgist_point_idx on spgist_point_tbl using spgist(p);
+create index spgist_point_idx on spgist_point_tbl using spgist(p) with (fillfactor = 75);
-- Test vacuum-root operation. It gets invoked when the root is also a leaf,
-- i.e. the index is very small.
@@ -48,3 +48,10 @@ select g, 'baaaaaaaaaaaaaar' || g from generate_series(1, 1000) g;
-- tuple to be moved to another page.
insert into spgist_text_tbl (id, t)
select -g, 'f' || repeat('o', 100-g) || 'surprise' from generate_series(1, 100) g;
+
+-- Check reloptions min, max values
+create index spgist_point_idx2 on spgist_point_tbl using spgist(p) with (fillfactor = 9);
+create index spgist_point_idx2 on spgist_point_tbl using spgist(p) with (fillfactor = 101);
+
+-- ALTER reloption should work
+alter index spgist_point_idx set (fillfactor = 76);
I've rebased the patch again.
Alvaro, I am waiting for you! ;-)
I've rebased the patch, so it can be applied to current master. See
attachment.Alvaro in private letter told that he will review the patch some time later.
So I am waiting.Also added this patch to commitfest:
https://commitfest.postgresql.org/13/992/(Please see Attribution notice at the bottom of the letter)
Hi! Some time ago I've send a proposal, about removing all am-related code
from src/backend/access/common/reloptions.c and move it into somewhere
inside am code. It was in
/messages/by-id/4636505.Bu9AKW1Kzc@nataraj-amd64
thread.Now I have a patch that is ready
Before explaining what have been done, I need to define some concepts, so
it would be easy to speak about them in the further explanation.Reloptions values in postgres actually exists in four representations:
DefList*, TextArray[], Bytea and so called Values.- DefList representation is a list of DefElem, it is the representation in
which reloptions comes from syntax analyzer, when relation is created or
altered, and also this representation that is needed for pg_dump to dump a
create statement that creates this relation.- TextArray[] representation is a way, how reloptions are stored in
pg_catalog. It is TEXT[] attribute in the pg_catalog table and from the
poinf o view of postgres code it is Datum array of TEXT Datums, when read
into the memory- Bytea (or I will also call it Binary) representation, is a
representation with which relation code really works. It has C-structure
inside in which fixed-length options are sored, some space is reserved
for storing varlen values after the structure, and certainly it has BYTEA
header in front of all of it. It is cached, it is fast to access. It is a
good stuff.- Values (or in some cases RawValues) -- internal representation of option
parser. Technically it is List of option_value structures (old name for it
is relopt_value). This representation is used for parsing and validating
options. Actually all the convertations between representations listed
above are dove through Values representation. Values are called RawValues
when Values List were already created, but it was not parsed yet, and all
option values are in raw state: represented as a text.DefList and TextArray representations are converted in both ways(always
through Values representation): we need DefList -> TextArray to create or
change entry in the pg_catalog, and need TextArray -> DefList for pg_dumpBytea representation is converted from TextArray representation (also
through Values representation). When relation code, tries to get cached
Bytea representation for certain relation, and none is actually cached,
then cache code calls extractRelOptions function that fetches entry from
pg_catalog for that relation, gets reloptions TextArray[], converts it
into Bytea and cache it.Each reloption has it's own definition. This definitions tells how this
option should be parsed, validated and converted. Before my patch this
information were stored in relopt_gen and relopt_parse_elt structures. In
my patch all this information were moved into option_definition_basic
structure (that is actually old relopt_gen + relopt_parse_elt)The set of options definition I would call a options catalog. Before my
patch there was one global options catalog (actually it was divided into
parts by data types and has another part for dynamically added options,
but
nevertheless, it was global). After my patch there would be a separate
options catalog for each relation type. For AM-relation this catalog would
be available via AM-handler. for other relation types it would be still
kept in reloption.cNow I am ready to tell what actually have been done in this patch:
1. I moved options definitions from global lists from reloptions.c
[type name]RelOpts to lists that are kept inside AM code. One list per AM.
heap options, toast options and options for all other relation types that
are not accessible through AccessMethod, all were kept inside
reloptions.c, but now it is one list for each relation type, not a global
one2. Each AccessMethod had amoptions function that was responsible for
converting reloptions from TextArray[] representation into Bytea
representaions. This function used functions from reloptions.c and also
had
an array of relopt_parse_elt that defines how parsed reloption data should
be packed inside Bytea chunk. I've moved data from relopt_parse_elt array
into option definitions that are stored in the catalog (since catalog are
now defined inside the AccessMethod we have access to a structure of Bytea
representation at the place where we define option catalog)3. Speaking of amoptions function, I've completely removed it, since we do
not need it. For developers of Access Methods who still want to do some
custom action with just parsed data, I've added postprocess_fun to option
catalog. If this function is defined, it is called by convertation
function
right after Bytea representation were created. In postprocess_fun
developer
can do some custom validation, or change just parsed data. This feature is
used in bloom index.4. Instead of amoptions function I've added amrelopt_catalog function to
the Access Method. This function returns option definition catalog for an
Access Method. The catalog is used for processing options.5. As before, relation cache calls extractRelOptions when it needs options
that were not previously cached.
Before my patch, it created Bytea representations for non-AM relations,
and
called amoptions AM function to get Bytea representation for AM relation.
In by patch, it now gets option catalog, (using local functions for non-AM
relations, and using amrelopt_catalog function for AM-relations), and
then
use this catalog to convert options from TextArray into Bytea
representation.6. I've added some more featues:
- Now you can set OPTION_DEFINITION_FLAG_FORBID_ALTER flag in definition
of
the option, and then postgres will refuse to change option value using
ALTER command. In many indexes, some options are used only for index
creation. After this it's value is saved in MetaPage, and used from there.
Nevertheless user is allowed to change option value, though it affects
nothing, one need to recreate index to really change such value. Now using
this flag we can prevent user from getting an illusion that he
successfully
changed option value.- After applying my patch if you try to set toast. option for table that
does not have toast, you will get an error. Before postgres will accept
toast option for a table without a toast, but this value will be lost.
This
is bad behavior, so now postgres will throw an error in this case.- I've noticed that all string relation options in postgres are
technically
enum options. So I've added enum option type. This will save some bytes of
the memory and some tacts of the CPU. I also left string option type
(through it is not used now). A. Korotkov said that it might be needed
later for storing patterns, xml/json paths or something like that.- Added some more tests that will trigger more options code. Though I am
dreaming about more advanced test system, that will allow to check that
certain value is received somewhere deep in the code.7. And now the most controversial change. I've got rid of StdRdOptions
structure, in which options for heap, toast, nbtree, hash and spgist were
stored in Bytea representation. In indexes only fillfactor value were
actually used from the whole big structure. So I've created a separate
structure for each relation type. HeapOptions and ToastOptions are very
similar to each other. So common part of creating these catalogs were
moved
to
add_autovacuum_options function that are called while creating each
catalog.Now to the controversial part: in src/include/utils/rel.h had a lot of
Relation* macroses that used StdRdOptions structure. I've changed them
into
View* Heap* and Toast* analogues, and changed the code to use these
macroses instead of Relation* one. But this part of code I least sure of.
I'd like to ask experienced developers to double check it.8. I've moved all options-abstract code into options.c and options.h file,
and left in reloptions.c and reloptions.h only the code that concerns
relation options. Actually the main idea of this patch was to get this
abstract code in order to use it for custom attribute options later. All
the rest just a side effects :-)So, before adding this to commitfest I want somebody to give a general
look
at it, if the code is OK.Alvaro Herrera, you once said that you can review the patch...
The patch is available in the attachment. It can be applied to current
masterYou can also see latest version on my github
https://github.com/dhyannataraj/postgres/tree/reloption_fix
at the reloption_fix branch.ATTRIBUTION NOTICE: I wrote this patch, when I was an employee in
Postgres
Professional company. So this patch should be attributed as patch from
Postgres Pro (or something like that). For some reasons I did not manage
to
commit this patch while I left that job. But I got a permission to commit
it even if I left the company.
So my contribution to this patch as a independent developer was final code
cleanup, and writing some more comments. All the rest was a work of
Postgres Pro employee Nikolay Shaplov.
--
Nikolay Shaplov, independent Perl & C/C++ developer. Available for hire.
Attachments:
reloptions6b.difftext/x-patch; charset=UTF-8; name=reloptions6b.diffDownload
diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
index 0cfe49a..21e59df 100644
--- a/contrib/bloom/bloom.h
+++ b/contrib/bloom/bloom.h
@@ -204,7 +204,7 @@ extern IndexBulkDeleteResult *blbulkdelete(IndexVacuumInfo *info,
void *callback_state);
extern IndexBulkDeleteResult *blvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
-extern bytea *bloptions(Datum reloptions, bool validate);
+extern bytea *bloptions(Datum reloptions, bool validate, bool for_alter);
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..93014af 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,27 @@ 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 +451,38 @@ BloomInitMetapage(Relation index)
UnlockReleaseBuffer(metaBuffer);
}
-/*
- * Parse reloptions for bloom index, producing a BloomOptions struct.
- */
-bytea *
-bloptions(Datum reloptions, bool validate)
+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);
+ bl_relopt_catalog->struct_size = sizeof(BloomOptions);
+ 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 cbc50f7..7e3bbc0 100644
--- a/contrib/bloom/expected/bloom.out
+++ b/contrib/bloom/expected/bloom.out
@@ -210,3 +210,37 @@ ORDER BY 1;
text_ops | t
(2 rows)
+-- reloptions test
+DROP INDEX bloomidx;
+CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (length=7, col1 = 4);
+SELECT reloptions FROM pg_class WHERE oid = 'bloomidx'::regclass;
+ reloptions
+-------------------
+ {length=7,col1=4}
+(1 row)
+
+-- check for min and max values
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=0);
+ERROR: value 0 out of bounds for option "length"
+DETAIL: Valid values are between "1" and "4096".
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=4097);
+ERROR: value 4097 out of bounds for option "length"
+DETAIL: Valid values are between "1" and "4096".
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=0);
+ERROR: value 0 out of bounds for option "col1"
+DETAIL: Valid values are between "1" and "4095".
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=4096);
+ERROR: value 4096 out of bounds for option "col1"
+DETAIL: Valid values are between "1" and "4095".
+-- check post_validate for colN<lengh
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=10,col1=11);
+ERROR: col0 should not be grater than length
+-- check that ALTERing of reloptions is forbidden
+ALTER INDEX bloomidx SET (length=4);
+ERROR: changing parameter "length" is not allowed
+ALTER INDEX bloomidx SET (col1=4);
+ERROR: changing parameter "col1" is not allowed
+ALTER INDEX bloomidx RESET (length);
+ERROR: changing parameter "length" is not allowed
+ALTER INDEX bloomidx RESET (col1);
+ERROR: changing parameter "col1" is not allowed
diff --git a/contrib/bloom/sql/bloom.sql b/contrib/bloom/sql/bloom.sql
index 2227460..dbac21c 100644
--- a/contrib/bloom/sql/bloom.sql
+++ b/contrib/bloom/sql/bloom.sql
@@ -81,3 +81,23 @@ SELECT opcname, amvalidate(opc.oid)
FROM pg_opclass opc JOIN pg_am am ON am.oid = opcmethod
WHERE amname = 'bloom'
ORDER BY 1;
+
+-- reloptions test
+
+DROP INDEX bloomidx;
+CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (length=7, col1 = 4);
+SELECT reloptions FROM pg_class WHERE oid = 'bloomidx'::regclass;
+-- check for min and max values
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=0);
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=4097);
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=0);
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=4096);
+-- check post_validate for colN<lengh
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=10,col1=11);
+
+-- check that ALTERing of reloptions is forbidden
+
+ALTER INDEX bloomidx SET (length=4);
+ALTER INDEX bloomidx SET (col1=4);
+ALTER INDEX bloomidx RESET (length);
+ALTER INDEX bloomidx RESET (col1);
diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
index eba6f27..3d1c73f 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -1948,7 +1948,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 735b794..b07399f 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 e24db56..be15189 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -65,7 +65,7 @@ PG_FUNCTION_INFO_V1(postgres_fdw_validator);
Datum
postgres_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid catalog = PG_GETARG_OID(1);
ListCell *cell;
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index b22563b..f69bcbd 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"
@@ -70,6 +70,7 @@ static void union_tuples(BrinDesc *bdesc, BrinMemTuple *a,
BrinTuple *b);
static void brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy);
+void *bringetreloptcatalog (void);
/*
* BRIN handler function: return IndexAmRoutine with access method parameters
@@ -103,7 +104,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;
@@ -113,6 +113,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;
@@ -756,36 +757,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)}
- };
-
- 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.
*/
@@ -1235,3 +1206,25 @@ brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy)
if (vacuum_fsm)
FreeSpaceMapVacuum(idxrel);
}
+
+static options_catalog * brin_relopt_catalog = NULL;
+
+void *
+bringetreloptcatalog (void)
+{
+ if (! brin_relopt_catalog)
+ {
+ brin_relopt_catalog = allocateOptionsCatalog(NULL);
+ brin_relopt_catalog->struct_size = sizeof(BrinOptions);
+
+ 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);
+ }
+ return brin_relopt_catalog;
+}
diff --git a/src/backend/access/common/Makefile b/src/backend/access/common/Makefile
index fb27944..c9fb063 100644
--- a/src/backend/access/common/Makefile
+++ b/src/backend/access/common/Makefile
@@ -12,7 +12,7 @@ subdir = src/backend/access/common
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
-OBJS = bufmask.o heaptuple.o indextuple.o printsimple.o printtup.o \
+OBJS = bufmask.o heaptuple.o indextuple.o options.o printsimple.o printtup.o \
reloptions.o scankey.o tupconvert.o tupdesc.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/common/options.c b/src/backend/access/common/options.c
new file mode 100644
index 0000000..3f2c183
--- /dev/null
+++ b/src/backend/access/common/options.c
@@ -0,0 +1,1389 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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);
+void *optionsAllocateBytea(options_catalog *catalog, List *options);
+
+
+/*
+ * 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.
+ */
+
+options_catalog *
+allocateOptionsCatalog(char *namespace)
+{
+ 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;
+
+ catalog->definitions = NULL;
+ catalog->num = 0;
+ catalog->num_allocated = 0;
+ catalog->struct_size = -1; /* Will Assert if not properly changed */
+ 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 + 1 >= catalog->num_allocated)
+ {
+ MemoryContext oldcxt;
+
+ 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->definitions[catalog->num + 1] = NULL;
+ 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
+ */
+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.
+ */
+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++)
+ {
+ text *option_src = DatumGetTextP(options[i]);
+ option_value * option_dst;
+ char *text_str = VARDATA(option_src);
+ int text_len = VARSIZE(option_src) - 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
+ */
+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.
+ */
+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.
+ */
+void *
+optionsAllocateBytea(options_catalog *catalog, List *options)
+{
+ Size size;
+ int i;
+ ListCell *cell;
+ int length;
+ void *res;
+
+ Assert(catalog->struct_size > 0); /* Make sure it was initialized */
+ 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.
+ *
+ */
+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 c506491..fb1386f 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,920 +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[] =
-{
- {
- {
- "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++)
- {
- text *oldoption = DatumGetTextP(oldoptions[i]);
- char *text_str = VARDATA(oldoption);
- int text_len = VARSIZE(oldoption) - 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.
@@ -946,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,
@@ -966,16 +66,19 @@ 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;
@@ -986,529 +89,293 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
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.
+ * 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
+ */
+
+/*
+ * 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.
*
- * If there are no options of the given kind, numrelopts is set to 0 and NULL
- * is returned.
+ * 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.
*
- * 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.
+ * 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
*/
-relopt_value *
-parseRelOptions(Datum options, bool validate, relopt_kind kind,
- int *numrelopts)
-{
- relopt_value *reloptions;
- 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)
- {
- *numrelopts = 0;
- return NULL;
- }
-
- 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++)
- {
- text *optiontext = DatumGetTextP(optiondatums[i]);
- char *text_str = VARDATA(optiontext);
- int text_len = VARSIZE(optiontext) - 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;
+void
+add_autovacuum_options(options_catalog *catalog, int base_offset,
+ bool for_toast)
+{
+ 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);
}
/*
- * Subroutine for parseRelOptions, to parse and validate a single option's
- * value
+ * get_heap_relopt_catalog
+ * Returns an options catalog for heap relation.
*/
-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;
+static options_catalog * heap_relopt_catalog = NULL;
- 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)
+options_catalog *
+get_heap_relopt_catalog(void)
+{
+ if (! heap_relopt_catalog)
{
- 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;
+ heap_relopt_catalog = allocateOptionsCatalog(NULL);
+ heap_relopt_catalog->struct_size = sizeof(HeapOptions);
- 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;
+ 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);
- 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;
+ add_autovacuum_options(heap_relopt_catalog,
+ offsetof(HeapOptions, autovacuum), false);
- 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;
+ 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);
}
-
- if (parsed)
- option->isset = true;
- if (!nofree)
- pfree(value);
+ return heap_relopt_catalog;
}
/*
- * Given the result from parseRelOptions, allocate a struct that's of the
- * specified base size plus any extra space that's needed for string variables.
- *
- * "base" should be sizeof(struct) of the reloptions struct (StdRdOptions or
- * equivalent).
+ * get_toast_relopt_catalog
+ * Returns an options catalog for toast relation.
*/
-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;
+static options_catalog * toast_relopt_catalog = NULL;
- return palloc0(size);
-}
-
-/*
- * Given the result of parseRelOptions and a parsing table, fill in the
- * struct (previously allocated with allocateReloptStruct) with the parsed
- * values.
- *
- * rdopts is the pointer to the allocated struct to be filled.
- * basesize is the sizeof(struct) that was passed to allocateReloptStruct.
- * options, of length numoptions, is parseRelOptions' output.
- * elems, of length numelems, is the table describing the allowed options.
- * When validate is true, it is expected that all options appear in elems.
- */
-void
-fillRelOptions(void *rdopts, Size basesize,
- relopt_value *options, int numoptions,
- bool validate,
- const relopt_parse_elt *elems, int numelems)
+options_catalog *
+get_toast_relopt_catalog(void)
{
- int i;
- int offset = basesize;
-
- for (i = 0; i < numoptions; i++)
+ if (! toast_relopt_catalog)
{
- 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;
+ toast_relopt_catalog = allocateOptionsCatalog("toast");
+ toast_relopt_catalog->struct_size = sizeof(ToastOptions);
- 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);
+ add_autovacuum_options(toast_relopt_catalog,
+ offsetof(ToastOptions, autovacuum), true);
}
- SET_VARSIZE(rdopts, offset);
+ return toast_relopt_catalog;
}
-
/*
- * Option parser for anything that uses StdRdOptions.
+ * get_view_relopt_catalog
+ * Returns an options catalog for view 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)}
- };
-
- 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));
+static options_catalog * view_relopt_catalog = NULL;
- pfree(options);
-
- return (bytea *) rdopts;
-}
-
-/*
- * Option parser for views
- */
-bytea *
-view_reloptions(Datum reloptions, bool validate)
+options_catalog *
+get_view_relopt_catalog(void)
{
- 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);
+ static const char *enum_names[] = VIEW_OPTION_CHECK_OPTION_VALUE_NAMES;
- /* if none set, we're done */
- if (numoptions == 0)
- return NULL;
-
- vopts = allocateReloptStruct(sizeof(ViewOptions), options, numoptions);
-
- fillRelOptions((void *) vopts, sizeof(ViewOptions), options, numoptions,
- validate, tab, lengthof(tab));
-
- pfree(options);
-
- return (bytea *) vopts;
-}
+ if (! view_relopt_catalog)
+ {
+ view_relopt_catalog = allocateOptionsCatalog(NULL);
+ view_relopt_catalog->struct_size = sizeof(ViewOptions);
-/*
- * Parse options for heaps, views and toast tables.
- */
-bytea *
-heap_reloptions(char relkind, Datum reloptions, bool validate)
-{
- StdRdOptions *rdopts;
+ optionsCatalogAddItemBool(view_relopt_catalog, "security_barrier",
+ "View acts as a row security barrier",
+ AccessExclusiveLock,
+ 0, offsetof(ViewOptions, security_barrier), false);
- switch (relkind)
- {
- case RELKIND_TOASTVALUE:
- rdopts = (StdRdOptions *)
- default_reloptions(reloptions, validate, RELOPT_KIND_TOAST);
- if (rdopts != NULL)
- {
- /* adjust default-only parameters for TOAST relations */
- rdopts->fillfactor = 100;
- rdopts->autovacuum.analyze_threshold = -1;
- rdopts->autovacuum.analyze_scale_factor = -1;
- }
- return (bytea *) rdopts;
- case RELKIND_RELATION:
- case RELKIND_MATVIEW:
- case RELKIND_PARTITIONED_TABLE:
- return default_reloptions(reloptions, validate, RELOPT_KIND_HEAP);
- default:
- /* other relkinds are not supported */
- return NULL;
+ 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;
}
-
/*
- * Parse options for indexes.
- *
- * amoptions index AM's option parser function
- * reloptions options as text[] datum
- * validate error flag
+ * get_attribute_options_catalog
+ * Returns an options catalog for heap attributes
*/
-bytea *
-index_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
-{
- Assert(amoptions != NULL);
-
- /* Assume function is strict */
- if (!PointerIsValid(DatumGetPointer(reloptions)))
- return NULL;
-
- return amoptions(reloptions, validate);
-}
+static options_catalog * attribute_options_catalog = NULL;
-/*
- * Option parser for attribute reloptions
- */
-bytea *
-attribute_reloptions(Datum reloptions, bool validate)
+options_catalog *
+get_attribute_options_catalog(void)
{
- 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);
-
- fillRelOptions((void *) aopts, sizeof(AttributeOpts), options, numoptions,
- validate, tab, lengthof(tab));
+ if (! attribute_options_catalog)
+ {
+ attribute_options_catalog = allocateOptionsCatalog(NULL);
+ attribute_options_catalog->struct_size = sizeof(AttributeOpts);
- 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;
+static options_catalog * tablespace_options_catalog = NULL;
- tsopts = allocateReloptStruct(sizeof(TableSpaceOpts), options, numoptions);
+options_catalog *
+get_tablespace_options_catalog(void)
+{
+ if (! tablespace_options_catalog)
+ {
+ tablespace_options_catalog = allocateOptionsCatalog(NULL);
+ tablespace_options_catalog->struct_size = sizeof(TableSpaceOpts);
- fillRelOptions((void *) tsopts, sizeof(TableSpaceOpts), options, numoptions,
- validate, tab, lengthof(tab));
+ 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);
- pfree(options);
+ 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);
- return (bytea *) tsopts;
+ 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;
}
/*
@@ -1518,33 +385,51 @@ 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;
+ 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;
+ 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;
+ DefElem *def = (DefElem *) lfirst(cell);
- for (i = 0; relOpts[i]; i++)
+ int 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 d03d59d..50ed21c 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -16,7 +16,7 @@
#include "access/gin_private.h"
#include "access/ginxlog.h"
-#include "access/reloptions.h"
+#include "access/options.h"
#include "access/xloginsert.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_type.h"
@@ -26,7 +26,8 @@
#include "utils/builtins.h"
#include "utils/index_selfuncs.h"
#include "utils/typcache.h"
-
+#include "utils/memutils.h"
+#include "utils/guc.h"
/*
* GIN handler function: return IndexAmRoutine with access method parameters
@@ -60,7 +61,6 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amvacuumcleanup = ginvacuumcleanup;
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = gincostestimate;
- amroutine->amoptions = ginoptions;
amroutine->amproperty = NULL;
amroutine->amvalidate = ginvalidate;
amroutine->ambeginscan = ginbeginscan;
@@ -70,6 +70,7 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amendscan = ginendscan;
amroutine->ammarkpos = NULL;
amroutine->amrestrpos = NULL;
+ amroutine->amrelopt_catalog = gingetreloptcatalog;
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
@@ -593,35 +594,6 @@ ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
return entries;
}
-bytea *
-ginoptions(Datum reloptions, bool validate)
-{
- relopt_value *options;
- GinOptions *rdopts;
- int numoptions;
- static const relopt_parse_elt tab[] = {
- {"fastupdate", RELOPT_TYPE_BOOL, offsetof(GinOptions, useFastUpdate)},
- {"gin_pending_list_limit", RELOPT_TYPE_INT, offsetof(GinOptions,
- pendingListCleanupSize)}
- };
-
- options = parseRelOptions(reloptions, validate, RELOPT_KIND_GIN,
- &numoptions);
-
- /* if none set, we're done */
- if (numoptions == 0)
- return NULL;
-
- rdopts = allocateReloptStruct(sizeof(GinOptions), options, numoptions);
-
- fillRelOptions((void *) rdopts, sizeof(GinOptions), options, numoptions,
- validate, tab, lengthof(tab));
-
- pfree(options);
-
- return (bytea *) rdopts;
-}
-
/*
* Fetch index's statistical data into *stats
*
@@ -698,3 +670,32 @@ ginUpdateStats(Relation index, const GinStatsData *stats)
END_CRIT_SECTION();
}
+
+
+static options_catalog * gin_relopt_catalog = NULL;
+
+void *
+gingetreloptcatalog (void)
+{
+ if (! gin_relopt_catalog)
+ {
+ gin_relopt_catalog = allocateOptionsCatalog(NULL);
+ gin_relopt_catalog->struct_size = sizeof(GinOptions);
+
+ 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 6593771..0a2cd59 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;
@@ -1583,3 +1583,4 @@ gistvacuumpage(Relation rel, Page page, Buffer buffer)
* the page.
*/
}
+
diff --git a/src/backend/access/gist/gistbuild.c b/src/backend/access/gist/gistbuild.c
index f1f08bb..936d4d0 100644
--- a/src/backend/access/gist/gistbuild.c
+++ b/src/backend/access/gist/gistbuild.c
@@ -126,11 +126,9 @@ 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 +232,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 cbdaec9..efb5133 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -18,14 +18,13 @@
#include "access/gist_private.h"
#include "access/htup_details.h"
-#include "access/reloptions.h"
+#include "access/options.h"
#include "catalog/pg_opclass.h"
#include "storage/indexfsm.h"
#include "storage/lmgr.h"
#include "utils/builtins.h"
#include "utils/syscache.h"
-
/*
* Write itup vector to page, has no control of free space.
*/
@@ -812,34 +811,6 @@ gistNewBuffer(Relation r)
return buffer;
}
-bytea *
-gistoptions(Datum reloptions, bool validate)
-{
- relopt_value *options;
- GiSTOptions *rdopts;
- int numoptions;
- static const relopt_parse_elt tab[] = {
- {"fillfactor", RELOPT_TYPE_INT, offsetof(GiSTOptions, fillfactor)},
- {"buffering", RELOPT_TYPE_STRING, offsetof(GiSTOptions, bufferingModeOffset)}
- };
-
- options = parseRelOptions(reloptions, validate, RELOPT_KIND_GIST,
- &numoptions);
-
- /* if none set, we're done */
- if (numoptions == 0)
- return NULL;
-
- rdopts = allocateReloptStruct(sizeof(GiSTOptions), options, numoptions);
-
- fillRelOptions((void *) rdopts, sizeof(GiSTOptions), options, numoptions,
- validate, tab, lengthof(tab));
-
- pfree(options);
-
- return (bytea *) rdopts;
-}
-
/*
* gistproperty() -- Check boolean properties of indexes.
*
@@ -965,3 +936,32 @@ 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);
+ gist_relopt_catalog->struct_size = sizeof(GiSTOptions);
+
+ 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 1f8a7f6..3eb60de 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -77,7 +77,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;
@@ -87,6 +86,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 c73929c..e7c9239 100644
--- a/src/backend/access/hash/hashpage.c
+++ b/src/backend/access/hash/hashpage.c
@@ -358,7 +358,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 c705531..478cb44 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"
@@ -219,12 +219,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
*/
@@ -446,3 +440,26 @@ _hash_get_newbucket_from_oldbucket(Relation rel, Bucket old_bucket,
return new_bucket;
}
+
+static options_catalog * hash_relopt_catalog = NULL;
+
+void *
+hashgetreloptcatalog(void)
+{
+ if (! hash_relopt_catalog)
+ {
+ hash_relopt_catalog = allocateOptionsCatalog(NULL);
+ hash_relopt_catalog->struct_size = sizeof(HashRelOptions);
+
+ 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 bffc971..f1a339d 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -2673,7 +2673,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 6529fe3..78ecc46 100644
--- a/src/backend/access/heap/hio.c
+++ b/src/backend/access/heap/hio.c
@@ -323,8 +323,11 @@ RelationGetBufferForTuple(Relation relation, Size len,
len, MaxHeapTupleSize)));
/* Compute desired extra freespace due to fillfactor option */
- saveFreeSpace = RelationGetTargetPageFreeSpace(relation,
- HEAP_DEFAULT_FILLFACTOR);
+ if (IsToastRelation(relation))
+ saveFreeSpace = ToastGetTargetPageFreeSpace();
+ else
+ saveFreeSpace = HeapGetTargetPageFreeSpace(relation,
+ HEAP_DEFAULT_FILLFACTOR);
if (otherBuffer != InvalidBuffer)
otherBlock = BufferGetBlockNumber(otherBuffer);
diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c
index d69a266..fef5e40 100644
--- a/src/backend/access/heap/pruneheap.c
+++ b/src/backend/access/heap/pruneheap.c
@@ -131,8 +131,12 @@ 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,
- HEAP_DEFAULT_FILLFACTOR);
+
+ if (IsToastRelation(relation))
+ minfree = ToastGetTargetPageFreeSpace();
+ else
+ minfree = HeapGetTargetPageFreeSpace(relation,
+ HEAP_DEFAULT_FILLFACTOR);
minfree = Max(minfree, BLCKSZ / 10);
if (PageIsFull(page) || PageGetHeapFreeSpace(page) < minfree)
diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c
index c7b283c..ee859e7 100644
--- a/src/backend/access/heap/rewriteheap.c
+++ b/src/backend/access/heap/rewriteheap.c
@@ -665,7 +665,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 6dca810..6b12304 100644
--- a/src/backend/access/nbtree/nbtinsert.c
+++ b/src/backend/access/nbtree/nbtinsert.c
@@ -1433,8 +1433,7 @@ _bt_findsplitloc(Relation rel,
state.is_rightmost = P_RIGHTMOST(opaque);
state.have_split = false;
if (state.is_leaf)
- state.fillfactor = RelationGetFillFactor(rel,
- BTREE_DEFAULT_FILLFACTOR);
+ state.fillfactor = BTGetFillFactor(rel);
else
state.fillfactor = BTREE_NONLEAF_FILLFACTOR;
state.newitemonleft = false; /* these just to keep compiler quiet */
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index 775f2ff..9d1b8ac 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -21,6 +21,7 @@
#include "access/nbtree.h"
#include "access/relscan.h"
#include "access/xlog.h"
+#include "access/reloptions.h"
#include "catalog/index.h"
#include "commands/vacuum.h"
#include "pgstat.h"
@@ -34,7 +35,6 @@
#include "utils/index_selfuncs.h"
#include "utils/memutils.h"
-
/* Working state for btbuild and its callback */
typedef struct
{
@@ -116,8 +116,7 @@ static void btvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
BTCycleId cycleid);
static void btvacuumpage(BTVacState *vstate, BlockNumber blkno,
BlockNumber orig_blkno);
-
-
+void * btgetreloptcatalog (void);
/*
* Btree handler function: return IndexAmRoutine with access method parameters
* and callbacks.
@@ -150,7 +149,6 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amvacuumcleanup = btvacuumcleanup;
amroutine->amcanreturn = btcanreturn;
amroutine->amcostestimate = btcostestimate;
- amroutine->amoptions = btoptions;
amroutine->amproperty = btproperty;
amroutine->amvalidate = btvalidate;
amroutine->ambeginscan = btbeginscan;
@@ -160,6 +158,7 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amendscan = btendscan;
amroutine->ammarkpos = btmarkpos;
amroutine->amrestrpos = btrestrpos;
+ amroutine->amrelopt_catalog = btgetreloptcatalog;
amroutine->amestimateparallelscan = btestimateparallelscan;
amroutine->aminitparallelscan = btinitparallelscan;
amroutine->amparallelrescan = btparallelrescan;
@@ -1391,3 +1390,26 @@ btcanreturn(Relation index, int attno)
{
return true;
}
+
+
+static options_catalog * bt_relopt_catalog = NULL;
+
+void *
+btgetreloptcatalog (void)
+{
+ if (! bt_relopt_catalog)
+ {
+ bt_relopt_catalog = allocateOptionsCatalog(NULL);
+ bt_relopt_catalog->struct_size = sizeof(BTRelOptions);
+
+ 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 3d041c4..692c751 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 5b259a3..3efb05c 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 "access/options.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 e57ac49..b24178c 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,26 @@ 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);
+ spgist_relopt_catalog->struct_size = sizeof(SpGistRelOptions);
+
+ 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 0e42316..838b357 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 646a884..5020ee1 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 d5d40e6..7aa4576 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 72bb06c..071bf64 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -325,7 +325,7 @@ DefineIndex(Oid relationId,
Form_pg_am accessMethodForm;
IndexAmRoutine *amRoutine;
bool amcanorder;
- amoptions_function amoptions;
+ amrelopt_catalog_function amoption_catalog;
Datum reloptions;
int16 *coloptions;
IndexInfo *indexInfo;
@@ -528,7 +528,7 @@ DefineIndex(Oid relationId,
accessMethodName)));
amcanorder = amRoutine->amcanorder;
- amoptions = amRoutine->amoptions;
+ amoption_catalog = amRoutine->amrelopt_catalog;
pfree(amRoutine);
ReleaseSysCache(tuple);
@@ -542,10 +542,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 1ddb72d..f6de506 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -499,7 +499,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
Datum reloptions;
ListCell *listptr;
AttrNumber attnum;
- static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
Oid ofTypeId;
ObjectAddress address;
@@ -585,13 +584,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)
{
@@ -3073,7 +3082,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);
@@ -3115,13 +3125,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)
{
@@ -3330,7 +3342,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:
@@ -4087,7 +4102,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",
@@ -5930,11 +5945,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));
@@ -9929,7 +9944,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 */
@@ -9958,25 +9973,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,
@@ -9990,7 +10032,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;
@@ -10050,6 +10092,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);
@@ -10073,12 +10117,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));
@@ -10106,6 +10153,17 @@ 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 f9c2620..9945318 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 fdb4f71..a32d376 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 463f806..a6b9570 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -135,7 +135,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 47ca685..d4e2281 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -3085,7 +3085,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 673276a..61e201e 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1391,7 +1391,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 fa8de13..61bf653 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -2509,6 +2509,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 ||
@@ -2518,8 +2519,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 354e5d0..748ba3a 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -1488,7 +1488,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 */
@@ -2989,7 +2989,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
@@ -3025,8 +3025,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 20b5273..7b6f49e 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -996,6 +996,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,
@@ -1016,18 +1018,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))
{
@@ -1093,8 +1101,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 f7f85b5..b5f3dce 100644
--- a/src/backend/utils/cache/attoptcache.c
+++ b/src/backend/utils/cache/attoptcache.c
@@ -150,7 +150,10 @@ get_attribute_options(Oid attrelid, int attnum)
opts = NULL;
else
{
- bytea *bytea_opts = attribute_reloptions(datum, false);
+ bytea *bytea_opts;
+
+ bytea_opts = optionsTextArrayToBytea(
+ get_attribute_options_catalog(), datum);
opts = MemoryContextAlloc(CacheMemoryContext,
VARSIZE(bytea_opts));
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 9001e20..19d7b44 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -459,7 +459,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..faafcaf 100644
--- a/src/backend/utils/cache/spccache.c
+++ b/src/backend/utils/cache/spccache.c
@@ -149,7 +149,9 @@ 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 +196,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 f919cf8..9f3db93 100644
--- a/src/include/access/amapi.h
+++ b/src/include/access/amapi.h
@@ -98,10 +98,6 @@ typedef void (*amcostestimate_function) (struct PlannerInfo *root,
double *indexCorrelation,
double *indexPages);
-/* parse index reloptions */
-typedef bytea *(*amoptions_function) (Datum reloptions,
- bool validate);
-
/* report AM, index, or index column property */
typedef bool (*amproperty_function) (Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
@@ -139,6 +135,9 @@ typedef void (*ammarkpos_function) (IndexScanDesc scan);
/* restore marked scan position */
typedef void (*amrestrpos_function) (IndexScanDesc scan);
+/* get catalog of reloptions definitions */
+typedef void *(*amrelopt_catalog_function) ();
+
/*
* Callback function signatures - for parallel index scans.
*/
@@ -202,7 +201,6 @@ typedef struct IndexAmRoutine
amvacuumcleanup_function amvacuumcleanup;
amcanreturn_function amcanreturn; /* can be NULL */
amcostestimate_function amcostestimate;
- amoptions_function amoptions;
amproperty_function amproperty; /* can be NULL */
amvalidate_function amvalidate;
ambeginscan_function ambeginscan;
@@ -212,6 +210,7 @@ typedef struct IndexAmRoutine
amendscan_function amendscan;
ammarkpos_function ammarkpos; /* can be NULL */
amrestrpos_function amrestrpos; /* can be NULL */
+ amrelopt_catalog_function amrelopt_catalog; /* can be NULL */
/* interface functions to support parallel index scans */
amestimateparallelscan_function amestimateparallelscan; /* can be NULL */
diff --git a/src/include/access/brin.h b/src/include/access/brin.h
index 896824a..c84d6d4 100644
--- a/src/include/access/brin.h
+++ b/src/include/access/brin.h
@@ -25,6 +25,9 @@ typedef struct BrinOptions
} BrinOptions;
#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 abe8877..4c32dd1 100644
--- a/src/include/access/brin_internal.h
+++ b/src/include/access/brin_internal.h
@@ -102,7 +102,6 @@ extern IndexBulkDeleteResult *brinbulkdelete(IndexVacuumInfo *info,
void *callback_state);
extern IndexBulkDeleteResult *brinvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
-extern bytea *brinoptions(Datum reloptions, bool validate);
/* brin_validate.c */
extern bool brinvalidate(Oid opclassoid);
diff --git a/src/include/access/gin_private.h b/src/include/access/gin_private.h
index 34e7339..89559ab 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 1ad4ed6..ad4895f 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 bfdfed8..aa73ee3 100644
--- a/src/include/access/hash.h
+++ b/src/include/access/hash.h
@@ -196,6 +196,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)
*/
@@ -291,7 +304,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);
@@ -391,4 +403,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 6289ffa..38f0257 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..1be9df0
--- /dev/null
+++ b/src/include/access/options.h
@@ -0,0 +1,224 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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 */
+ 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);
+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 List *optionsDefListToRawValues(List *defList, options_parse_mode
+ parse_mode);
+extern Datum optionsValuesToTextArray(List *options_values);
+extern List *optionsTextArrayToRawValues(Datum array_datum);
+extern List *optionsMergeOptionValues(List *old_options, List *new_options);
+extern void optionsDefListValdateNamespaces(List *defList,
+ char **allowed_namespaces);
+extern List *optionsDefListFilterNamespaces(List *defList, char *namespace);
+extern List *optionsTextArrayToDefList(Datum options);
+extern List *optionsParseRawValues(List * raw_values, options_catalog *catalog,
+ options_parse_mode mode);
+extern bytea *optionsValuesToBytea(List * options, options_catalog *catalog);
+
+/*
+ * 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 861977a..78c45db 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,263 +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),
- /* if you add a new kind, make sure you update "last_default" too */
- RELOPT_KIND_LAST_DEFAULT = RELOPT_KIND_BRIN,
- /* 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 9dca8fd..a3b0a5e 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
@@ -183,7 +179,7 @@ typedef struct spgLeafConsistentOut
/* spgutils.c */
-extern bytea *spgoptions(Datum reloptions, bool validate);
+extern Datum spghandler(PG_FUNCTION_ARGS);
/* spginsert.c */
extern IndexBuildResult *spgbuild(Relation heap, Relation index,
diff --git a/src/include/access/spgist_private.h b/src/include/access/spgist_private.h
index 4072c05..38ac122 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 db7f145..d6464a7 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 f3a9701..33f5782 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -31,7 +31,7 @@ extern Oid AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode);
extern void AlterTable(Oid relid, LOCKMODE lockmode, AlterTableStmt *stmt);
-extern LOCKMODE AlterTableGetLockLevel(List *cmds);
+extern LOCKMODE AlterTableGetLockLevel(Oid relid, List *cmds);
extern void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index a617a7c..28113ef 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -269,7 +269,7 @@ typedef struct AutoVacOpts
float8 analyze_scale_factor;
} AutoVacOpts;
-typedef struct StdRdOptions
+typedef struct HeapOptions
{
int32 vl_len_; /* varlena header (do not touch directly!) */
int fillfactor; /* page fill factor in percent (0..100) */
@@ -277,107 +277,115 @@ typedef struct StdRdOptions
bool user_catalog_table; /* use as an additional catalog
* relation */
int parallel_workers; /* max number of parallel workers */
-} StdRdOptions;
+} HeapOptions;
+
+typedef struct ToastOptions
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ AutoVacOpts autovacuum; /* autovacuum-related options */
+} ToastOptions;
#define HEAP_MIN_FILLFACTOR 10
#define HEAP_DEFAULT_FILLFACTOR 100
+#define TOAST_DEFAULT_FILLFACTOR 100 /* Only default is actually used */
+
/*
- * RelationGetFillFactor
+ * HeapGetFillFactor
* Returns the relation's fillfactor. Note multiple eval of argument!
*/
-#define RelationGetFillFactor(relation, defaultff) \
+#define HeapGetFillFactor(relation, defaultff) \
((relation)->rd_options ? \
- ((StdRdOptions *) (relation)->rd_options)->fillfactor : (defaultff))
+ ((HeapOptions *) (relation)->rd_options)->fillfactor : (defaultff))
/*
- * RelationGetTargetPageUsage
- * Returns the relation's desired space usage per page in bytes.
- */
-#define RelationGetTargetPageUsage(relation, defaultff) \
- (BLCKSZ * RelationGetFillFactor(relation, defaultff) / 100)
-
-/*
- * RelationGetTargetPageFreeSpace
+ * HeapGetTargetPageFreeSpace
* Returns the relation's desired freespace per page in bytes.
*/
-#define RelationGetTargetPageFreeSpace(relation, defaultff) \
- (BLCKSZ * (100 - RelationGetFillFactor(relation, defaultff)) / 100)
+#define HeapGetTargetPageFreeSpace(relation, defaultff) \
+ (BLCKSZ * (100 - HeapGetFillFactor(relation, defaultff)) / 100)
/*
- * RelationIsUsedAsCatalogTable
+ * HeapIsUsedAsCatalogTable
* Returns whether the relation should be treated as a catalog table
* from the pov of logical decoding. Note multiple eval of argument!
*/
-#define RelationIsUsedAsCatalogTable(relation) \
+#define HeapIsUsedAsCatalogTable(relation) \
((relation)->rd_options && \
((relation)->rd_rel->relkind == RELKIND_RELATION || \
(relation)->rd_rel->relkind == RELKIND_MATVIEW) ? \
- ((StdRdOptions *) (relation)->rd_options)->user_catalog_table : false)
+ ((HeapOptions *) (relation)->rd_options)->user_catalog_table : false)
/*
- * RelationGetParallelWorkers
+ * HeapGetParallelWorkers
* Returns the relation's parallel_workers reloption setting.
* Note multiple eval of argument!
*/
-#define RelationGetParallelWorkers(relation, defaultpw) \
+#define HeapGetParallelWorkers(relation, defaultpw) \
((relation)->rd_options ? \
- ((StdRdOptions *) (relation)->rd_options)->parallel_workers : (defaultpw))
+ ((HeapOptions *) (relation)->rd_options)->parallel_workers : (defaultpw))
+#define ToastGetTargetPageFreeSpace() \
+ (BLCKSZ * (100 - TOAST_DEFAULT_FILLFACTOR) / 100)
/*
* ViewOptions
* Contents of rd_options for views
*/
+
+/*
+ * Definition of items of enum type. Names and codes. To add or modify item
+ * edit both lists
+ */
+#define VIEW_OPTION_CHECK_OPTION_VALUE_NAMES { \
+ "local", \
+ "cascaded", \
+ (const char *) NULL \
+}
+
+typedef enum view_option_check_option_value_numbers
+{
+ VIEW_OPTION_CHECK_OPTION_NOT_SET = -1,
+ VIEW_OPTION_CHECK_OPTION_LOCAL = 0,
+ VIEW_OPTION_CHECK_OPTION_CASCADED = 1,
+} view_option_check_option_value_numbers;
+
typedef struct ViewOptions
{
int32 vl_len_; /* varlena header (do not touch directly!) */
bool security_barrier;
- int check_option_offset;
+ int check_option;
} ViewOptions;
-
/*
- * RelationIsSecurityView
- * Returns whether the relation is security view, or not. Note multiple
- * eval of argument!
+ * ViewIsSecurityView
+ * Returns whether the view is security view, or not. Note multiple eval
+ * of argument!
*/
-#define RelationIsSecurityView(relation) \
+#define ViewIsSecurityView(relation) \
((relation)->rd_options ? \
- ((ViewOptions *) (relation)->rd_options)->security_barrier : false)
+ ((ViewOptions *) (relation)->rd_options)->security_barrier : false)
/*
- * RelationHasCheckOption
- * Returns true if the relation is a view defined with either the local
- * or the cascaded check option. Note multiple eval of argument!
+ * ViewHasCheckOption
+ * Returns true if view is defined with either the local or the cascaded
+ * check option. Note multiple eval of argument!
*/
-#define RelationHasCheckOption(relation) \
- ((relation)->rd_options && \
- ((ViewOptions *) (relation)->rd_options)->check_option_offset != 0)
-/*
- * RelationHasLocalCheckOption
- * Returns true if the relation is a view defined with the local check
- * option. Note multiple eval of argument!
- */
-#define RelationHasLocalCheckOption(relation) \
- ((relation)->rd_options && \
- ((ViewOptions *) (relation)->rd_options)->check_option_offset != 0 ? \
- strcmp((char *) (relation)->rd_options + \
- ((ViewOptions *) (relation)->rd_options)->check_option_offset, \
- "local") == 0 : false)
+#define ViewHasCheckOption(relation) \
+((relation)->rd_options && \
+ ((ViewOptions *) (relation)->rd_options)->check_option != \
+ VIEW_OPTION_CHECK_OPTION_NOT_SET)
/*
- * RelationHasCascadedCheckOption
- * Returns true if the relation is a view defined with the cascaded check
- * option. Note multiple eval of argument!
+ * ViewHasCascadedCheckOption
+ * Returns true if the view is defined with the cascaded check option.
+ * Note multiple eval of argument!
*/
-#define RelationHasCascadedCheckOption(relation) \
- ((relation)->rd_options && \
- ((ViewOptions *) (relation)->rd_options)->check_option_offset != 0 ? \
- strcmp((char *) (relation)->rd_options + \
- ((ViewOptions *) (relation)->rd_options)->check_option_offset, \
- "cascaded") == 0 : false)
+#define ViewHasCascadedCheckOption(relation) \
+((relation)->rd_options && \
+ ((ViewOptions *) (relation)->rd_options)->check_option == \
+ VIEW_OPTION_CHECK_OPTION_CASCADED)
/*
* RelationIsValid
@@ -556,7 +564,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 2227f2d..0a17ae9 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -2285,7 +2285,9 @@ ERROR: unrecognized parameter "autovacuum_enabled"
alter view my_locks set (autovacuum_enabled = false);
ERROR: unrecognized parameter "autovacuum_enabled"
alter table my_locks reset (autovacuum_enabled);
+ERROR: unrecognized parameter "autovacuum_enabled"
alter view my_locks reset (autovacuum_enabled);
+ERROR: unrecognized parameter "autovacuum_enabled"
begin;
alter view my_locks set (security_barrier=off);
select * from my_locks order by 1;
@@ -3372,3 +3374,10 @@ ERROR: partition constraint is violated by some row
-- cleanup
drop table p;
drop table p1;
+-- test alter column options
+CREATE TABLE tmp(i integer);
+INSERT INTO tmp VALUES (1);
+ALTER TABLE tmp ALTER COLUMN i SET (n_distinct = 1, n_distinct_inherited = 2);
+ALTER TABLE tmp ALTER COLUMN i RESET (n_distinct_inherited);
+ANALYZE tmp;
+DROP TABLE tmp;
diff --git a/src/test/regress/expected/brin.out b/src/test/regress/expected/brin.out
index f0008dd3..c52b022 100644
--- a/src/test/regress/expected/brin.out
+++ b/src/test/regress/expected/brin.out
@@ -406,3 +406,8 @@ SELECT brin_summarize_new_values('brinidx'); -- ok, no change expected
0
(1 row)
+-- 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/create_index.out b/src/test/regress/expected/create_index.out
index e519fdb..2ff134e 100644
--- a/src/test/regress/expected/create_index.out
+++ b/src/test/regress/expected/create_index.out
@@ -2340,7 +2340,7 @@ CREATE INDEX hash_name_index ON hash_name_heap USING hash (random name_ops);
WARNING: hash indexes are not WAL-logged and their use is discouraged
CREATE INDEX hash_txt_index ON hash_txt_heap USING hash (random text_ops);
WARNING: hash indexes are not WAL-logged and their use is discouraged
-CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_ops);
+CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=60);
WARNING: hash indexes are not WAL-logged and their use is discouraged
CREATE UNLOGGED TABLE unlogged_hash_table (id int4);
CREATE INDEX unlogged_hash_index ON unlogged_hash_table USING hash (id int4_ops);
diff --git a/src/test/regress/expected/gist.out b/src/test/regress/expected/gist.out
index c7181b0..19f1881 100644
--- a/src/test/regress/expected/gist.out
+++ b/src/test/regress/expected/gist.out
@@ -12,6 +12,20 @@ insert into gist_point_tbl (id, p)
select g+100000, point(g*10+1, g*10+1) from generate_series(1, 10000) g;
-- To test vacuum, delete some entries from all over the index.
delete from gist_point_tbl where id % 2 = 1;
+-- Test buffering and fillfactor reloption takes valid values
+create index gist_pointidx2 on gist_point_tbl using gist(p) with (buffering = on, fillfactor=50);
+create index gist_pointidx3 on gist_point_tbl using gist(p) with (buffering = off);
+create index gist_pointidx4 on gist_point_tbl using gist(p) with (buffering = auto);
+--Test buffering and fillfactor for refising invalid values
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (buffering = invalid_value);
+ERROR: invalid value for "buffering" option
+DETAIL: Valid values are "on", "off", "auto".
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfactor=9);
+ERROR: value 9 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfactor=101);
+ERROR: value 101 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
-- And also delete some concentration of values. (GiST doesn't currently
-- attempt to delete pages even when they become empty, but if it did, this
-- would exercise it)
diff --git a/src/test/regress/expected/hash_index.out b/src/test/regress/expected/hash_index.out
index f8b9f02..9f9352d 100644
--- a/src/test/regress/expected/hash_index.out
+++ b/src/test/regress/expected/hash_index.out
@@ -232,3 +232,14 @@ INSERT INTO hash_heap_float4 VALUES (1.1,1);
CREATE INDEX hash_idx ON hash_heap_float4 USING hash (x);
WARNING: hash indexes are not WAL-logged and their use is discouraged
DROP TABLE hash_heap_float4 CASCADE;
+-- Check reloptions min max values and that it is not allowed to ALTER
+CREATE INDEX hash_f8_index2 ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=9);
+WARNING: hash indexes are not WAL-logged and their use is discouraged
+ERROR: value 9 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+CREATE INDEX hash_f8_index2 ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=101);
+WARNING: hash indexes are not WAL-logged and their use is discouraged
+ERROR: value 101 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+ALTER INDEX hash_f8_index SET (fillfactor=99);
+ERROR: changing parameter "fillfactor" is not allowed
diff --git a/src/test/regress/expected/reloptions.out b/src/test/regress/expected/reloptions.out
new file mode 100644
index 0000000..835d72f
--- /dev/null
+++ b/src/test/regress/expected/reloptions.out
@@ -0,0 +1,193 @@
+-- Simple create
+CREATE TABLE reloptions_test(i INT) WITH (fillfactor=30,autovacuum_enabled = false, autovacuum_analyze_scale_factor = 0.2);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+ reloptions
+------------------------------------------------------------------------------
+ {fillfactor=30,autovacuum_enabled=false,autovacuum_analyze_scale_factor=0.2}
+(1 row)
+
+-- Test сase insensitive
+CREATE TABLE reloptions_test3(i INT) WITH (FiLlFaCtoR=40);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test3'::regclass;
+ reloptions
+-----------------
+ {fillfactor=40}
+(1 row)
+
+-- Fail on min/max values check
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=2);
+ERROR: value 2 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=110);
+ERROR: value 110 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor = -10.0);
+ERROR: value -10.0 out of bounds for option "autovacuum_analyze_scale_factor"
+DETAIL: Valid values are between "0.000000" and "100.000000".
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor = 110.0);
+ERROR: value 110.0 out of bounds for option "autovacuum_analyze_scale_factor"
+DETAIL: Valid values are between "0.000000" and "100.000000".
+-- Fail when option and namespase do not exist
+CREATE TABLE reloptions_test2(i INT) WITH (not_existing_option=2);
+ERROR: unrecognized parameter "not_existing_option"
+CREATE TABLE reloptions_test2(i INT) WITH (not_existing_namespace.fillfactor=2);
+ERROR: unrecognized parameter namespace "not_existing_namespace"
+-- Fail while setting unproper value
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=30.5);
+ERROR: invalid value for integer option "fillfactor": 30.5
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor='string');
+ERROR: invalid value for integer option "fillfactor": string
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=true);
+ERROR: invalid value for integer option "fillfactor": true
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled=12);
+ERROR: invalid value for boolean option "autovacuum_enabled": 12
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled=30.5);
+ERROR: invalid value for boolean option "autovacuum_enabled": 30.5
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled='string');
+ERROR: invalid value for boolean option "autovacuum_enabled": string
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor='string');
+ERROR: invalid value for floating point option "autovacuum_analyze_scale_factor": string
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor=true);
+ERROR: invalid value for floating point option "autovacuum_analyze_scale_factor": true
+-- Specifing name only should fail as if there was =ture after it
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor);
+ERROR: invalid value for integer option "fillfactor": true
+-- Simple ALTER TABLE
+ALTER TABLE reloptions_test SET (fillfactor=31, autovacuum_analyze_scale_factor = 0.3);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+ reloptions
+------------------------------------------------------------------------------
+ {autovacuum_enabled=false,fillfactor=31,autovacuum_analyze_scale_factor=0.3}
+(1 row)
+
+-- Check that we cat set boolean option to true just by mentioning it
+ALTER TABLE reloptions_test SET (autovacuum_enabled, fillfactor=32);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+ reloptions
+-----------------------------------------------------------------------------
+ {autovacuum_analyze_scale_factor=0.3,autovacuum_enabled=true,fillfactor=32}
+(1 row)
+
+-- Check that RESET works well
+ALTER TABLE reloptions_test RESET (fillfactor);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+ reloptions
+---------------------------------------------------------------
+ {autovacuum_analyze_scale_factor=0.3,autovacuum_enabled=true}
+(1 row)
+
+-- Check that RESETting all values make NULL reloptions record in pg_class
+ALTER TABLE reloptions_test RESET (autovacuum_enabled, autovacuum_analyze_scale_factor);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass AND reloptions IS NULL;
+ reloptions
+------------
+
+(1 row)
+
+-- Check RESET fails on att=value
+ALTER TABLE reloptions_test RESET (fillfactor=12);
+ERROR: RESET must not include values for parameters
+-- Check oids options is ignored
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test(i INT) WITH (fillfactor=20, oids=true);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+ reloptions
+-----------------
+ {fillfactor=20}
+(1 row)
+
+-- Now testing toast.* options
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test (s VARCHAR) WITH (toast.autovacuum_vacuum_cost_delay = 23 );
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+ reloptions
+-----------------------------------
+ {autovacuum_vacuum_cost_delay=23}
+(1 row)
+
+ALTER TABLE reloptions_test SET (toast.autovacuum_vacuum_cost_delay = 24);
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+ reloptions
+-----------------------------------
+ {autovacuum_vacuum_cost_delay=24}
+(1 row)
+
+ALTER TABLE reloptions_test RESET (toast.autovacuum_vacuum_cost_delay);
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+ reloptions
+------------
+
+(1 row)
+
+-- Fail on unexisting options in toast namespace
+CREATE TABLE reloptions_test2 (i int) WITH (toast.not_existing_option = 42 );
+ERROR: unrecognized parameter "toast.not_existing_option"
+-- Fail on setting reloption to a table that does not have a TOAST relation
+CREATE TABLE reloptions_test2 (i int) WITH (toast.autovacuum_vacuum_cost_delay = 23 );
+ERROR: no TOAST relation was created for a table. Can't set toast.* storage parameters
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test(i INT);
+ALTER TABLE reloptions_test SET (toast.autovacuum_vacuum_cost_delay = 23);
+ERROR: TOAST relation for this table does not exist. Can't change toast.* storage parameters
+ALTER TABLE reloptions_test RESET (toast.autovacuum_vacuum_cost_delay);
+ERROR: TOAST relation for this table does not exist. Can't change toast.* storage parameters
+-- autovacuum_analyze_scale_factor and autovacuum_analyze_threshold should be
+-- accepted by heap but rejected by toast (special case)
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test (s VARCHAR) WITH (autovacuum_analyze_scale_factor=1, autovacuum_analyze_threshold=1);
+CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analyze_scale_factor=1);
+ERROR: unrecognized parameter "toast.autovacuum_analyze_scale_factor"
+CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analyze_threshold=1);
+ERROR: unrecognized parameter "toast.autovacuum_analyze_threshold"
+-- And now mixed toast + heap
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test (s VARCHAR) WITH (toast.autovacuum_vacuum_cost_delay = 23, autovacuum_vacuum_cost_delay = 24, fillfactor = 40);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+ reloptions
+-------------------------------------------------
+ {autovacuum_vacuum_cost_delay=24,fillfactor=40}
+(1 row)
+
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+ reloptions
+-----------------------------------
+ {autovacuum_vacuum_cost_delay=23}
+(1 row)
+
+-- Same FOR CREATE and ALTER INDEX for btree indexes
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (fillfactor=30);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx'::regclass;
+ reloptions
+-----------------
+ {fillfactor=30}
+(1 row)
+
+-- Fail when option and namespase do not exist
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_existing_option=2);
+ERROR: unrecognized parameter "not_existing_option"
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_existing_option.fillfactor=2);
+ERROR: unrecognized parameter namespace "not_existing_option"
+-- Check ranges
+CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfactor=1);
+ERROR: value 1 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfactor=130);
+ERROR: value 130 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+-- Check alter
+ALTER INDEX reloptions_test_idx SET (fillfactor=40);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx'::regclass;
+ reloptions
+-----------------
+ {fillfactor=40}
+(1 row)
+
+-- Check alter on empty relop list
+CREATE INDEX reloptions_test_idx3 ON reloptions_test (s);
+ALTER INDEX reloptions_test_idx3 SET (fillfactor=40);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx3'::regclass;
+ reloptions
+-----------------
+ {fillfactor=40}
+(1 row)
+
diff --git a/src/test/regress/expected/spgist.out b/src/test/regress/expected/spgist.out
index 0691e91..bb58fb7 100644
--- a/src/test/regress/expected/spgist.out
+++ b/src/test/regress/expected/spgist.out
@@ -4,7 +4,7 @@
-- There are other tests to test different SP-GiST opclasses. This is for
-- testing SP-GiST code itself.
create table spgist_point_tbl(id int4, p point);
-create index spgist_point_idx on spgist_point_tbl using spgist(p);
+create index spgist_point_idx on spgist_point_tbl using spgist(p) with (fillfactor = 75);
-- Test vacuum-root operation. It gets invoked when the root is also a leaf,
-- i.e. the index is very small.
insert into spgist_point_tbl (id, p)
@@ -37,3 +37,12 @@ select g, 'baaaaaaaaaaaaaar' || g from generate_series(1, 1000) g;
-- tuple to be moved to another page.
insert into spgist_text_tbl (id, t)
select -g, 'f' || repeat('o', 100-g) || 'surprise' from generate_series(1, 100) g;
+-- Check reloptions min, max values
+create index spgist_point_idx2 on spgist_point_tbl using spgist(p) with (fillfactor = 9);
+ERROR: value 9 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+create index spgist_point_idx2 on spgist_point_tbl using spgist(p) with (fillfactor = 101);
+ERROR: value 101 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+-- ALTER reloption should work
+alter index spgist_point_idx set (fillfactor = 76);
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out
index aa06d1d..287dd84 100644
--- a/src/test/regress/expected/updatable_views.out
+++ b/src/test/regress/expected/updatable_views.out
@@ -1551,7 +1551,7 @@ SELECT * FROM base_tbl;
ALTER VIEW rw_view1 SET (check_option=here); -- invalid
ERROR: invalid value for "check_option" option
-DETAIL: Valid values are "local" and "cascaded".
+DETAIL: Valid values are "local", "cascaded".
ALTER VIEW rw_view1 SET (check_option=local);
INSERT INTO rw_view2 VALUES (-20); -- should fail
ERROR: new row violates check option for view "rw_view1"
diff --git a/src/test/regress/input/tablespace.source b/src/test/regress/input/tablespace.source
index 03a62bd..7f7934b 100644
--- a/src/test/regress/input/tablespace.source
+++ b/src/test/regress/input/tablespace.source
@@ -12,10 +12,10 @@ DROP TABLESPACE regress_tblspacewith;
CREATE TABLESPACE regress_tblspace LOCATION '@testtablespace@';
-- try setting and resetting some properties for the new tablespace
-ALTER TABLESPACE regress_tblspace SET (random_page_cost = 1.0);
+ALTER TABLESPACE regress_tblspace SET (random_page_cost = 1.0, seq_page_cost = 1.1);
ALTER TABLESPACE regress_tblspace SET (some_nonexistent_parameter = true); -- fail
ALTER TABLESPACE regress_tblspace RESET (random_page_cost = 2.0); -- fail
-ALTER TABLESPACE regress_tblspace RESET (random_page_cost, seq_page_cost); -- ok
+ALTER TABLESPACE regress_tblspace RESET (random_page_cost, effective_io_concurrency); -- ok
-- create a schema we can use
CREATE SCHEMA testschema;
diff --git a/src/test/regress/output/tablespace.source b/src/test/regress/output/tablespace.source
index aaedf5f..2443511 100644
--- a/src/test/regress/output/tablespace.source
+++ b/src/test/regress/output/tablespace.source
@@ -14,12 +14,12 @@ DROP TABLESPACE regress_tblspacewith;
-- create a tablespace we can use
CREATE TABLESPACE regress_tblspace LOCATION '@testtablespace@';
-- try setting and resetting some properties for the new tablespace
-ALTER TABLESPACE regress_tblspace SET (random_page_cost = 1.0);
+ALTER TABLESPACE regress_tblspace SET (random_page_cost = 1.0, seq_page_cost = 1.1);
ALTER TABLESPACE regress_tblspace SET (some_nonexistent_parameter = true); -- fail
ERROR: unrecognized parameter "some_nonexistent_parameter"
ALTER TABLESPACE regress_tblspace RESET (random_page_cost = 2.0); -- fail
ERROR: RESET must not include values for parameters
-ALTER TABLESPACE regress_tblspace RESET (random_page_cost, seq_page_cost); -- ok
+ALTER TABLESPACE regress_tblspace RESET (random_page_cost, effective_io_concurrency); -- ok
-- create a schema we can use
CREATE SCHEMA testschema;
-- try a table
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 9f38349..f35ece6 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -109,7 +109,7 @@ test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combo
# NB: temp.sql does a reconnect which transiently uses 2 connections,
# so keep this parallel group to at most 19 tests
# ----------
-test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml
+test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml reloptions
# event triggers cannot run concurrently with any test that runs DDL
test: event_trigger
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 2987b24..8173710 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -173,5 +173,6 @@ test: returning
test: largeobject
test: with
test: xml
+test: reloptions
test: event_trigger
test: stats
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index 8cd6786..bd59482 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -2228,3 +2228,11 @@ alter table p attach partition p1 for values from (1, 2) to (1, 10);
-- cleanup
drop table p;
drop table p1;
+
+-- test alter column options
+CREATE TABLE tmp(i integer);
+INSERT INTO tmp VALUES (1);
+ALTER TABLE tmp ALTER COLUMN i SET (n_distinct = 1, n_distinct_inherited = 2);
+ALTER TABLE tmp ALTER COLUMN i RESET (n_distinct_inherited);
+ANALYZE tmp;
+DROP TABLE tmp;
diff --git a/src/test/regress/sql/brin.sql b/src/test/regress/sql/brin.sql
index 5bf5387..6353c82 100644
--- a/src/test/regress/sql/brin.sql
+++ b/src/test/regress/sql/brin.sql
@@ -409,3 +409,7 @@ UPDATE brintest SET textcol = '' WHERE textcol IS NOT NULL;
SELECT brin_summarize_new_values('brintest'); -- error, not an index
SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index
SELECT brin_summarize_new_values('brinidx'); -- ok, no change expected
+
+-- Check that changing reloptions for brin index is not allowed
+ALTER INDEX brinidx SET (pages_per_range = 10);
+ALTER INDEX brinidx RESET (pages_per_range);
diff --git a/src/test/regress/sql/create_index.sql b/src/test/regress/sql/create_index.sql
index 1648072..1f8d101 100644
--- a/src/test/regress/sql/create_index.sql
+++ b/src/test/regress/sql/create_index.sql
@@ -682,7 +682,7 @@ CREATE INDEX hash_name_index ON hash_name_heap USING hash (random name_ops);
CREATE INDEX hash_txt_index ON hash_txt_heap USING hash (random text_ops);
-CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_ops);
+CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=60);
CREATE UNLOGGED TABLE unlogged_hash_table (id int4);
CREATE INDEX unlogged_hash_index ON unlogged_hash_table USING hash (id int4_ops);
diff --git a/src/test/regress/sql/gist.sql b/src/test/regress/sql/gist.sql
index 9d4ff1e..ce1ac1a 100644
--- a/src/test/regress/sql/gist.sql
+++ b/src/test/regress/sql/gist.sql
@@ -17,6 +17,17 @@ select g+100000, point(g*10+1, g*10+1) from generate_series(1, 10000) g;
-- To test vacuum, delete some entries from all over the index.
delete from gist_point_tbl where id % 2 = 1;
+-- Test buffering and fillfactor reloption takes valid values
+create index gist_pointidx2 on gist_point_tbl using gist(p) with (buffering = on, fillfactor=50);
+create index gist_pointidx3 on gist_point_tbl using gist(p) with (buffering = off);
+create index gist_pointidx4 on gist_point_tbl using gist(p) with (buffering = auto);
+--Test buffering and fillfactor for refising invalid values
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (buffering = invalid_value);
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfactor=9);
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfactor=101);
+
+
+
-- And also delete some concentration of values. (GiST doesn't currently
-- attempt to delete pages even when they become empty, but if it did, this
-- would exercise it)
diff --git a/src/test/regress/sql/hash_index.sql b/src/test/regress/sql/hash_index.sql
index 15a3b06..b034efb 100644
--- a/src/test/regress/sql/hash_index.sql
+++ b/src/test/regress/sql/hash_index.sql
@@ -194,3 +194,8 @@ CREATE TABLE hash_heap_float4 (x float4, y int);
INSERT INTO hash_heap_float4 VALUES (1.1,1);
CREATE INDEX hash_idx ON hash_heap_float4 USING hash (x);
DROP TABLE hash_heap_float4 CASCADE;
+
+-- Check reloptions min max values and that it is not allowed to ALTER
+CREATE INDEX hash_f8_index2 ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=9);
+CREATE INDEX hash_f8_index2 ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=101);
+ALTER INDEX hash_f8_index SET (fillfactor=99);
\ No newline at end of file
diff --git a/src/test/regress/sql/reloptions.sql b/src/test/regress/sql/reloptions.sql
new file mode 100644
index 0000000..0bfec28
--- /dev/null
+++ b/src/test/regress/sql/reloptions.sql
@@ -0,0 +1,153 @@
+
+-- Simple create
+CREATE TABLE reloptions_test(i INT) WITH (fillfactor=30,autovacuum_enabled = false, autovacuum_analyze_scale_factor = 0.2);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+
+-- Test сase insensitive
+CREATE TABLE reloptions_test3(i INT) WITH (FiLlFaCtoR=40);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test3'::regclass;
+
+-- Fail on min/max values check
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=2);
+
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=110);
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor = -10.0);
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor = 110.0);
+
+-- Fail when option and namespase do not exist
+
+CREATE TABLE reloptions_test2(i INT) WITH (not_existing_option=2);
+
+CREATE TABLE reloptions_test2(i INT) WITH (not_existing_namespace.fillfactor=2);
+
+-- Fail while setting unproper value
+
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=30.5);
+
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor='string');
+
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=true);
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled=12);
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled=30.5);
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled='string');
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor='string');
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor=true);
+
+-- Specifing name only should fail as if there was =ture after it
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor);
+
+-- Simple ALTER TABLE
+
+ALTER TABLE reloptions_test SET (fillfactor=31, autovacuum_analyze_scale_factor = 0.3);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+
+-- Check that we cat set boolean option to true just by mentioning it
+
+ALTER TABLE reloptions_test SET (autovacuum_enabled, fillfactor=32);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+
+-- Check that RESET works well
+
+ALTER TABLE reloptions_test RESET (fillfactor);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+
+-- Check that RESETting all values make NULL reloptions record in pg_class
+ALTER TABLE reloptions_test RESET (autovacuum_enabled, autovacuum_analyze_scale_factor);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass AND reloptions IS NULL;
+
+-- Check RESET fails on att=value
+
+ALTER TABLE reloptions_test RESET (fillfactor=12);
+
+-- Check oids options is ignored
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test(i INT) WITH (fillfactor=20, oids=true);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+
+-- Now testing toast.* options
+DROP TABLE reloptions_test;
+
+CREATE TABLE reloptions_test (s VARCHAR) WITH (toast.autovacuum_vacuum_cost_delay = 23 );
+
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+
+ALTER TABLE reloptions_test SET (toast.autovacuum_vacuum_cost_delay = 24);
+
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+
+ALTER TABLE reloptions_test RESET (toast.autovacuum_vacuum_cost_delay);
+
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+
+-- Fail on unexisting options in toast namespace
+CREATE TABLE reloptions_test2 (i int) WITH (toast.not_existing_option = 42 );
+
+-- Fail on setting reloption to a table that does not have a TOAST relation
+CREATE TABLE reloptions_test2 (i int) WITH (toast.autovacuum_vacuum_cost_delay = 23 );
+DROP TABLE reloptions_test;
+
+CREATE TABLE reloptions_test(i INT);
+ALTER TABLE reloptions_test SET (toast.autovacuum_vacuum_cost_delay = 23);
+ALTER TABLE reloptions_test RESET (toast.autovacuum_vacuum_cost_delay);
+
+-- autovacuum_analyze_scale_factor and autovacuum_analyze_threshold should be
+-- accepted by heap but rejected by toast (special case)
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test (s VARCHAR) WITH (autovacuum_analyze_scale_factor=1, autovacuum_analyze_threshold=1);
+
+CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analyze_scale_factor=1);
+CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analyze_threshold=1);
+
+-- And now mixed toast + heap
+DROP TABLE reloptions_test;
+
+CREATE TABLE reloptions_test (s VARCHAR) WITH (toast.autovacuum_vacuum_cost_delay = 23, autovacuum_vacuum_cost_delay = 24, fillfactor = 40);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+
+-- Same FOR CREATE and ALTER INDEX for btree indexes
+
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (fillfactor=30);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx'::regclass;
+
+-- Fail when option and namespase do not exist
+
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_existing_option=2);
+
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_existing_option.fillfactor=2);
+
+-- Check ranges
+
+CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfactor=1);
+
+CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfactor=130);
+
+-- Check alter
+
+ALTER INDEX reloptions_test_idx SET (fillfactor=40);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx'::regclass;
+
+-- Check alter on empty relop list
+
+CREATE INDEX reloptions_test_idx3 ON reloptions_test (s);
+
+ALTER INDEX reloptions_test_idx3 SET (fillfactor=40);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx3'::regclass;
+
+
diff --git a/src/test/regress/sql/spgist.sql b/src/test/regress/sql/spgist.sql
index 5896b50..8d26e49 100644
--- a/src/test/regress/sql/spgist.sql
+++ b/src/test/regress/sql/spgist.sql
@@ -5,7 +5,7 @@
-- testing SP-GiST code itself.
create table spgist_point_tbl(id int4, p point);
-create index spgist_point_idx on spgist_point_tbl using spgist(p);
+create index spgist_point_idx on spgist_point_tbl using spgist(p) with (fillfactor = 75);
-- Test vacuum-root operation. It gets invoked when the root is also a leaf,
-- i.e. the index is very small.
@@ -48,3 +48,10 @@ select g, 'baaaaaaaaaaaaaar' || g from generate_series(1, 1000) g;
-- tuple to be moved to another page.
insert into spgist_text_tbl (id, t)
select -g, 'f' || repeat('o', 100-g) || 'surprise' from generate_series(1, 100) g;
+
+-- Check reloptions min, max values
+create index spgist_point_idx2 on spgist_point_tbl using spgist(p) with (fillfactor = 9);
+create index spgist_point_idx2 on spgist_point_tbl using spgist(p) with (fillfactor = 101);
+
+-- ALTER reloption should work
+alter index spgist_point_idx set (fillfactor = 76);
В письме от 12 марта 2017 22:29:30 пользователь Nikolay Shaplov написал:
Another rebase.
I've rebased the patch again.
Alvaro, I am waiting for you! ;-)
I've rebased the patch, so it can be applied to current master. See
attachment.Alvaro in private letter told that he will review the patch some time
later. So I am waiting.Also added this patch to commitfest:
https://commitfest.postgresql.org/13/992/(Please see Attribution notice at the bottom of the letter)
Hi! Some time ago I've send a proposal, about removing all am-related
code
from src/backend/access/common/reloptions.c and move it into somewhere
inside am code. It was in
/messages/by-id/4636505.Bu9AKW1Kzc@nataraj-amd64
thread.Now I have a patch that is ready
Before explaining what have been done, I need to define some concepts,
so
it would be easy to speak about them in the further explanation.Reloptions values in postgres actually exists in four representations:
DefList*, TextArray[], Bytea and so called Values.- DefList representation is a list of DefElem, it is the representation
in
which reloptions comes from syntax analyzer, when relation is created or
altered, and also this representation that is needed for pg_dump to dump
a
create statement that creates this relation.- TextArray[] representation is a way, how reloptions are stored in
pg_catalog. It is TEXT[] attribute in the pg_catalog table and from the
poinf o view of postgres code it is Datum array of TEXT Datums, when
read
into the memory- Bytea (or I will also call it Binary) representation, is a
representation with which relation code really works. It has C-structure
inside in which fixed-length options are sored, some space is reserved
for storing varlen values after the structure, and certainly it has
BYTEA
header in front of all of it. It is cached, it is fast to access. It is
a
good stuff.- Values (or in some cases RawValues) -- internal representation of
option
parser. Technically it is List of option_value structures (old name for
it
is relopt_value). This representation is used for parsing and validating
options. Actually all the convertations between representations listed
above are dove through Values representation. Values are called
RawValues
when Values List were already created, but it was not parsed yet, and
all
option values are in raw state: represented as a text.DefList and TextArray representations are converted in both ways(always
through Values representation): we need DefList -> TextArray to create
or
change entry in the pg_catalog, and need TextArray -> DefList for
pg_dumpBytea representation is converted from TextArray representation (also
through Values representation). When relation code, tries to get cached
Bytea representation for certain relation, and none is actually cached,
then cache code calls extractRelOptions function that fetches entry from
pg_catalog for that relation, gets reloptions TextArray[], converts it
into Bytea and cache it.Each reloption has it's own definition. This definitions tells how this
option should be parsed, validated and converted. Before my patch this
information were stored in relopt_gen and relopt_parse_elt structures.
In
my patch all this information were moved into option_definition_basic
structure (that is actually old relopt_gen + relopt_parse_elt)The set of options definition I would call a options catalog. Before my
patch there was one global options catalog (actually it was divided into
parts by data types and has another part for dynamically added options,
but
nevertheless, it was global). After my patch there would be a separate
options catalog for each relation type. For AM-relation this catalog
would
be available via AM-handler. for other relation types it would be still
kept in reloption.cNow I am ready to tell what actually have been done in this patch:
1. I moved options definitions from global lists from reloptions.c
[type name]RelOpts to lists that are kept inside AM code. One list per
AM.
heap options, toast options and options for all other relation types
that
are not accessible through AccessMethod, all were kept inside
reloptions.c, but now it is one list for each relation type, not a
global
one2. Each AccessMethod had amoptions function that was responsible for
converting reloptions from TextArray[] representation into Bytea
representaions. This function used functions from reloptions.c and also
had
an array of relopt_parse_elt that defines how parsed reloption data
should
be packed inside Bytea chunk. I've moved data from relopt_parse_elt
array
into option definitions that are stored in the catalog (since catalog
are
now defined inside the AccessMethod we have access to a structure of
Bytea
representation at the place where we define option catalog)3. Speaking of amoptions function, I've completely removed it, since we
do
not need it. For developers of Access Methods who still want to do some
custom action with just parsed data, I've added postprocess_fun to
option
catalog. If this function is defined, it is called by convertation
function
right after Bytea representation were created. In postprocess_fun
developer
can do some custom validation, or change just parsed data. This feature
is
used in bloom index.4. Instead of amoptions function I've added amrelopt_catalog function to
the Access Method. This function returns option definition catalog for
an
Access Method. The catalog is used for processing options.5. As before, relation cache calls extractRelOptions when it needs
options
that were not previously cached.
Before my patch, it created Bytea representations for non-AM relations,
and
called amoptions AM function to get Bytea representation for AM
relation.
In by patch, it now gets option catalog, (using local functions for
non-AM
relations, and using amrelopt_catalog function for AM-relations), and
then
use this catalog to convert options from TextArray into Bytea
representation.6. I've added some more featues:
- Now you can set OPTION_DEFINITION_FLAG_FORBID_ALTER flag in definition
of
the option, and then postgres will refuse to change option value using
ALTER command. In many indexes, some options are used only for index
creation. After this it's value is saved in MetaPage, and used from
there.
Nevertheless user is allowed to change option value, though it affects
nothing, one need to recreate index to really change such value. Now
using
this flag we can prevent user from getting an illusion that he
successfully
changed option value.- After applying my patch if you try to set toast. option for table that
does not have toast, you will get an error. Before postgres will accept
toast option for a table without a toast, but this value will be lost.
This
is bad behavior, so now postgres will throw an error in this case.- I've noticed that all string relation options in postgres are
technically
enum options. So I've added enum option type. This will save some bytes
of
the memory and some tacts of the CPU. I also left string option type
(through it is not used now). A. Korotkov said that it might be needed
later for storing patterns, xml/json paths or something like that.- Added some more tests that will trigger more options code. Though I am
dreaming about more advanced test system, that will allow to check that
certain value is received somewhere deep in the code.7. And now the most controversial change. I've got rid of StdRdOptions
structure, in which options for heap, toast, nbtree, hash and spgist
were
stored in Bytea representation. In indexes only fillfactor value were
actually used from the whole big structure. So I've created a separate
structure for each relation type. HeapOptions and ToastOptions are very
similar to each other. So common part of creating these catalogs were
moved
to
add_autovacuum_options function that are called while creating each
catalog.Now to the controversial part: in src/include/utils/rel.h had a lot of
Relation* macroses that used StdRdOptions structure. I've changed them
into
View* Heap* and Toast* analogues, and changed the code to use these
macroses instead of Relation* one. But this part of code I least sure
of.
I'd like to ask experienced developers to double check it.8. I've moved all options-abstract code into options.c and options.h
file,
and left in reloptions.c and reloptions.h only the code that concerns
relation options. Actually the main idea of this patch was to get this
abstract code in order to use it for custom attribute options later. All
the rest just a side effects :-)So, before adding this to commitfest I want somebody to give a general
look
at it, if the code is OK.Alvaro Herrera, you once said that you can review the patch...
The patch is available in the attachment. It can be applied to current
masterYou can also see latest version on my github
https://github.com/dhyannataraj/postgres/tree/reloption_fix
at the reloption_fix branch.ATTRIBUTION NOTICE: I wrote this patch, when I was an employee in
Postgres
Professional company. So this patch should be attributed as patch from
Postgres Pro (or something like that). For some reasons I did not manage
to
commit this patch while I left that job. But I got a permission to
commit
it even if I left the company.
So my contribution to this patch as a independent developer was final
code
cleanup, and writing some more comments. All the rest was a work of
Postgres Pro employee Nikolay Shaplov.
--
Nikolay Shaplov, independent Perl & C/C++ developer. Available for hire.
Attachments:
reloptions6c.difftext/x-patch; charset=UTF-8; name=reloptions6c.diffDownload
diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
index 0cfe49a..21e59df 100644
--- a/contrib/bloom/bloom.h
+++ b/contrib/bloom/bloom.h
@@ -204,7 +204,7 @@ extern IndexBulkDeleteResult *blbulkdelete(IndexVacuumInfo *info,
void *callback_state);
extern IndexBulkDeleteResult *blvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
-extern bytea *bloptions(Datum reloptions, bool validate);
+extern bytea *bloptions(Datum reloptions, bool validate, bool for_alter);
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..93014af 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,27 @@ 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 +451,38 @@ BloomInitMetapage(Relation index)
UnlockReleaseBuffer(metaBuffer);
}
-/*
- * Parse reloptions for bloom index, producing a BloomOptions struct.
- */
-bytea *
-bloptions(Datum reloptions, bool validate)
+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);
+ bl_relopt_catalog->struct_size = sizeof(BloomOptions);
+ 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 cbc50f7..7e3bbc0 100644
--- a/contrib/bloom/expected/bloom.out
+++ b/contrib/bloom/expected/bloom.out
@@ -210,3 +210,37 @@ ORDER BY 1;
text_ops | t
(2 rows)
+-- reloptions test
+DROP INDEX bloomidx;
+CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (length=7, col1 = 4);
+SELECT reloptions FROM pg_class WHERE oid = 'bloomidx'::regclass;
+ reloptions
+-------------------
+ {length=7,col1=4}
+(1 row)
+
+-- check for min and max values
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=0);
+ERROR: value 0 out of bounds for option "length"
+DETAIL: Valid values are between "1" and "4096".
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=4097);
+ERROR: value 4097 out of bounds for option "length"
+DETAIL: Valid values are between "1" and "4096".
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=0);
+ERROR: value 0 out of bounds for option "col1"
+DETAIL: Valid values are between "1" and "4095".
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=4096);
+ERROR: value 4096 out of bounds for option "col1"
+DETAIL: Valid values are between "1" and "4095".
+-- check post_validate for colN<lengh
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=10,col1=11);
+ERROR: col0 should not be grater than length
+-- check that ALTERing of reloptions is forbidden
+ALTER INDEX bloomidx SET (length=4);
+ERROR: changing parameter "length" is not allowed
+ALTER INDEX bloomidx SET (col1=4);
+ERROR: changing parameter "col1" is not allowed
+ALTER INDEX bloomidx RESET (length);
+ERROR: changing parameter "length" is not allowed
+ALTER INDEX bloomidx RESET (col1);
+ERROR: changing parameter "col1" is not allowed
diff --git a/contrib/bloom/sql/bloom.sql b/contrib/bloom/sql/bloom.sql
index 2227460..dbac21c 100644
--- a/contrib/bloom/sql/bloom.sql
+++ b/contrib/bloom/sql/bloom.sql
@@ -81,3 +81,23 @@ SELECT opcname, amvalidate(opc.oid)
FROM pg_opclass opc JOIN pg_am am ON am.oid = opcmethod
WHERE amname = 'bloom'
ORDER BY 1;
+
+-- reloptions test
+
+DROP INDEX bloomidx;
+CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (length=7, col1 = 4);
+SELECT reloptions FROM pg_class WHERE oid = 'bloomidx'::regclass;
+-- check for min and max values
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=0);
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=4097);
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=0);
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=4096);
+-- check post_validate for colN<lengh
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=10,col1=11);
+
+-- check that ALTERing of reloptions is forbidden
+
+ALTER INDEX bloomidx SET (length=4);
+ALTER INDEX bloomidx SET (col1=4);
+ALTER INDEX bloomidx RESET (length);
+ALTER INDEX bloomidx RESET (col1);
diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
index c1e9089..5a37add 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -1949,7 +1949,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 735b794..b07399f 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 e24db56..be15189 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -65,7 +65,7 @@ PG_FUNCTION_INFO_V1(postgres_fdw_validator);
Datum
postgres_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid catalog = PG_GETARG_OID(1);
ListCell *cell;
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index b22563b..f69bcbd 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"
@@ -70,6 +70,7 @@ static void union_tuples(BrinDesc *bdesc, BrinMemTuple *a,
BrinTuple *b);
static void brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy);
+void *bringetreloptcatalog (void);
/*
* BRIN handler function: return IndexAmRoutine with access method parameters
@@ -103,7 +104,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;
@@ -113,6 +113,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;
@@ -756,36 +757,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)}
- };
-
- 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.
*/
@@ -1235,3 +1206,25 @@ brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy)
if (vacuum_fsm)
FreeSpaceMapVacuum(idxrel);
}
+
+static options_catalog * brin_relopt_catalog = NULL;
+
+void *
+bringetreloptcatalog (void)
+{
+ if (! brin_relopt_catalog)
+ {
+ brin_relopt_catalog = allocateOptionsCatalog(NULL);
+ brin_relopt_catalog->struct_size = sizeof(BrinOptions);
+
+ 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);
+ }
+ return brin_relopt_catalog;
+}
diff --git a/src/backend/access/common/Makefile b/src/backend/access/common/Makefile
index fb27944..c9fb063 100644
--- a/src/backend/access/common/Makefile
+++ b/src/backend/access/common/Makefile
@@ -12,7 +12,7 @@ subdir = src/backend/access/common
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
-OBJS = bufmask.o heaptuple.o indextuple.o printsimple.o printtup.o \
+OBJS = bufmask.o heaptuple.o indextuple.o options.o printsimple.o printtup.o \
reloptions.o scankey.o tupconvert.o tupdesc.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/common/options.c b/src/backend/access/common/options.c
new file mode 100644
index 0000000..fb413a5
--- /dev/null
+++ b/src/backend/access/common/options.c
@@ -0,0 +1,1388 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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);
+void *optionsAllocateBytea(options_catalog *catalog, List *options);
+
+
+/*
+ * 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.
+ */
+
+options_catalog *
+allocateOptionsCatalog(char *namespace)
+{
+ 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;
+
+ catalog->definitions = NULL;
+ catalog->num = 0;
+ catalog->num_allocated = 0;
+ catalog->struct_size = -1; /* Will Assert if not properly changed */
+ 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 + 1 >= catalog->num_allocated)
+ {
+ MemoryContext oldcxt;
+
+ 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->definitions[catalog->num + 1] = NULL;
+ 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
+ */
+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.
+ */
+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
+ */
+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.
+ */
+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.
+ */
+void *
+optionsAllocateBytea(options_catalog *catalog, List *options)
+{
+ Size size;
+ int i;
+ ListCell *cell;
+ int length;
+ void *res;
+
+ Assert(catalog->struct_size > 0); /* Make sure it was initialized */
+ 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.
+ *
+ */
+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 72e1253..fb1386f 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,919 +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[] =
-{
- {
- {
- "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.
@@ -945,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,
@@ -965,16 +66,19 @@ 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;
@@ -985,528 +89,293 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
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.
+ * 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
+ */
+
+/*
+ * 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.
*
- * If there are no options of the given kind, numrelopts is set to 0 and NULL
- * is returned.
+ * 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.
*
- * 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.
+ * 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
*/
-relopt_value *
-parseRelOptions(Datum options, bool validate, relopt_kind kind,
- int *numrelopts)
-{
- relopt_value *reloptions;
- 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)
- {
- *numrelopts = 0;
- return NULL;
- }
-
- 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;
+void
+add_autovacuum_options(options_catalog *catalog, int base_offset,
+ bool for_toast)
+{
+ 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);
}
/*
- * Subroutine for parseRelOptions, to parse and validate a single option's
- * value
+ * get_heap_relopt_catalog
+ * Returns an options catalog for heap relation.
*/
-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;
+static options_catalog * heap_relopt_catalog = NULL;
- 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)
+options_catalog *
+get_heap_relopt_catalog(void)
+{
+ if (! heap_relopt_catalog)
{
- 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;
+ heap_relopt_catalog = allocateOptionsCatalog(NULL);
+ heap_relopt_catalog->struct_size = sizeof(HeapOptions);
- 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;
+ 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);
- 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;
+ add_autovacuum_options(heap_relopt_catalog,
+ offsetof(HeapOptions, autovacuum), false);
- 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;
+ 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);
}
-
- if (parsed)
- option->isset = true;
- if (!nofree)
- pfree(value);
+ return heap_relopt_catalog;
}
/*
- * Given the result from parseRelOptions, allocate a struct that's of the
- * specified base size plus any extra space that's needed for string variables.
- *
- * "base" should be sizeof(struct) of the reloptions struct (StdRdOptions or
- * equivalent).
+ * get_toast_relopt_catalog
+ * Returns an options catalog for toast relation.
*/
-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;
+static options_catalog * toast_relopt_catalog = NULL;
- return palloc0(size);
-}
-
-/*
- * Given the result of parseRelOptions and a parsing table, fill in the
- * struct (previously allocated with allocateReloptStruct) with the parsed
- * values.
- *
- * rdopts is the pointer to the allocated struct to be filled.
- * basesize is the sizeof(struct) that was passed to allocateReloptStruct.
- * options, of length numoptions, is parseRelOptions' output.
- * elems, of length numelems, is the table describing the allowed options.
- * When validate is true, it is expected that all options appear in elems.
- */
-void
-fillRelOptions(void *rdopts, Size basesize,
- relopt_value *options, int numoptions,
- bool validate,
- const relopt_parse_elt *elems, int numelems)
+options_catalog *
+get_toast_relopt_catalog(void)
{
- int i;
- int offset = basesize;
-
- for (i = 0; i < numoptions; i++)
+ if (! toast_relopt_catalog)
{
- 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;
+ toast_relopt_catalog = allocateOptionsCatalog("toast");
+ toast_relopt_catalog->struct_size = sizeof(ToastOptions);
- 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);
+ add_autovacuum_options(toast_relopt_catalog,
+ offsetof(ToastOptions, autovacuum), true);
}
- SET_VARSIZE(rdopts, offset);
+ return toast_relopt_catalog;
}
-
/*
- * Option parser for anything that uses StdRdOptions.
+ * get_view_relopt_catalog
+ * Returns an options catalog for view 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)}
- };
-
- 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));
+static options_catalog * view_relopt_catalog = NULL;
- pfree(options);
-
- return (bytea *) rdopts;
-}
-
-/*
- * Option parser for views
- */
-bytea *
-view_reloptions(Datum reloptions, bool validate)
+options_catalog *
+get_view_relopt_catalog(void)
{
- 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);
+ static const char *enum_names[] = VIEW_OPTION_CHECK_OPTION_VALUE_NAMES;
- /* if none set, we're done */
- if (numoptions == 0)
- return NULL;
-
- vopts = allocateReloptStruct(sizeof(ViewOptions), options, numoptions);
-
- fillRelOptions((void *) vopts, sizeof(ViewOptions), options, numoptions,
- validate, tab, lengthof(tab));
-
- pfree(options);
-
- return (bytea *) vopts;
-}
+ if (! view_relopt_catalog)
+ {
+ view_relopt_catalog = allocateOptionsCatalog(NULL);
+ view_relopt_catalog->struct_size = sizeof(ViewOptions);
-/*
- * Parse options for heaps, views and toast tables.
- */
-bytea *
-heap_reloptions(char relkind, Datum reloptions, bool validate)
-{
- StdRdOptions *rdopts;
+ optionsCatalogAddItemBool(view_relopt_catalog, "security_barrier",
+ "View acts as a row security barrier",
+ AccessExclusiveLock,
+ 0, offsetof(ViewOptions, security_barrier), false);
- switch (relkind)
- {
- case RELKIND_TOASTVALUE:
- rdopts = (StdRdOptions *)
- default_reloptions(reloptions, validate, RELOPT_KIND_TOAST);
- if (rdopts != NULL)
- {
- /* adjust default-only parameters for TOAST relations */
- rdopts->fillfactor = 100;
- rdopts->autovacuum.analyze_threshold = -1;
- rdopts->autovacuum.analyze_scale_factor = -1;
- }
- return (bytea *) rdopts;
- case RELKIND_RELATION:
- case RELKIND_MATVIEW:
- case RELKIND_PARTITIONED_TABLE:
- return default_reloptions(reloptions, validate, RELOPT_KIND_HEAP);
- default:
- /* other relkinds are not supported */
- return NULL;
+ 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;
}
-
/*
- * Parse options for indexes.
- *
- * amoptions index AM's option parser function
- * reloptions options as text[] datum
- * validate error flag
+ * get_attribute_options_catalog
+ * Returns an options catalog for heap attributes
*/
-bytea *
-index_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
-{
- Assert(amoptions != NULL);
-
- /* Assume function is strict */
- if (!PointerIsValid(DatumGetPointer(reloptions)))
- return NULL;
-
- return amoptions(reloptions, validate);
-}
+static options_catalog * attribute_options_catalog = NULL;
-/*
- * Option parser for attribute reloptions
- */
-bytea *
-attribute_reloptions(Datum reloptions, bool validate)
+options_catalog *
+get_attribute_options_catalog(void)
{
- 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);
-
- fillRelOptions((void *) aopts, sizeof(AttributeOpts), options, numoptions,
- validate, tab, lengthof(tab));
+ if (! attribute_options_catalog)
+ {
+ attribute_options_catalog = allocateOptionsCatalog(NULL);
+ attribute_options_catalog->struct_size = sizeof(AttributeOpts);
- 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;
+static options_catalog * tablespace_options_catalog = NULL;
- tsopts = allocateReloptStruct(sizeof(TableSpaceOpts), options, numoptions);
+options_catalog *
+get_tablespace_options_catalog(void)
+{
+ if (! tablespace_options_catalog)
+ {
+ tablespace_options_catalog = allocateOptionsCatalog(NULL);
+ tablespace_options_catalog->struct_size = sizeof(TableSpaceOpts);
- fillRelOptions((void *) tsopts, sizeof(TableSpaceOpts), options, numoptions,
- validate, tab, lengthof(tab));
+ 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);
- pfree(options);
+ 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);
- return (bytea *) tsopts;
+ 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;
}
/*
@@ -1516,33 +385,51 @@ 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;
+ 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;
+ 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;
+ DefElem *def = (DefElem *) lfirst(cell);
- for (i = 0; relOpts[i]; i++)
+ int 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 d03d59d..50ed21c 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -16,7 +16,7 @@
#include "access/gin_private.h"
#include "access/ginxlog.h"
-#include "access/reloptions.h"
+#include "access/options.h"
#include "access/xloginsert.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_type.h"
@@ -26,7 +26,8 @@
#include "utils/builtins.h"
#include "utils/index_selfuncs.h"
#include "utils/typcache.h"
-
+#include "utils/memutils.h"
+#include "utils/guc.h"
/*
* GIN handler function: return IndexAmRoutine with access method parameters
@@ -60,7 +61,6 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amvacuumcleanup = ginvacuumcleanup;
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = gincostestimate;
- amroutine->amoptions = ginoptions;
amroutine->amproperty = NULL;
amroutine->amvalidate = ginvalidate;
amroutine->ambeginscan = ginbeginscan;
@@ -70,6 +70,7 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amendscan = ginendscan;
amroutine->ammarkpos = NULL;
amroutine->amrestrpos = NULL;
+ amroutine->amrelopt_catalog = gingetreloptcatalog;
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
@@ -593,35 +594,6 @@ ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
return entries;
}
-bytea *
-ginoptions(Datum reloptions, bool validate)
-{
- relopt_value *options;
- GinOptions *rdopts;
- int numoptions;
- static const relopt_parse_elt tab[] = {
- {"fastupdate", RELOPT_TYPE_BOOL, offsetof(GinOptions, useFastUpdate)},
- {"gin_pending_list_limit", RELOPT_TYPE_INT, offsetof(GinOptions,
- pendingListCleanupSize)}
- };
-
- options = parseRelOptions(reloptions, validate, RELOPT_KIND_GIN,
- &numoptions);
-
- /* if none set, we're done */
- if (numoptions == 0)
- return NULL;
-
- rdopts = allocateReloptStruct(sizeof(GinOptions), options, numoptions);
-
- fillRelOptions((void *) rdopts, sizeof(GinOptions), options, numoptions,
- validate, tab, lengthof(tab));
-
- pfree(options);
-
- return (bytea *) rdopts;
-}
-
/*
* Fetch index's statistical data into *stats
*
@@ -698,3 +670,32 @@ ginUpdateStats(Relation index, const GinStatsData *stats)
END_CRIT_SECTION();
}
+
+
+static options_catalog * gin_relopt_catalog = NULL;
+
+void *
+gingetreloptcatalog (void)
+{
+ if (! gin_relopt_catalog)
+ {
+ gin_relopt_catalog = allocateOptionsCatalog(NULL);
+ gin_relopt_catalog->struct_size = sizeof(GinOptions);
+
+ 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 6593771..0a2cd59 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;
@@ -1583,3 +1583,4 @@ gistvacuumpage(Relation rel, Page page, Buffer buffer)
* the page.
*/
}
+
diff --git a/src/backend/access/gist/gistbuild.c b/src/backend/access/gist/gistbuild.c
index f1f08bb..936d4d0 100644
--- a/src/backend/access/gist/gistbuild.c
+++ b/src/backend/access/gist/gistbuild.c
@@ -126,11 +126,9 @@ 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 +232,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 cbdaec9..efb5133 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -18,14 +18,13 @@
#include "access/gist_private.h"
#include "access/htup_details.h"
-#include "access/reloptions.h"
+#include "access/options.h"
#include "catalog/pg_opclass.h"
#include "storage/indexfsm.h"
#include "storage/lmgr.h"
#include "utils/builtins.h"
#include "utils/syscache.h"
-
/*
* Write itup vector to page, has no control of free space.
*/
@@ -812,34 +811,6 @@ gistNewBuffer(Relation r)
return buffer;
}
-bytea *
-gistoptions(Datum reloptions, bool validate)
-{
- relopt_value *options;
- GiSTOptions *rdopts;
- int numoptions;
- static const relopt_parse_elt tab[] = {
- {"fillfactor", RELOPT_TYPE_INT, offsetof(GiSTOptions, fillfactor)},
- {"buffering", RELOPT_TYPE_STRING, offsetof(GiSTOptions, bufferingModeOffset)}
- };
-
- options = parseRelOptions(reloptions, validate, RELOPT_KIND_GIST,
- &numoptions);
-
- /* if none set, we're done */
- if (numoptions == 0)
- return NULL;
-
- rdopts = allocateReloptStruct(sizeof(GiSTOptions), options, numoptions);
-
- fillRelOptions((void *) rdopts, sizeof(GiSTOptions), options, numoptions,
- validate, tab, lengthof(tab));
-
- pfree(options);
-
- return (bytea *) rdopts;
-}
-
/*
* gistproperty() -- Check boolean properties of indexes.
*
@@ -965,3 +936,32 @@ 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);
+ gist_relopt_catalog->struct_size = sizeof(GiSTOptions);
+
+ 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 cfcec34..453827c 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 622cc4b..f97426c 100644
--- a/src/backend/access/hash/hashpage.c
+++ b/src/backend/access/hash/hashpage.c
@@ -360,7 +360,7 @@ _hash_init(Relation rel, double num_tuples, ForkNumber forkNum)
data_width = sizeof(uint32);
item_width = MAXALIGN(sizeof(IndexTupleData)) + MAXALIGN(data_width) +
sizeof(ItemIdData); /* include the line pointer */
- ffactor = RelationGetTargetPageUsage(rel, HASH_DEFAULT_FILLFACTOR) / item_width;
+ ffactor = (BLCKSZ * HashGetFillFactor(rel) / 100) / item_width;
/* keep to a sane range */
if (ffactor < 10)
ffactor = 10;
diff --git a/src/backend/access/hash/hashutil.c b/src/backend/access/hash/hashutil.c
index 2e99719..5fea5e3 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"
@@ -220,12 +220,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
*/
@@ -514,3 +508,26 @@ _hash_kill_items(IndexScanDesc scan)
MarkBufferDirtyHint(so->hashso_curbuf, true);
}
}
+
+static options_catalog * hash_relopt_catalog = NULL;
+
+void *
+hashgetreloptcatalog(void)
+{
+ if (! hash_relopt_catalog)
+ {
+ hash_relopt_catalog = allocateOptionsCatalog(NULL);
+ hash_relopt_catalog->struct_size = sizeof(HashRelOptions);
+
+ 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 bffc971..f1a339d 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -2673,7 +2673,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 6529fe3..78ecc46 100644
--- a/src/backend/access/heap/hio.c
+++ b/src/backend/access/heap/hio.c
@@ -323,8 +323,11 @@ RelationGetBufferForTuple(Relation relation, Size len,
len, MaxHeapTupleSize)));
/* Compute desired extra freespace due to fillfactor option */
- saveFreeSpace = RelationGetTargetPageFreeSpace(relation,
- HEAP_DEFAULT_FILLFACTOR);
+ if (IsToastRelation(relation))
+ saveFreeSpace = ToastGetTargetPageFreeSpace();
+ else
+ saveFreeSpace = HeapGetTargetPageFreeSpace(relation,
+ HEAP_DEFAULT_FILLFACTOR);
if (otherBuffer != InvalidBuffer)
otherBlock = BufferGetBlockNumber(otherBuffer);
diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c
index d69a266..fef5e40 100644
--- a/src/backend/access/heap/pruneheap.c
+++ b/src/backend/access/heap/pruneheap.c
@@ -131,8 +131,12 @@ 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,
- HEAP_DEFAULT_FILLFACTOR);
+
+ if (IsToastRelation(relation))
+ minfree = ToastGetTargetPageFreeSpace();
+ else
+ minfree = HeapGetTargetPageFreeSpace(relation,
+ HEAP_DEFAULT_FILLFACTOR);
minfree = Max(minfree, BLCKSZ / 10);
if (PageIsFull(page) || PageGetHeapFreeSpace(page) < minfree)
diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c
index c7b283c..ee859e7 100644
--- a/src/backend/access/heap/rewriteheap.c
+++ b/src/backend/access/heap/rewriteheap.c
@@ -665,7 +665,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 6dca810..6b12304 100644
--- a/src/backend/access/nbtree/nbtinsert.c
+++ b/src/backend/access/nbtree/nbtinsert.c
@@ -1433,8 +1433,7 @@ _bt_findsplitloc(Relation rel,
state.is_rightmost = P_RIGHTMOST(opaque);
state.have_split = false;
if (state.is_leaf)
- state.fillfactor = RelationGetFillFactor(rel,
- BTREE_DEFAULT_FILLFACTOR);
+ state.fillfactor = BTGetFillFactor(rel);
else
state.fillfactor = BTREE_NONLEAF_FILLFACTOR;
state.newitemonleft = false; /* these just to keep compiler quiet */
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index 775f2ff..9d1b8ac 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -21,6 +21,7 @@
#include "access/nbtree.h"
#include "access/relscan.h"
#include "access/xlog.h"
+#include "access/reloptions.h"
#include "catalog/index.h"
#include "commands/vacuum.h"
#include "pgstat.h"
@@ -34,7 +35,6 @@
#include "utils/index_selfuncs.h"
#include "utils/memutils.h"
-
/* Working state for btbuild and its callback */
typedef struct
{
@@ -116,8 +116,7 @@ static void btvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
BTCycleId cycleid);
static void btvacuumpage(BTVacState *vstate, BlockNumber blkno,
BlockNumber orig_blkno);
-
-
+void * btgetreloptcatalog (void);
/*
* Btree handler function: return IndexAmRoutine with access method parameters
* and callbacks.
@@ -150,7 +149,6 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amvacuumcleanup = btvacuumcleanup;
amroutine->amcanreturn = btcanreturn;
amroutine->amcostestimate = btcostestimate;
- amroutine->amoptions = btoptions;
amroutine->amproperty = btproperty;
amroutine->amvalidate = btvalidate;
amroutine->ambeginscan = btbeginscan;
@@ -160,6 +158,7 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amendscan = btendscan;
amroutine->ammarkpos = btmarkpos;
amroutine->amrestrpos = btrestrpos;
+ amroutine->amrelopt_catalog = btgetreloptcatalog;
amroutine->amestimateparallelscan = btestimateparallelscan;
amroutine->aminitparallelscan = btinitparallelscan;
amroutine->amparallelrescan = btparallelrescan;
@@ -1391,3 +1390,26 @@ btcanreturn(Relation index, int attno)
{
return true;
}
+
+
+static options_catalog * bt_relopt_catalog = NULL;
+
+void *
+btgetreloptcatalog (void)
+{
+ if (! bt_relopt_catalog)
+ {
+ bt_relopt_catalog = allocateOptionsCatalog(NULL);
+ bt_relopt_catalog->struct_size = sizeof(BTRelOptions);
+
+ 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 3d041c4..692c751 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 5b259a3..3efb05c 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 "access/options.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 e57ac49..b24178c 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,26 @@ 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);
+ spgist_relopt_catalog->struct_size = sizeof(SpGistRelOptions);
+
+ 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 0e42316..838b357 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 646a884..5020ee1 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 d5d40e6..7aa4576 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 9618032..543b77f 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -325,7 +325,7 @@ DefineIndex(Oid relationId,
Form_pg_am accessMethodForm;
IndexAmRoutine *amRoutine;
bool amcanorder;
- amoptions_function amoptions;
+ amrelopt_catalog_function amoption_catalog;
Datum reloptions;
int16 *coloptions;
IndexInfo *indexInfo;
@@ -523,7 +523,7 @@ DefineIndex(Oid relationId,
accessMethodName)));
amcanorder = amRoutine->amcanorder;
- amoptions = amRoutine->amoptions;
+ amoption_catalog = amRoutine->amrelopt_catalog;
pfree(amRoutine);
ReleaseSysCache(tuple);
@@ -537,10 +537,18 @@ DefineIndex(Oid relationId,
/*
* Parse AM-specific options, convert to text array form, validate.
*/
- reloptions = transformRelOptions((Datum) 0, stmt->options,
- NULL, NULL, false, false);
-
- (void) index_reloptions(amoptions, reloptions, true);
+ if (amoption_catalog)
+ {
+ reloptions = transformOptions(amoption_catalog(),
+ (Datum) 0,stmt->options, 0);
+ }
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("access method %s does not support options",
+ accessMethodName)));
+ }
/*
* Prepare arguments for index_create, primarily an IndexInfo structure.
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 86329e5..c0434c6 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -499,7 +499,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
Datum reloptions;
ListCell *listptr;
AttrNumber attnum;
- static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
Oid ofTypeId;
ObjectAddress address;
@@ -585,13 +584,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)
{
@@ -3073,7 +3082,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);
@@ -3115,13 +3125,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)
{
@@ -3330,7 +3342,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:
@@ -4087,7 +4102,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",
@@ -5930,11 +5945,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));
@@ -9929,7 +9944,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 */
@@ -9958,25 +9973,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,
@@ -9990,7 +10032,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;
@@ -10050,6 +10092,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);
@@ -10073,12 +10117,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));
@@ -10106,6 +10153,17 @@ 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 f9c2620..9945318 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 5a45558..5e3762c 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 463f806..a6b9570 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -135,7 +135,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 47ca685..d4e2281 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -3085,7 +3085,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 673276a..61e201e 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1391,7 +1391,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 fa8de13..61bf653 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -2509,6 +2509,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 ||
@@ -2518,8 +2519,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 354e5d0..748ba3a 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -1488,7 +1488,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 */
@@ -2989,7 +2989,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
@@ -3025,8 +3025,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 20b5273..7b6f49e 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -996,6 +996,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,
@@ -1016,18 +1018,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))
{
@@ -1093,8 +1101,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 f7f85b5..b5f3dce 100644
--- a/src/backend/utils/cache/attoptcache.c
+++ b/src/backend/utils/cache/attoptcache.c
@@ -150,7 +150,10 @@ get_attribute_options(Oid attrelid, int attnum)
opts = NULL;
else
{
- bytea *bytea_opts = attribute_reloptions(datum, false);
+ bytea *bytea_opts;
+
+ bytea_opts = optionsTextArrayToBytea(
+ get_attribute_options_catalog(), datum);
opts = MemoryContextAlloc(CacheMemoryContext,
VARSIZE(bytea_opts));
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index ce55fc5..f560448 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -459,7 +459,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..faafcaf 100644
--- a/src/backend/utils/cache/spccache.c
+++ b/src/backend/utils/cache/spccache.c
@@ -149,7 +149,9 @@ 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 +196,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 f919cf8..9f3db93 100644
--- a/src/include/access/amapi.h
+++ b/src/include/access/amapi.h
@@ -98,10 +98,6 @@ typedef void (*amcostestimate_function) (struct PlannerInfo *root,
double *indexCorrelation,
double *indexPages);
-/* parse index reloptions */
-typedef bytea *(*amoptions_function) (Datum reloptions,
- bool validate);
-
/* report AM, index, or index column property */
typedef bool (*amproperty_function) (Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
@@ -139,6 +135,9 @@ typedef void (*ammarkpos_function) (IndexScanDesc scan);
/* restore marked scan position */
typedef void (*amrestrpos_function) (IndexScanDesc scan);
+/* get catalog of reloptions definitions */
+typedef void *(*amrelopt_catalog_function) ();
+
/*
* Callback function signatures - for parallel index scans.
*/
@@ -202,7 +201,6 @@ typedef struct IndexAmRoutine
amvacuumcleanup_function amvacuumcleanup;
amcanreturn_function amcanreturn; /* can be NULL */
amcostestimate_function amcostestimate;
- amoptions_function amoptions;
amproperty_function amproperty; /* can be NULL */
amvalidate_function amvalidate;
ambeginscan_function ambeginscan;
@@ -212,6 +210,7 @@ typedef struct IndexAmRoutine
amendscan_function amendscan;
ammarkpos_function ammarkpos; /* can be NULL */
amrestrpos_function amrestrpos; /* can be NULL */
+ amrelopt_catalog_function amrelopt_catalog; /* can be NULL */
/* interface functions to support parallel index scans */
amestimateparallelscan_function amestimateparallelscan; /* can be NULL */
diff --git a/src/include/access/brin.h b/src/include/access/brin.h
index 896824a..c84d6d4 100644
--- a/src/include/access/brin.h
+++ b/src/include/access/brin.h
@@ -25,6 +25,9 @@ typedef struct BrinOptions
} BrinOptions;
#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 abe8877..4c32dd1 100644
--- a/src/include/access/brin_internal.h
+++ b/src/include/access/brin_internal.h
@@ -102,7 +102,6 @@ extern IndexBulkDeleteResult *brinbulkdelete(IndexVacuumInfo *info,
void *callback_state);
extern IndexBulkDeleteResult *brinvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
-extern bytea *brinoptions(Datum reloptions, bool validate);
/* brin_validate.c */
extern bool brinvalidate(Oid opclassoid);
diff --git a/src/include/access/gin_private.h b/src/include/access/gin_private.h
index 34e7339..89559ab 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 1ad4ed6..ad4895f 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 eb1df57..12db8cc 100644
--- a/src/include/access/hash.h
+++ b/src/include/access/hash.h
@@ -208,6 +208,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)
*/
@@ -303,7 +316,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);
@@ -404,4 +416,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 6289ffa..38f0257 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..1be9df0
--- /dev/null
+++ b/src/include/access/options.h
@@ -0,0 +1,224 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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 */
+ 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);
+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 List *optionsDefListToRawValues(List *defList, options_parse_mode
+ parse_mode);
+extern Datum optionsValuesToTextArray(List *options_values);
+extern List *optionsTextArrayToRawValues(Datum array_datum);
+extern List *optionsMergeOptionValues(List *old_options, List *new_options);
+extern void optionsDefListValdateNamespaces(List *defList,
+ char **allowed_namespaces);
+extern List *optionsDefListFilterNamespaces(List *defList, char *namespace);
+extern List *optionsTextArrayToDefList(Datum options);
+extern List *optionsParseRawValues(List * raw_values, options_catalog *catalog,
+ options_parse_mode mode);
+extern bytea *optionsValuesToBytea(List * options, options_catalog *catalog);
+
+/*
+ * 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 861977a..78c45db 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,263 +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),
- /* if you add a new kind, make sure you update "last_default" too */
- RELOPT_KIND_LAST_DEFAULT = RELOPT_KIND_BRIN,
- /* 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 9dca8fd..a3b0a5e 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
@@ -183,7 +179,7 @@ typedef struct spgLeafConsistentOut
/* spgutils.c */
-extern bytea *spgoptions(Datum reloptions, bool validate);
+extern Datum spghandler(PG_FUNCTION_ARGS);
/* spginsert.c */
extern IndexBuildResult *spgbuild(Relation heap, Relation index,
diff --git a/src/include/access/spgist_private.h b/src/include/access/spgist_private.h
index 4072c05..38ac122 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 db7f145..d6464a7 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 f3a9701..33f5782 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -31,7 +31,7 @@ extern Oid AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode);
extern void AlterTable(Oid relid, LOCKMODE lockmode, AlterTableStmt *stmt);
-extern LOCKMODE AlterTableGetLockLevel(List *cmds);
+extern LOCKMODE AlterTableGetLockLevel(Oid relid, List *cmds);
extern void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index a617a7c..28113ef 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -269,7 +269,7 @@ typedef struct AutoVacOpts
float8 analyze_scale_factor;
} AutoVacOpts;
-typedef struct StdRdOptions
+typedef struct HeapOptions
{
int32 vl_len_; /* varlena header (do not touch directly!) */
int fillfactor; /* page fill factor in percent (0..100) */
@@ -277,107 +277,115 @@ typedef struct StdRdOptions
bool user_catalog_table; /* use as an additional catalog
* relation */
int parallel_workers; /* max number of parallel workers */
-} StdRdOptions;
+} HeapOptions;
+
+typedef struct ToastOptions
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ AutoVacOpts autovacuum; /* autovacuum-related options */
+} ToastOptions;
#define HEAP_MIN_FILLFACTOR 10
#define HEAP_DEFAULT_FILLFACTOR 100
+#define TOAST_DEFAULT_FILLFACTOR 100 /* Only default is actually used */
+
/*
- * RelationGetFillFactor
+ * HeapGetFillFactor
* Returns the relation's fillfactor. Note multiple eval of argument!
*/
-#define RelationGetFillFactor(relation, defaultff) \
+#define HeapGetFillFactor(relation, defaultff) \
((relation)->rd_options ? \
- ((StdRdOptions *) (relation)->rd_options)->fillfactor : (defaultff))
+ ((HeapOptions *) (relation)->rd_options)->fillfactor : (defaultff))
/*
- * RelationGetTargetPageUsage
- * Returns the relation's desired space usage per page in bytes.
- */
-#define RelationGetTargetPageUsage(relation, defaultff) \
- (BLCKSZ * RelationGetFillFactor(relation, defaultff) / 100)
-
-/*
- * RelationGetTargetPageFreeSpace
+ * HeapGetTargetPageFreeSpace
* Returns the relation's desired freespace per page in bytes.
*/
-#define RelationGetTargetPageFreeSpace(relation, defaultff) \
- (BLCKSZ * (100 - RelationGetFillFactor(relation, defaultff)) / 100)
+#define HeapGetTargetPageFreeSpace(relation, defaultff) \
+ (BLCKSZ * (100 - HeapGetFillFactor(relation, defaultff)) / 100)
/*
- * RelationIsUsedAsCatalogTable
+ * HeapIsUsedAsCatalogTable
* Returns whether the relation should be treated as a catalog table
* from the pov of logical decoding. Note multiple eval of argument!
*/
-#define RelationIsUsedAsCatalogTable(relation) \
+#define HeapIsUsedAsCatalogTable(relation) \
((relation)->rd_options && \
((relation)->rd_rel->relkind == RELKIND_RELATION || \
(relation)->rd_rel->relkind == RELKIND_MATVIEW) ? \
- ((StdRdOptions *) (relation)->rd_options)->user_catalog_table : false)
+ ((HeapOptions *) (relation)->rd_options)->user_catalog_table : false)
/*
- * RelationGetParallelWorkers
+ * HeapGetParallelWorkers
* Returns the relation's parallel_workers reloption setting.
* Note multiple eval of argument!
*/
-#define RelationGetParallelWorkers(relation, defaultpw) \
+#define HeapGetParallelWorkers(relation, defaultpw) \
((relation)->rd_options ? \
- ((StdRdOptions *) (relation)->rd_options)->parallel_workers : (defaultpw))
+ ((HeapOptions *) (relation)->rd_options)->parallel_workers : (defaultpw))
+#define ToastGetTargetPageFreeSpace() \
+ (BLCKSZ * (100 - TOAST_DEFAULT_FILLFACTOR) / 100)
/*
* ViewOptions
* Contents of rd_options for views
*/
+
+/*
+ * Definition of items of enum type. Names and codes. To add or modify item
+ * edit both lists
+ */
+#define VIEW_OPTION_CHECK_OPTION_VALUE_NAMES { \
+ "local", \
+ "cascaded", \
+ (const char *) NULL \
+}
+
+typedef enum view_option_check_option_value_numbers
+{
+ VIEW_OPTION_CHECK_OPTION_NOT_SET = -1,
+ VIEW_OPTION_CHECK_OPTION_LOCAL = 0,
+ VIEW_OPTION_CHECK_OPTION_CASCADED = 1,
+} view_option_check_option_value_numbers;
+
typedef struct ViewOptions
{
int32 vl_len_; /* varlena header (do not touch directly!) */
bool security_barrier;
- int check_option_offset;
+ int check_option;
} ViewOptions;
-
/*
- * RelationIsSecurityView
- * Returns whether the relation is security view, or not. Note multiple
- * eval of argument!
+ * ViewIsSecurityView
+ * Returns whether the view is security view, or not. Note multiple eval
+ * of argument!
*/
-#define RelationIsSecurityView(relation) \
+#define ViewIsSecurityView(relation) \
((relation)->rd_options ? \
- ((ViewOptions *) (relation)->rd_options)->security_barrier : false)
+ ((ViewOptions *) (relation)->rd_options)->security_barrier : false)
/*
- * RelationHasCheckOption
- * Returns true if the relation is a view defined with either the local
- * or the cascaded check option. Note multiple eval of argument!
+ * ViewHasCheckOption
+ * Returns true if view is defined with either the local or the cascaded
+ * check option. Note multiple eval of argument!
*/
-#define RelationHasCheckOption(relation) \
- ((relation)->rd_options && \
- ((ViewOptions *) (relation)->rd_options)->check_option_offset != 0)
-/*
- * RelationHasLocalCheckOption
- * Returns true if the relation is a view defined with the local check
- * option. Note multiple eval of argument!
- */
-#define RelationHasLocalCheckOption(relation) \
- ((relation)->rd_options && \
- ((ViewOptions *) (relation)->rd_options)->check_option_offset != 0 ? \
- strcmp((char *) (relation)->rd_options + \
- ((ViewOptions *) (relation)->rd_options)->check_option_offset, \
- "local") == 0 : false)
+#define ViewHasCheckOption(relation) \
+((relation)->rd_options && \
+ ((ViewOptions *) (relation)->rd_options)->check_option != \
+ VIEW_OPTION_CHECK_OPTION_NOT_SET)
/*
- * RelationHasCascadedCheckOption
- * Returns true if the relation is a view defined with the cascaded check
- * option. Note multiple eval of argument!
+ * ViewHasCascadedCheckOption
+ * Returns true if the view is defined with the cascaded check option.
+ * Note multiple eval of argument!
*/
-#define RelationHasCascadedCheckOption(relation) \
- ((relation)->rd_options && \
- ((ViewOptions *) (relation)->rd_options)->check_option_offset != 0 ? \
- strcmp((char *) (relation)->rd_options + \
- ((ViewOptions *) (relation)->rd_options)->check_option_offset, \
- "cascaded") == 0 : false)
+#define ViewHasCascadedCheckOption(relation) \
+((relation)->rd_options && \
+ ((ViewOptions *) (relation)->rd_options)->check_option == \
+ VIEW_OPTION_CHECK_OPTION_CASCADED)
/*
* RelationIsValid
@@ -556,7 +564,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 2227f2d..0a17ae9 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -2285,7 +2285,9 @@ ERROR: unrecognized parameter "autovacuum_enabled"
alter view my_locks set (autovacuum_enabled = false);
ERROR: unrecognized parameter "autovacuum_enabled"
alter table my_locks reset (autovacuum_enabled);
+ERROR: unrecognized parameter "autovacuum_enabled"
alter view my_locks reset (autovacuum_enabled);
+ERROR: unrecognized parameter "autovacuum_enabled"
begin;
alter view my_locks set (security_barrier=off);
select * from my_locks order by 1;
@@ -3372,3 +3374,10 @@ ERROR: partition constraint is violated by some row
-- cleanup
drop table p;
drop table p1;
+-- test alter column options
+CREATE TABLE tmp(i integer);
+INSERT INTO tmp VALUES (1);
+ALTER TABLE tmp ALTER COLUMN i SET (n_distinct = 1, n_distinct_inherited = 2);
+ALTER TABLE tmp ALTER COLUMN i RESET (n_distinct_inherited);
+ANALYZE tmp;
+DROP TABLE tmp;
diff --git a/src/test/regress/expected/brin.out b/src/test/regress/expected/brin.out
index f0008dd3..c52b022 100644
--- a/src/test/regress/expected/brin.out
+++ b/src/test/regress/expected/brin.out
@@ -406,3 +406,8 @@ SELECT brin_summarize_new_values('brinidx'); -- ok, no change expected
0
(1 row)
+-- 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/create_index.out b/src/test/regress/expected/create_index.out
index 26cd059..7b8f805 100644
--- a/src/test/regress/expected/create_index.out
+++ b/src/test/regress/expected/create_index.out
@@ -2337,7 +2337,7 @@ Options: fastupdate=on, gin_pending_list_limit=128
CREATE INDEX hash_i4_index ON hash_i4_heap USING hash (random int4_ops);
CREATE INDEX hash_name_index ON hash_name_heap USING hash (random name_ops);
CREATE INDEX hash_txt_index ON hash_txt_heap USING hash (random text_ops);
-CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_ops);
+CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=60);
CREATE UNLOGGED TABLE unlogged_hash_table (id int4);
CREATE INDEX unlogged_hash_index ON unlogged_hash_table USING hash (id int4_ops);
DROP TABLE unlogged_hash_table;
diff --git a/src/test/regress/expected/gist.out b/src/test/regress/expected/gist.out
index c7181b0..19f1881 100644
--- a/src/test/regress/expected/gist.out
+++ b/src/test/regress/expected/gist.out
@@ -12,6 +12,20 @@ insert into gist_point_tbl (id, p)
select g+100000, point(g*10+1, g*10+1) from generate_series(1, 10000) g;
-- To test vacuum, delete some entries from all over the index.
delete from gist_point_tbl where id % 2 = 1;
+-- Test buffering and fillfactor reloption takes valid values
+create index gist_pointidx2 on gist_point_tbl using gist(p) with (buffering = on, fillfactor=50);
+create index gist_pointidx3 on gist_point_tbl using gist(p) with (buffering = off);
+create index gist_pointidx4 on gist_point_tbl using gist(p) with (buffering = auto);
+--Test buffering and fillfactor for refising invalid values
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (buffering = invalid_value);
+ERROR: invalid value for "buffering" option
+DETAIL: Valid values are "on", "off", "auto".
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfactor=9);
+ERROR: value 9 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfactor=101);
+ERROR: value 101 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
-- And also delete some concentration of values. (GiST doesn't currently
-- attempt to delete pages even when they become empty, but if it did, this
-- would exercise it)
diff --git a/src/test/regress/expected/hash_index.out b/src/test/regress/expected/hash_index.out
index 0a18efa..c67b61a 100644
--- a/src/test/regress/expected/hash_index.out
+++ b/src/test/regress/expected/hash_index.out
@@ -230,3 +230,12 @@ CREATE TABLE hash_heap_float4 (x float4, y int);
INSERT INTO hash_heap_float4 VALUES (1.1,1);
CREATE INDEX hash_idx ON hash_heap_float4 USING hash (x);
DROP TABLE hash_heap_float4 CASCADE;
+-- Check reloptions min max values and that it is not allowed to ALTER
+CREATE INDEX hash_f8_index2 ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=9);
+ERROR: value 9 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+CREATE INDEX hash_f8_index2 ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=101);
+ERROR: value 101 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+ALTER INDEX hash_f8_index SET (fillfactor=99);
+ERROR: changing parameter "fillfactor" is not allowed
diff --git a/src/test/regress/expected/reloptions.out b/src/test/regress/expected/reloptions.out
new file mode 100644
index 0000000..835d72f
--- /dev/null
+++ b/src/test/regress/expected/reloptions.out
@@ -0,0 +1,193 @@
+-- Simple create
+CREATE TABLE reloptions_test(i INT) WITH (fillfactor=30,autovacuum_enabled = false, autovacuum_analyze_scale_factor = 0.2);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+ reloptions
+------------------------------------------------------------------------------
+ {fillfactor=30,autovacuum_enabled=false,autovacuum_analyze_scale_factor=0.2}
+(1 row)
+
+-- Test сase insensitive
+CREATE TABLE reloptions_test3(i INT) WITH (FiLlFaCtoR=40);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test3'::regclass;
+ reloptions
+-----------------
+ {fillfactor=40}
+(1 row)
+
+-- Fail on min/max values check
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=2);
+ERROR: value 2 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=110);
+ERROR: value 110 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor = -10.0);
+ERROR: value -10.0 out of bounds for option "autovacuum_analyze_scale_factor"
+DETAIL: Valid values are between "0.000000" and "100.000000".
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor = 110.0);
+ERROR: value 110.0 out of bounds for option "autovacuum_analyze_scale_factor"
+DETAIL: Valid values are between "0.000000" and "100.000000".
+-- Fail when option and namespase do not exist
+CREATE TABLE reloptions_test2(i INT) WITH (not_existing_option=2);
+ERROR: unrecognized parameter "not_existing_option"
+CREATE TABLE reloptions_test2(i INT) WITH (not_existing_namespace.fillfactor=2);
+ERROR: unrecognized parameter namespace "not_existing_namespace"
+-- Fail while setting unproper value
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=30.5);
+ERROR: invalid value for integer option "fillfactor": 30.5
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor='string');
+ERROR: invalid value for integer option "fillfactor": string
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=true);
+ERROR: invalid value for integer option "fillfactor": true
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled=12);
+ERROR: invalid value for boolean option "autovacuum_enabled": 12
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled=30.5);
+ERROR: invalid value for boolean option "autovacuum_enabled": 30.5
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled='string');
+ERROR: invalid value for boolean option "autovacuum_enabled": string
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor='string');
+ERROR: invalid value for floating point option "autovacuum_analyze_scale_factor": string
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor=true);
+ERROR: invalid value for floating point option "autovacuum_analyze_scale_factor": true
+-- Specifing name only should fail as if there was =ture after it
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor);
+ERROR: invalid value for integer option "fillfactor": true
+-- Simple ALTER TABLE
+ALTER TABLE reloptions_test SET (fillfactor=31, autovacuum_analyze_scale_factor = 0.3);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+ reloptions
+------------------------------------------------------------------------------
+ {autovacuum_enabled=false,fillfactor=31,autovacuum_analyze_scale_factor=0.3}
+(1 row)
+
+-- Check that we cat set boolean option to true just by mentioning it
+ALTER TABLE reloptions_test SET (autovacuum_enabled, fillfactor=32);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+ reloptions
+-----------------------------------------------------------------------------
+ {autovacuum_analyze_scale_factor=0.3,autovacuum_enabled=true,fillfactor=32}
+(1 row)
+
+-- Check that RESET works well
+ALTER TABLE reloptions_test RESET (fillfactor);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+ reloptions
+---------------------------------------------------------------
+ {autovacuum_analyze_scale_factor=0.3,autovacuum_enabled=true}
+(1 row)
+
+-- Check that RESETting all values make NULL reloptions record in pg_class
+ALTER TABLE reloptions_test RESET (autovacuum_enabled, autovacuum_analyze_scale_factor);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass AND reloptions IS NULL;
+ reloptions
+------------
+
+(1 row)
+
+-- Check RESET fails on att=value
+ALTER TABLE reloptions_test RESET (fillfactor=12);
+ERROR: RESET must not include values for parameters
+-- Check oids options is ignored
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test(i INT) WITH (fillfactor=20, oids=true);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+ reloptions
+-----------------
+ {fillfactor=20}
+(1 row)
+
+-- Now testing toast.* options
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test (s VARCHAR) WITH (toast.autovacuum_vacuum_cost_delay = 23 );
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+ reloptions
+-----------------------------------
+ {autovacuum_vacuum_cost_delay=23}
+(1 row)
+
+ALTER TABLE reloptions_test SET (toast.autovacuum_vacuum_cost_delay = 24);
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+ reloptions
+-----------------------------------
+ {autovacuum_vacuum_cost_delay=24}
+(1 row)
+
+ALTER TABLE reloptions_test RESET (toast.autovacuum_vacuum_cost_delay);
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+ reloptions
+------------
+
+(1 row)
+
+-- Fail on unexisting options in toast namespace
+CREATE TABLE reloptions_test2 (i int) WITH (toast.not_existing_option = 42 );
+ERROR: unrecognized parameter "toast.not_existing_option"
+-- Fail on setting reloption to a table that does not have a TOAST relation
+CREATE TABLE reloptions_test2 (i int) WITH (toast.autovacuum_vacuum_cost_delay = 23 );
+ERROR: no TOAST relation was created for a table. Can't set toast.* storage parameters
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test(i INT);
+ALTER TABLE reloptions_test SET (toast.autovacuum_vacuum_cost_delay = 23);
+ERROR: TOAST relation for this table does not exist. Can't change toast.* storage parameters
+ALTER TABLE reloptions_test RESET (toast.autovacuum_vacuum_cost_delay);
+ERROR: TOAST relation for this table does not exist. Can't change toast.* storage parameters
+-- autovacuum_analyze_scale_factor and autovacuum_analyze_threshold should be
+-- accepted by heap but rejected by toast (special case)
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test (s VARCHAR) WITH (autovacuum_analyze_scale_factor=1, autovacuum_analyze_threshold=1);
+CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analyze_scale_factor=1);
+ERROR: unrecognized parameter "toast.autovacuum_analyze_scale_factor"
+CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analyze_threshold=1);
+ERROR: unrecognized parameter "toast.autovacuum_analyze_threshold"
+-- And now mixed toast + heap
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test (s VARCHAR) WITH (toast.autovacuum_vacuum_cost_delay = 23, autovacuum_vacuum_cost_delay = 24, fillfactor = 40);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+ reloptions
+-------------------------------------------------
+ {autovacuum_vacuum_cost_delay=24,fillfactor=40}
+(1 row)
+
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+ reloptions
+-----------------------------------
+ {autovacuum_vacuum_cost_delay=23}
+(1 row)
+
+-- Same FOR CREATE and ALTER INDEX for btree indexes
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (fillfactor=30);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx'::regclass;
+ reloptions
+-----------------
+ {fillfactor=30}
+(1 row)
+
+-- Fail when option and namespase do not exist
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_existing_option=2);
+ERROR: unrecognized parameter "not_existing_option"
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_existing_option.fillfactor=2);
+ERROR: unrecognized parameter namespace "not_existing_option"
+-- Check ranges
+CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfactor=1);
+ERROR: value 1 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfactor=130);
+ERROR: value 130 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+-- Check alter
+ALTER INDEX reloptions_test_idx SET (fillfactor=40);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx'::regclass;
+ reloptions
+-----------------
+ {fillfactor=40}
+(1 row)
+
+-- Check alter on empty relop list
+CREATE INDEX reloptions_test_idx3 ON reloptions_test (s);
+ALTER INDEX reloptions_test_idx3 SET (fillfactor=40);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx3'::regclass;
+ reloptions
+-----------------
+ {fillfactor=40}
+(1 row)
+
diff --git a/src/test/regress/expected/spgist.out b/src/test/regress/expected/spgist.out
index 0691e91..bb58fb7 100644
--- a/src/test/regress/expected/spgist.out
+++ b/src/test/regress/expected/spgist.out
@@ -4,7 +4,7 @@
-- There are other tests to test different SP-GiST opclasses. This is for
-- testing SP-GiST code itself.
create table spgist_point_tbl(id int4, p point);
-create index spgist_point_idx on spgist_point_tbl using spgist(p);
+create index spgist_point_idx on spgist_point_tbl using spgist(p) with (fillfactor = 75);
-- Test vacuum-root operation. It gets invoked when the root is also a leaf,
-- i.e. the index is very small.
insert into spgist_point_tbl (id, p)
@@ -37,3 +37,12 @@ select g, 'baaaaaaaaaaaaaar' || g from generate_series(1, 1000) g;
-- tuple to be moved to another page.
insert into spgist_text_tbl (id, t)
select -g, 'f' || repeat('o', 100-g) || 'surprise' from generate_series(1, 100) g;
+-- Check reloptions min, max values
+create index spgist_point_idx2 on spgist_point_tbl using spgist(p) with (fillfactor = 9);
+ERROR: value 9 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+create index spgist_point_idx2 on spgist_point_tbl using spgist(p) with (fillfactor = 101);
+ERROR: value 101 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+-- ALTER reloption should work
+alter index spgist_point_idx set (fillfactor = 76);
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out
index aa06d1d..287dd84 100644
--- a/src/test/regress/expected/updatable_views.out
+++ b/src/test/regress/expected/updatable_views.out
@@ -1551,7 +1551,7 @@ SELECT * FROM base_tbl;
ALTER VIEW rw_view1 SET (check_option=here); -- invalid
ERROR: invalid value for "check_option" option
-DETAIL: Valid values are "local" and "cascaded".
+DETAIL: Valid values are "local", "cascaded".
ALTER VIEW rw_view1 SET (check_option=local);
INSERT INTO rw_view2 VALUES (-20); -- should fail
ERROR: new row violates check option for view "rw_view1"
diff --git a/src/test/regress/input/tablespace.source b/src/test/regress/input/tablespace.source
index 03a62bd..7f7934b 100644
--- a/src/test/regress/input/tablespace.source
+++ b/src/test/regress/input/tablespace.source
@@ -12,10 +12,10 @@ DROP TABLESPACE regress_tblspacewith;
CREATE TABLESPACE regress_tblspace LOCATION '@testtablespace@';
-- try setting and resetting some properties for the new tablespace
-ALTER TABLESPACE regress_tblspace SET (random_page_cost = 1.0);
+ALTER TABLESPACE regress_tblspace SET (random_page_cost = 1.0, seq_page_cost = 1.1);
ALTER TABLESPACE regress_tblspace SET (some_nonexistent_parameter = true); -- fail
ALTER TABLESPACE regress_tblspace RESET (random_page_cost = 2.0); -- fail
-ALTER TABLESPACE regress_tblspace RESET (random_page_cost, seq_page_cost); -- ok
+ALTER TABLESPACE regress_tblspace RESET (random_page_cost, effective_io_concurrency); -- ok
-- create a schema we can use
CREATE SCHEMA testschema;
diff --git a/src/test/regress/output/tablespace.source b/src/test/regress/output/tablespace.source
index aaedf5f..2443511 100644
--- a/src/test/regress/output/tablespace.source
+++ b/src/test/regress/output/tablespace.source
@@ -14,12 +14,12 @@ DROP TABLESPACE regress_tblspacewith;
-- create a tablespace we can use
CREATE TABLESPACE regress_tblspace LOCATION '@testtablespace@';
-- try setting and resetting some properties for the new tablespace
-ALTER TABLESPACE regress_tblspace SET (random_page_cost = 1.0);
+ALTER TABLESPACE regress_tblspace SET (random_page_cost = 1.0, seq_page_cost = 1.1);
ALTER TABLESPACE regress_tblspace SET (some_nonexistent_parameter = true); -- fail
ERROR: unrecognized parameter "some_nonexistent_parameter"
ALTER TABLESPACE regress_tblspace RESET (random_page_cost = 2.0); -- fail
ERROR: RESET must not include values for parameters
-ALTER TABLESPACE regress_tblspace RESET (random_page_cost, seq_page_cost); -- ok
+ALTER TABLESPACE regress_tblspace RESET (random_page_cost, effective_io_concurrency); -- ok
-- create a schema we can use
CREATE SCHEMA testschema;
-- try a table
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index ea7b5b4..96157a1 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -109,7 +109,7 @@ test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combo
# NB: temp.sql does a reconnect which transiently uses 2 connections,
# so keep this parallel group to at most 19 tests
# ----------
-test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml
+test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml reloptions
# event triggers cannot run concurrently with any test that runs DDL
test: event_trigger
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index cf48ea7..d8b4517 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -174,5 +174,6 @@ test: returning
test: largeobject
test: with
test: xml
+test: reloptions
test: event_trigger
test: stats
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index 8cd6786..bd59482 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -2228,3 +2228,11 @@ alter table p attach partition p1 for values from (1, 2) to (1, 10);
-- cleanup
drop table p;
drop table p1;
+
+-- test alter column options
+CREATE TABLE tmp(i integer);
+INSERT INTO tmp VALUES (1);
+ALTER TABLE tmp ALTER COLUMN i SET (n_distinct = 1, n_distinct_inherited = 2);
+ALTER TABLE tmp ALTER COLUMN i RESET (n_distinct_inherited);
+ANALYZE tmp;
+DROP TABLE tmp;
diff --git a/src/test/regress/sql/brin.sql b/src/test/regress/sql/brin.sql
index 5bf5387..6353c82 100644
--- a/src/test/regress/sql/brin.sql
+++ b/src/test/regress/sql/brin.sql
@@ -409,3 +409,7 @@ UPDATE brintest SET textcol = '' WHERE textcol IS NOT NULL;
SELECT brin_summarize_new_values('brintest'); -- error, not an index
SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index
SELECT brin_summarize_new_values('brinidx'); -- ok, no change expected
+
+-- Check that changing reloptions for brin index is not allowed
+ALTER INDEX brinidx SET (pages_per_range = 10);
+ALTER INDEX brinidx RESET (pages_per_range);
diff --git a/src/test/regress/sql/create_index.sql b/src/test/regress/sql/create_index.sql
index 1648072..1f8d101 100644
--- a/src/test/regress/sql/create_index.sql
+++ b/src/test/regress/sql/create_index.sql
@@ -682,7 +682,7 @@ CREATE INDEX hash_name_index ON hash_name_heap USING hash (random name_ops);
CREATE INDEX hash_txt_index ON hash_txt_heap USING hash (random text_ops);
-CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_ops);
+CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=60);
CREATE UNLOGGED TABLE unlogged_hash_table (id int4);
CREATE INDEX unlogged_hash_index ON unlogged_hash_table USING hash (id int4_ops);
diff --git a/src/test/regress/sql/gist.sql b/src/test/regress/sql/gist.sql
index 9d4ff1e..ce1ac1a 100644
--- a/src/test/regress/sql/gist.sql
+++ b/src/test/regress/sql/gist.sql
@@ -17,6 +17,17 @@ select g+100000, point(g*10+1, g*10+1) from generate_series(1, 10000) g;
-- To test vacuum, delete some entries from all over the index.
delete from gist_point_tbl where id % 2 = 1;
+-- Test buffering and fillfactor reloption takes valid values
+create index gist_pointidx2 on gist_point_tbl using gist(p) with (buffering = on, fillfactor=50);
+create index gist_pointidx3 on gist_point_tbl using gist(p) with (buffering = off);
+create index gist_pointidx4 on gist_point_tbl using gist(p) with (buffering = auto);
+--Test buffering and fillfactor for refising invalid values
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (buffering = invalid_value);
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfactor=9);
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfactor=101);
+
+
+
-- And also delete some concentration of values. (GiST doesn't currently
-- attempt to delete pages even when they become empty, but if it did, this
-- would exercise it)
diff --git a/src/test/regress/sql/hash_index.sql b/src/test/regress/sql/hash_index.sql
index 15a3b06..b034efb 100644
--- a/src/test/regress/sql/hash_index.sql
+++ b/src/test/regress/sql/hash_index.sql
@@ -194,3 +194,8 @@ CREATE TABLE hash_heap_float4 (x float4, y int);
INSERT INTO hash_heap_float4 VALUES (1.1,1);
CREATE INDEX hash_idx ON hash_heap_float4 USING hash (x);
DROP TABLE hash_heap_float4 CASCADE;
+
+-- Check reloptions min max values and that it is not allowed to ALTER
+CREATE INDEX hash_f8_index2 ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=9);
+CREATE INDEX hash_f8_index2 ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=101);
+ALTER INDEX hash_f8_index SET (fillfactor=99);
\ No newline at end of file
diff --git a/src/test/regress/sql/reloptions.sql b/src/test/regress/sql/reloptions.sql
new file mode 100644
index 0000000..0bfec28
--- /dev/null
+++ b/src/test/regress/sql/reloptions.sql
@@ -0,0 +1,153 @@
+
+-- Simple create
+CREATE TABLE reloptions_test(i INT) WITH (fillfactor=30,autovacuum_enabled = false, autovacuum_analyze_scale_factor = 0.2);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+
+-- Test сase insensitive
+CREATE TABLE reloptions_test3(i INT) WITH (FiLlFaCtoR=40);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test3'::regclass;
+
+-- Fail on min/max values check
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=2);
+
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=110);
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor = -10.0);
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor = 110.0);
+
+-- Fail when option and namespase do not exist
+
+CREATE TABLE reloptions_test2(i INT) WITH (not_existing_option=2);
+
+CREATE TABLE reloptions_test2(i INT) WITH (not_existing_namespace.fillfactor=2);
+
+-- Fail while setting unproper value
+
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=30.5);
+
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor='string');
+
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=true);
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled=12);
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled=30.5);
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled='string');
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor='string');
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor=true);
+
+-- Specifing name only should fail as if there was =ture after it
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor);
+
+-- Simple ALTER TABLE
+
+ALTER TABLE reloptions_test SET (fillfactor=31, autovacuum_analyze_scale_factor = 0.3);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+
+-- Check that we cat set boolean option to true just by mentioning it
+
+ALTER TABLE reloptions_test SET (autovacuum_enabled, fillfactor=32);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+
+-- Check that RESET works well
+
+ALTER TABLE reloptions_test RESET (fillfactor);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+
+-- Check that RESETting all values make NULL reloptions record in pg_class
+ALTER TABLE reloptions_test RESET (autovacuum_enabled, autovacuum_analyze_scale_factor);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass AND reloptions IS NULL;
+
+-- Check RESET fails on att=value
+
+ALTER TABLE reloptions_test RESET (fillfactor=12);
+
+-- Check oids options is ignored
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test(i INT) WITH (fillfactor=20, oids=true);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+
+-- Now testing toast.* options
+DROP TABLE reloptions_test;
+
+CREATE TABLE reloptions_test (s VARCHAR) WITH (toast.autovacuum_vacuum_cost_delay = 23 );
+
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+
+ALTER TABLE reloptions_test SET (toast.autovacuum_vacuum_cost_delay = 24);
+
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+
+ALTER TABLE reloptions_test RESET (toast.autovacuum_vacuum_cost_delay);
+
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+
+-- Fail on unexisting options in toast namespace
+CREATE TABLE reloptions_test2 (i int) WITH (toast.not_existing_option = 42 );
+
+-- Fail on setting reloption to a table that does not have a TOAST relation
+CREATE TABLE reloptions_test2 (i int) WITH (toast.autovacuum_vacuum_cost_delay = 23 );
+DROP TABLE reloptions_test;
+
+CREATE TABLE reloptions_test(i INT);
+ALTER TABLE reloptions_test SET (toast.autovacuum_vacuum_cost_delay = 23);
+ALTER TABLE reloptions_test RESET (toast.autovacuum_vacuum_cost_delay);
+
+-- autovacuum_analyze_scale_factor and autovacuum_analyze_threshold should be
+-- accepted by heap but rejected by toast (special case)
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test (s VARCHAR) WITH (autovacuum_analyze_scale_factor=1, autovacuum_analyze_threshold=1);
+
+CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analyze_scale_factor=1);
+CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analyze_threshold=1);
+
+-- And now mixed toast + heap
+DROP TABLE reloptions_test;
+
+CREATE TABLE reloptions_test (s VARCHAR) WITH (toast.autovacuum_vacuum_cost_delay = 23, autovacuum_vacuum_cost_delay = 24, fillfactor = 40);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+
+-- Same FOR CREATE and ALTER INDEX for btree indexes
+
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (fillfactor=30);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx'::regclass;
+
+-- Fail when option and namespase do not exist
+
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_existing_option=2);
+
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_existing_option.fillfactor=2);
+
+-- Check ranges
+
+CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfactor=1);
+
+CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfactor=130);
+
+-- Check alter
+
+ALTER INDEX reloptions_test_idx SET (fillfactor=40);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx'::regclass;
+
+-- Check alter on empty relop list
+
+CREATE INDEX reloptions_test_idx3 ON reloptions_test (s);
+
+ALTER INDEX reloptions_test_idx3 SET (fillfactor=40);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx3'::regclass;
+
+
diff --git a/src/test/regress/sql/spgist.sql b/src/test/regress/sql/spgist.sql
index 5896b50..8d26e49 100644
--- a/src/test/regress/sql/spgist.sql
+++ b/src/test/regress/sql/spgist.sql
@@ -5,7 +5,7 @@
-- testing SP-GiST code itself.
create table spgist_point_tbl(id int4, p point);
-create index spgist_point_idx on spgist_point_tbl using spgist(p);
+create index spgist_point_idx on spgist_point_tbl using spgist(p) with (fillfactor = 75);
-- Test vacuum-root operation. It gets invoked when the root is also a leaf,
-- i.e. the index is very small.
@@ -48,3 +48,10 @@ select g, 'baaaaaaaaaaaaaar' || g from generate_series(1, 1000) g;
-- tuple to be moved to another page.
insert into spgist_text_tbl (id, t)
select -g, 'f' || repeat('o', 100-g) || 'surprise' from generate_series(1, 100) g;
+
+-- Check reloptions min, max values
+create index spgist_point_idx2 on spgist_point_tbl using spgist(p) with (fillfactor = 9);
+create index spgist_point_idx2 on spgist_point_tbl using spgist(p) with (fillfactor = 101);
+
+-- ALTER reloption should work
+alter index spgist_point_idx set (fillfactor = 76);
I gave this patch a quick skim. At first I was confused by the term
"catalog"; I thought it meant we stored options in a system table. But
that's not what is meant at all; instead, what we do is build these
"catalogs" in memory. Maybe a different term could have been used, but
I'm not sure it's stricly necessary. I wouldn't really bother.
I'm confused by the "no ALTER, no lock" rule. Does it mean that if
"ALTER..SET" is forbidden? Because I noticed that brin's
pages_per_range is marked as such, but we do allow that option to change
with ALTER..SET, so there's at least one bug there, and I would be
surprised if there aren't any others.
Please make sure to mark functions as static (e.g. bringetreloptcatalog).
Why not pass ->struct_size and ->postprocess_fun (which I'd rename it as
->postprocess_fn, more in line with our naming style) as a parameter to
allocateOptionsCatalog? Also, to avoid repalloc() in most cases (and to
avoid pallocing more options that you're going to need in a bunch of
cases, perhaps that function should the number of times you expect to
call AddItems for that catalog (since you do it immediately afterwards
in all cases I can see), and allocate that number. If later another
item arrives, then repalloc using the same code you already have in
AddItems().
Something is wrong with leading whitespace in many places; either you
added too many tabs, or the wrong number spaces; not sure which but
visually it's clearly wrong. ... Actually there are whitespace-style
violations in several places; please fix using pgindent (after adding
any new typedefs your defining to typedefs.list).
--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
By the way, it would be very good if you could review some patches, too.
--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Copying Fabr�zio Mello here, who spent some time trying to work on
reloptions too. He may have something to say about the new
functionality that this patch provides.
--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Thu, Mar 23, 2017 at 3:58 PM, Alvaro Herrera <alvherre@2ndquadrant.com>
wrote:
Copying Fabrízio Mello here, who spent some time trying to work on
reloptions too. He may have something to say about the new
functionality that this patch provides.
Thanks Álvaro, I'll look the patch and try to help in some way.
Regards,
--
Fabrízio de Royes Mello Timbira - http://www.timbira.com.br/
PostgreSQL: Consultoria, Desenvolvimento, Suporte 24x7 e Treinamento
В письме от 17 марта 2017 14:21:26 пользователь Alvaro Herrera написал:
I gave this patch a quick skim.
Thanks!
At first I was confused by the term
"catalog"; I thought it meant we stored options in a system table. But
that's not what is meant at all; instead, what we do is build these
"catalogs" in memory. Maybe a different term could have been used, but
I'm not sure it's stricly necessary. I wouldn't really bother.
Yes "catalog" is quite confusing. I did not find better name while I was
writing the code, so I take first one, that came into my mind.
If you, or somebody else, have better idea how to call this sets of options
definitions I will gladly rename it, as catalog is a bad name. May be
OptionsDefSet instead of OptionsCatalog?
I'm confused by the "no ALTER, no lock" rule. Does it mean that if
"ALTER..SET" is forbidden? Because I noticed that brin's
pages_per_range is marked as such, but we do allow that option to change
with ALTER..SET, so there's at least one bug there, and I would be
surprised if there aren't any others.
If you grep, for example, gist index code for "buffering_mode" option, you will
see, that this option is used only in gistbuild.c. There it is written into
the meta page, and afterwards, value from meta page is used, and one from
options, is just ignored.
Nowdays you can successfully alter this value, but this will not affect
anything until index is recreated... I thought it is very confusing behavior
and decided that we should just forbid such alters.
Please make sure to mark functions as static (e.g. bringetreloptcatalog).
Ok. Will do.
Why not pass ->struct_size and ->postprocess_fun (which I'd rename it as
->postprocess_fn, more in line with our naming style) as a parameter to
allocateOptionsCatalog?
struct_size -- very good idea!
postprocess_fn -- now it is rarely used. In most cases it is NULL. May be it
would be ok to set it afterwards in that rare cases when it is needed.
Also, to avoid repalloc() in most cases (and to
avoid pallocing more options that you're going to need in a bunch of
cases, perhaps that function should the number of times you expect to
call AddItems for that catalog (since you do it immediately afterwards
in all cases I can see), and allocate that number. If later another
item arrives, then repalloc using the same code you already have in
AddItems().
I've copied this code from reloptions code for custom options. Without much
thinking about it.
If I would think about it now: we always know how many options we will have.
So we can just pass this number to palloc and assert if somebody adds more
options then expected... What do yo think about it.
Something is wrong with leading whitespace in many places; either you
added too many tabs, or the wrong number spaces; not sure which but
visually it's clearly wrong. ... Actually there are whitespace-style
violations in several places; please fix using pgindent (after adding
any new typedefs your defining to typedefs.list).
I will run pgindent on my code.
--
Nikolay Shaplov, independent Perl & C/C++ developer. Available for hire.
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Nikolay Shaplov wrote:
If I would think about it now: we always know how many options we will have.
So we can just pass this number to palloc and assert if somebody adds more
options then expected... What do yo think about it.
I think we need to preserve the ability to add custom options, because
extensions may want to do that.
--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
В письме от 23 марта 2017 16:14:58 пользователь Fabrízio de Royes Mello
написал:
On Thu, Mar 23, 2017 at 3:58 PM, Alvaro Herrera <alvherre@2ndquadrant.com>
wrote:
Copying Fabrízio Mello here, who spent some time trying to work on
reloptions too. He may have something to say about the new
functionality that this patch provides.Thanks Álvaro, I'll look the patch and try to help in some way.
Thank you, that would be great!
--
Nikolay Shaplov, independent Perl & C/C++ developer. Available for hire.
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
В письме от 26 марта 2017 15:02:12 Вы написали:
Nikolay Shaplov wrote:
If I would think about it now: we always know how many options we will
have. So we can just pass this number to palloc and assert if somebody
adds more options then expected... What do yo think about it.I think we need to preserve the ability to add custom options, because
extensions may want to do that.
Ok. At least this will not do any harm :-)
--
Nikolay Shaplov, independent Perl & C/C++ developer. Available for hire.
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
В письме от 17 марта 2017 14:21:26 пользователь Alvaro Herrera написал:
Please make sure to mark functions as static (e.g. bringetreloptcatalog).
I am a bit confused here:
For brin and nbtree this is a good idea: brin.c and nbtree.c has AM-handler
inside it, and there are other static functions there. Adding one more static
function here seems to be quite logical.
For gin, gist and spgist, authors seems to use [index_name]_private.h files to
hide internal functions from outside code. In ginutil.c and spgutils.c, where
AM-handler is located, there is no static functions at all... gist.c has, but
I think I should write similar code for all *_private.h indexes.
So I think it wold be good to hide catalog function via *_pricate.h include
file for these three indexes.
hash.c is quite a mess...
There is no hash_private.h, AM-handles is located in hash.c, that has "This
file contains only the public interface routines." comment at the beginning,
and there is no static functions inside. I do not know what is the right way
to hide hashgetreloptcatalog function here...
What would you advise?
--
Nikolay Shaplov, independent Perl & C/C++ developer. Available for hire.
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Nikolay Shaplov wrote:
В письме от 17 марта 2017 14:21:26 пользователь Alvaro Herrera написал:
Please make sure to mark functions as static (e.g. bringetreloptcatalog).
I am a bit confused here:
For brin and nbtree this is a good idea: brin.c and nbtree.c has AM-handler
inside it, and there are other static functions there. Adding one more static
function here seems to be quite logical.
I am just saying that if there is a function used only in one
compilation unit (.c file) then let's make sure its prototype says
"static".
For gin, gist and spgist, authors seems to use [index_name]_private.h files to
hide internal functions from outside code. In ginutil.c and spgutils.c, where
AM-handler is located, there is no static functions at all... gist.c has, but
I think I should write similar code for all *_private.h indexes.
Sure.
hash.c is quite a mess...
There is no hash_private.h, AM-handles is located in hash.c, that has "This
file contains only the public interface routines." comment at the beginning,
and there is no static functions inside. I do not know what is the right way
to hide hashgetreloptcatalog function here...What would you advise?
Leave it where it is (hashutil.c), no static marker. We may want to
move things around later, but that's not your patch's responsibility.
--
Álvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
В письме от 26 марта 2017 15:02:12 пользователь Alvaro Herrera написал:
Nikolay Shaplov wrote:
If I would think about it now: we always know how many options we will
have. So we can just pass this number to palloc and assert if somebody
adds more options then expected... What do yo think about it.I think we need to preserve the ability to add custom options, because
extensions may want to do that.
I've been thinking about it for a while... I think this might lead to reducing
the quality of the code...
For example: There was 10 options for some relation type. I decided to add one
more option, but i did not ++ the number of options for
allocateOptionsCatalog. So space for 10 options were allocated, and then when
11th option is added, optionCatalogAddItem would allocate space for ten more
options, and nine of them would not be used. And nobody will notice it.
So, I see here four solutions:
0. Leave it as it was. (We both do not like it)
1. Forbid dynamic number of options (you do not like it)
2. Allow dynamic number of options only for special cases, and in all other
cases make them strict, and asserting if option number is wrong. This can be
done in two ways:
2a. Add strict boolean flag, that tells if allow to add more options or not
2b. Do not add any flags, but if number of items is specified, then process
number of items in strict mode. If it is set to -1, then do as it is done now,
totally dynamically.
I would prefer 2b, if you sure that somebody will need dynamic number of
options.
PS. I hardly believe that there can be dynamic number of options, as this
options later are mapped into C-structure that is quite static. No use case
comes into my mind, where I would like to have dynamic number of options, not
knowing at build time, how many of them there would be.
--
Nikolay Shaplov, independent Perl & C/C++ developer. Available for hire.
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Hi!
Here is a new version of the patch.
I've run pgindent on all my code.
I've declared static all functions I can.
I've moved size of Bytea to the arguments of allocateOptionsCatalog, and I
also pass expected number of catalog items there. If it is positive, it will
be treated as strict number of items. If it is -1, then new items will be
dynamically reparroced when needed.
--
Nikolay Shaplov, independent Perl & C/C++ developer. Available for hire.
Attachments:
reloptions7.difftext/x-patch; charset=UTF-8; name=reloptions7.diffDownload
diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
index 0cfe49a..96edc9a 100644
--- a/contrib/bloom/bloom.h
+++ b/contrib/bloom/bloom.h
@@ -204,7 +204,6 @@ extern IndexBulkDeleteResult *blbulkdelete(IndexVacuumInfo *info,
void *callback_state);
extern IndexBulkDeleteResult *blvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
-extern bytea *bloptions(Datum reloptions, bool validate);
extern void blcostestimate(PlannerInfo *root, IndexPath *path,
double loop_count, Cost *indexStartupCost,
Cost *indexTotalCost, Selectivity *indexSelectivity,
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.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 cbc50f7..7e3bbc0 100644
--- a/contrib/bloom/expected/bloom.out
+++ b/contrib/bloom/expected/bloom.out
@@ -210,3 +210,37 @@ ORDER BY 1;
text_ops | t
(2 rows)
+-- reloptions test
+DROP INDEX bloomidx;
+CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (length=7, col1 = 4);
+SELECT reloptions FROM pg_class WHERE oid = 'bloomidx'::regclass;
+ reloptions
+-------------------
+ {length=7,col1=4}
+(1 row)
+
+-- check for min and max values
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=0);
+ERROR: value 0 out of bounds for option "length"
+DETAIL: Valid values are between "1" and "4096".
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=4097);
+ERROR: value 4097 out of bounds for option "length"
+DETAIL: Valid values are between "1" and "4096".
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=0);
+ERROR: value 0 out of bounds for option "col1"
+DETAIL: Valid values are between "1" and "4095".
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=4096);
+ERROR: value 4096 out of bounds for option "col1"
+DETAIL: Valid values are between "1" and "4095".
+-- check post_validate for colN<lengh
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=10,col1=11);
+ERROR: col0 should not be grater than length
+-- check that ALTERing of reloptions is forbidden
+ALTER INDEX bloomidx SET (length=4);
+ERROR: changing parameter "length" is not allowed
+ALTER INDEX bloomidx SET (col1=4);
+ERROR: changing parameter "col1" is not allowed
+ALTER INDEX bloomidx RESET (length);
+ERROR: changing parameter "length" is not allowed
+ALTER INDEX bloomidx RESET (col1);
+ERROR: changing parameter "col1" is not allowed
diff --git a/contrib/bloom/sql/bloom.sql b/contrib/bloom/sql/bloom.sql
index 2227460..dbac21c 100644
--- a/contrib/bloom/sql/bloom.sql
+++ b/contrib/bloom/sql/bloom.sql
@@ -81,3 +81,23 @@ SELECT opcname, amvalidate(opc.oid)
FROM pg_opclass opc JOIN pg_am am ON am.oid = opcmethod
WHERE amname = 'bloom'
ORDER BY 1;
+
+-- reloptions test
+
+DROP INDEX bloomidx;
+CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (length=7, col1 = 4);
+SELECT reloptions FROM pg_class WHERE oid = 'bloomidx'::regclass;
+-- check for min and max values
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=0);
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=4097);
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=0);
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=4096);
+-- check post_validate for colN<lengh
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=10,col1=11);
+
+-- check that ALTERing of reloptions is forbidden
+
+ALTER INDEX bloomidx SET (length=4);
+ALTER INDEX bloomidx SET (col1=4);
+ALTER INDEX bloomidx RESET (length);
+ALTER INDEX bloomidx RESET (col1);
diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
index 1d5555a..87dc630 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -1955,7 +1955,7 @@ PG_FUNCTION_INFO_V1(dblink_fdw_validator);
Datum
dblink_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid context = PG_GETARG_OID(1);
ListCell *cell;
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 277639f..876b8bb 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -193,7 +193,7 @@ file_fdw_handler(PG_FUNCTION_ARGS)
Datum
file_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid catalog = PG_GETARG_OID(1);
char *filename = NULL;
DefElem *force_not_null = NULL;
diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c
index e24db56..be15189 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -65,7 +65,7 @@ PG_FUNCTION_INFO_V1(postgres_fdw_validator);
Datum
postgres_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid catalog = PG_GETARG_OID(1);
ListCell *cell;
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index b22563b..4cb4f32 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"
@@ -70,6 +70,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
@@ -103,7 +104,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;
@@ -113,6 +113,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;
@@ -756,36 +757,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)}
- };
-
- 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.
*/
@@ -1235,3 +1206,26 @@ 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), 1);
+
+ 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);
+ }
+ return brin_relopt_catalog;
+}
diff --git a/src/backend/access/common/Makefile b/src/backend/access/common/Makefile
index fb27944..c9fb063 100644
--- a/src/backend/access/common/Makefile
+++ b/src/backend/access/common/Makefile
@@ -12,7 +12,7 @@ subdir = src/backend/access/common
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
-OBJS = bufmask.o heaptuple.o indextuple.o printsimple.o printtup.o \
+OBJS = bufmask.o heaptuple.o indextuple.o options.o printsimple.o printtup.o \
reloptions.o scankey.o tupconvert.o tupdesc.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/common/options.c b/src/backend/access/common/options.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 72e1253..6b9477b 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,919 +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[] =
-{
- {
- {
- "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.
@@ -945,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,
@@ -965,16 +66,19 @@ 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;
@@ -985,528 +89,296 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
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.
- *
- * 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;
- 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)
- {
- *numrelopts = 0;
- return NULL;
- }
-
- 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)}
- };
-
- 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);
+static options_catalog *heap_relopt_catalog = NULL;
- 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;
+static options_catalog *toast_relopt_catalog = NULL;
- vopts = allocateReloptStruct(sizeof(ViewOptions), options, numoptions);
-
- 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:
- case RELKIND_PARTITIONED_TABLE:
- return default_reloptions(reloptions, validate, RELOPT_KIND_HEAP);
- 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);
+
+ optionsCatalogAddItemBool(view_relopt_catalog, "security_barrier",
+ "View acts as a row security barrier",
+ AccessExclusiveLock,
+ 0, offsetof(ViewOptions, security_barrier), false);
- return amoptions(reloptions, validate);
+ 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);
+static options_catalog *attribute_options_catalog = NULL;
- /* if none set, we're done */
- if (numoptions == 0)
- return NULL;
-
- aopts = allocateReloptStruct(sizeof(AttributeOpts), options, numoptions);
-
- 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);
+static options_catalog *tablespace_options_catalog = NULL;
- fillRelOptions((void *) tsopts, sizeof(TableSpaceOpts), options, numoptions,
- validate, tab, lengthof(tab));
-
- pfree(options);
-
- 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;
}
/*
@@ -1516,33 +388,53 @@ 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;
+ 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 d03d59d..a942738 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -16,7 +16,7 @@
#include "access/gin_private.h"
#include "access/ginxlog.h"
-#include "access/reloptions.h"
+#include "access/options.h"
#include "access/xloginsert.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_type.h"
@@ -26,7 +26,8 @@
#include "utils/builtins.h"
#include "utils/index_selfuncs.h"
#include "utils/typcache.h"
-
+#include "utils/memutils.h"
+#include "utils/guc.h"
/*
* GIN handler function: return IndexAmRoutine with access method parameters
@@ -60,7 +61,6 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amvacuumcleanup = ginvacuumcleanup;
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = gincostestimate;
- amroutine->amoptions = ginoptions;
amroutine->amproperty = NULL;
amroutine->amvalidate = ginvalidate;
amroutine->ambeginscan = ginbeginscan;
@@ -70,6 +70,7 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amendscan = ginendscan;
amroutine->ammarkpos = NULL;
amroutine->amrestrpos = NULL;
+ amroutine->amrelopt_catalog = gingetreloptcatalog;
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
@@ -593,35 +594,6 @@ ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
return entries;
}
-bytea *
-ginoptions(Datum reloptions, bool validate)
-{
- relopt_value *options;
- GinOptions *rdopts;
- int numoptions;
- static const relopt_parse_elt tab[] = {
- {"fastupdate", RELOPT_TYPE_BOOL, offsetof(GinOptions, useFastUpdate)},
- {"gin_pending_list_limit", RELOPT_TYPE_INT, offsetof(GinOptions,
- pendingListCleanupSize)}
- };
-
- options = parseRelOptions(reloptions, validate, RELOPT_KIND_GIN,
- &numoptions);
-
- /* if none set, we're done */
- if (numoptions == 0)
- return NULL;
-
- rdopts = allocateReloptStruct(sizeof(GinOptions), options, numoptions);
-
- fillRelOptions((void *) rdopts, sizeof(GinOptions), options, numoptions,
- validate, tab, lengthof(tab));
-
- pfree(options);
-
- return (bytea *) rdopts;
-}
-
/*
* Fetch index's statistical data into *stats
*
@@ -698,3 +670,32 @@ ginUpdateStats(Relation index, const GinStatsData *stats)
END_CRIT_SECTION();
}
+
+
+static options_catalog *gin_relopt_catalog = NULL;
+
+void *
+gingetreloptcatalog(void)
+{
+ if (!gin_relopt_catalog)
+ {
+ gin_relopt_catalog = allocateOptionsCatalog(NULL,
+ sizeof(GinOptions), 2);
+
+ optionsCatalogAddItemBool(gin_relopt_catalog, "fastupdate",
+ "Enables \"fast update\" feature for this GIN index",
+ AccessExclusiveLock,
+ 0,
+ offsetof(GinOptions, useFastUpdate),
+ GIN_DEFAULT_USE_FASTUPDATE);
+
+ optionsCatalogAddItemInt(gin_relopt_catalog, "gin_pending_list_limit",
+ "Maximum size of the pending list for this GIN index, in kilobytes",
+ AccessExclusiveLock,
+ 0,
+ offsetof(GinOptions, pendingListCleanupSize),
+ -1, 64, MAX_KILOBYTES);
+
+ }
+ return gin_relopt_catalog;
+}
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index 6593771..c366c77 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -81,7 +81,6 @@ gisthandler(PG_FUNCTION_ARGS)
amroutine->amvacuumcleanup = gistvacuumcleanup;
amroutine->amcanreturn = gistcanreturn;
amroutine->amcostestimate = gistcostestimate;
- amroutine->amoptions = gistoptions;
amroutine->amproperty = gistproperty;
amroutine->amvalidate = gistvalidate;
amroutine->ambeginscan = gistbeginscan;
@@ -91,6 +90,7 @@ gisthandler(PG_FUNCTION_ARGS)
amroutine->amendscan = gistendscan;
amroutine->ammarkpos = NULL;
amroutine->amrestrpos = NULL;
+ amroutine->amrelopt_catalog = gistgetreloptcatalog;
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
diff --git a/src/backend/access/gist/gistbuild.c b/src/backend/access/gist/gistbuild.c
index f1f08bb..09d0c68 100644
--- a/src/backend/access/gist/gistbuild.c
+++ b/src/backend/access/gist/gistbuild.c
@@ -126,11 +126,10 @@ gistbuild(Relation heap, Relation index, IndexInfo *indexInfo)
{
/* Get buffering mode from the options string */
GiSTOptions *options = (GiSTOptions *) index->rd_options;
- char *bufferingMode = (char *) options + options->bufferingModeOffset;
- if (strcmp(bufferingMode, "on") == 0)
+ if (options->buffering_mode == GIST_OPTION_BUFFERING_ON)
buildstate.bufferingMode = GIST_BUFFERING_STATS;
- else if (strcmp(bufferingMode, "off") == 0)
+ else if (options->buffering_mode == GIST_OPTION_BUFFERING_OFF)
buildstate.bufferingMode = GIST_BUFFERING_DISABLED;
else
buildstate.bufferingMode = GIST_BUFFERING_AUTO;
@@ -234,25 +233,6 @@ gistbuild(Relation heap, Relation index, IndexInfo *indexInfo)
}
/*
- * Validator for "buffering" reloption on GiST indexes. Allows "on", "off"
- * and "auto" values.
- */
-void
-gistValidateBufferingOption(char *value)
-{
- if (value == NULL ||
- (strcmp(value, "on") != 0 &&
- strcmp(value, "off") != 0 &&
- strcmp(value, "auto") != 0))
- {
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for \"buffering\" option"),
- errdetail("Valid values are \"on\", \"off\", and \"auto\".")));
- }
-}
-
-/*
* Attempt to switch to buffering mode.
*
* If there is not enough memory for buffering build, sets bufferingMode
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
index cbdaec9..fddb863 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -18,14 +18,13 @@
#include "access/gist_private.h"
#include "access/htup_details.h"
-#include "access/reloptions.h"
+#include "access/options.h"
#include "catalog/pg_opclass.h"
#include "storage/indexfsm.h"
#include "storage/lmgr.h"
#include "utils/builtins.h"
#include "utils/syscache.h"
-
/*
* Write itup vector to page, has no control of free space.
*/
@@ -812,34 +811,6 @@ gistNewBuffer(Relation r)
return buffer;
}
-bytea *
-gistoptions(Datum reloptions, bool validate)
-{
- relopt_value *options;
- GiSTOptions *rdopts;
- int numoptions;
- static const relopt_parse_elt tab[] = {
- {"fillfactor", RELOPT_TYPE_INT, offsetof(GiSTOptions, fillfactor)},
- {"buffering", RELOPT_TYPE_STRING, offsetof(GiSTOptions, bufferingModeOffset)}
- };
-
- options = parseRelOptions(reloptions, validate, RELOPT_KIND_GIST,
- &numoptions);
-
- /* if none set, we're done */
- if (numoptions == 0)
- return NULL;
-
- rdopts = allocateReloptStruct(sizeof(GiSTOptions), options, numoptions);
-
- fillRelOptions((void *) rdopts, sizeof(GiSTOptions), options, numoptions,
- validate, tab, lengthof(tab));
-
- pfree(options);
-
- return (bytea *) rdopts;
-}
-
/*
* gistproperty() -- Check boolean properties of indexes.
*
@@ -965,3 +936,33 @@ gistGetFakeLSN(Relation rel)
return GetFakeLSNForUnloggedRel();
}
}
+
+static options_catalog *gist_relopt_catalog = NULL;
+
+void *
+gistgetreloptcatalog(void)
+{
+ static const char *enum_names[] = GIST_OPTION_BUFFERING_VALUE_NAMES;
+
+ if (!gist_relopt_catalog)
+ {
+ gist_relopt_catalog = allocateOptionsCatalog(NULL,
+ sizeof(GiSTOptions), 2);
+
+ optionsCatalogAddItemInt(gist_relopt_catalog, "fillfactor",
+ "Packs gist index pages only to this percentage",
+ NoLock, /* No ALTER, no lock */
+ OPTION_DEFINITION_FLAG_FORBID_ALTER,
+ offsetof(GiSTOptions, fillfactor),
+ GIST_DEFAULT_FILLFACTOR,
+ GIST_MIN_FILLFACTOR, 100);
+ optionsCatalogAddItemEnum(gist_relopt_catalog, "buffering",
+ "Enables buffering build for this GiST index",
+ NoLock, /* No ALTER, no lock */
+ OPTION_DEFINITION_FLAG_FORBID_ALTER,
+ offsetof(GiSTOptions, buffering_mode),
+ enum_names,
+ GIST_OPTION_BUFFERING_AUTO);
+ }
+ return gist_relopt_catalog;
+}
diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c
index 34cc08f..476f643 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 61ca2ec..f3c2b68 100644
--- a/src/backend/access/hash/hashpage.c
+++ b/src/backend/access/hash/hashpage.c
@@ -360,7 +360,7 @@ _hash_init(Relation rel, double num_tuples, ForkNumber forkNum)
data_width = sizeof(uint32);
item_width = MAXALIGN(sizeof(IndexTupleData)) + MAXALIGN(data_width) +
sizeof(ItemIdData); /* include the line pointer */
- ffactor = RelationGetTargetPageUsage(rel, HASH_DEFAULT_FILLFACTOR) / item_width;
+ ffactor = (BLCKSZ * HashGetFillFactor(rel) / 100) / item_width;
/* keep to a sane range */
if (ffactor < 10)
ffactor = 10;
diff --git a/src/backend/access/hash/hashutil.c b/src/backend/access/hash/hashutil.c
index 2e99719..fc46b68 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"
@@ -220,12 +220,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
*/
@@ -514,3 +508,28 @@ _hash_kill_items(IndexScanDesc scan)
MarkBufferDirtyHint(so->hashso_curbuf, true);
}
}
+
+static options_catalog *hash_relopt_catalog = NULL;
+
+void *
+hashgetreloptcatalog(void)
+{
+ if (!hash_relopt_catalog)
+ {
+ hash_relopt_catalog = allocateOptionsCatalog(NULL,
+ sizeof(HashRelOptions), 1);
+
+ optionsCatalogAddItemInt(hash_relopt_catalog, "fillfactor",
+ "Packs hash index pages only to this percentage",
+ NoLock, /* No ALTER -- no lock */
+ OPTION_DEFINITION_FLAG_FORBID_ALTER, /* fillfactor is actualy
+ * stored in metapage
+ * and should not be
+ * changed once index is
+ * created */
+ offsetof(HashRelOptions, fillfactor),
+ HASH_DEFAULT_FILLFACTOR,
+ HASH_MIN_FILLFACTOR, 100);
+ }
+ return hash_relopt_catalog;
+}
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index b147f64..5752194 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -2673,7 +2673,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 6529fe3..6668bd2 100644
--- a/src/backend/access/heap/hio.c
+++ b/src/backend/access/heap/hio.c
@@ -323,7 +323,10 @@ RelationGetBufferForTuple(Relation relation, Size len,
len, MaxHeapTupleSize)));
/* Compute desired extra freespace due to fillfactor option */
- saveFreeSpace = RelationGetTargetPageFreeSpace(relation,
+ if (IsToastRelation(relation))
+ saveFreeSpace = ToastGetTargetPageFreeSpace();
+ else
+ saveFreeSpace = HeapGetTargetPageFreeSpace(relation,
HEAP_DEFAULT_FILLFACTOR);
if (otherBuffer != InvalidBuffer)
diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c
index d69a266..9735ec7 100644
--- a/src/backend/access/heap/pruneheap.c
+++ b/src/backend/access/heap/pruneheap.c
@@ -131,7 +131,11 @@ heap_page_prune_opt(Relation relation, Buffer buffer)
* important than sometimes getting a wrong answer in what is after all
* just a heuristic estimate.
*/
- minfree = RelationGetTargetPageFreeSpace(relation,
+
+ if (IsToastRelation(relation))
+ minfree = ToastGetTargetPageFreeSpace();
+ else
+ minfree = HeapGetTargetPageFreeSpace(relation,
HEAP_DEFAULT_FILLFACTOR);
minfree = Max(minfree, BLCKSZ / 10);
diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c
index d7f65a5..ad26a1e 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 6dca810..6b12304 100644
--- a/src/backend/access/nbtree/nbtinsert.c
+++ b/src/backend/access/nbtree/nbtinsert.c
@@ -1433,8 +1433,7 @@ _bt_findsplitloc(Relation rel,
state.is_rightmost = P_RIGHTMOST(opaque);
state.have_split = false;
if (state.is_leaf)
- state.fillfactor = RelationGetFillFactor(rel,
- BTREE_DEFAULT_FILLFACTOR);
+ state.fillfactor = BTGetFillFactor(rel);
else
state.fillfactor = BTREE_NONLEAF_FILLFACTOR;
state.newitemonleft = false; /* these just to keep compiler quiet */
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index 775f2ff..27d274e 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -21,6 +21,7 @@
#include "access/nbtree.h"
#include "access/relscan.h"
#include "access/xlog.h"
+#include "access/reloptions.h"
#include "catalog/index.h"
#include "commands/vacuum.h"
#include "pgstat.h"
@@ -34,7 +35,6 @@
#include "utils/index_selfuncs.h"
#include "utils/memutils.h"
-
/* Working state for btbuild and its callback */
typedef struct
{
@@ -116,8 +116,7 @@ static void btvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
BTCycleId cycleid);
static void btvacuumpage(BTVacState *vstate, BlockNumber blkno,
BlockNumber orig_blkno);
-
-
+static void * btgetreloptcatalog (void);
/*
* Btree handler function: return IndexAmRoutine with access method parameters
* and callbacks.
@@ -150,7 +149,6 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amvacuumcleanup = btvacuumcleanup;
amroutine->amcanreturn = btcanreturn;
amroutine->amcostestimate = btcostestimate;
- amroutine->amoptions = btoptions;
amroutine->amproperty = btproperty;
amroutine->amvalidate = btvalidate;
amroutine->ambeginscan = btbeginscan;
@@ -160,6 +158,7 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amendscan = btendscan;
amroutine->ammarkpos = btmarkpos;
amroutine->amrestrpos = btrestrpos;
+ amroutine->amrelopt_catalog = btgetreloptcatalog;
amroutine->amestimateparallelscan = btestimateparallelscan;
amroutine->aminitparallelscan = btinitparallelscan;
amroutine->amparallelrescan = btparallelrescan;
@@ -1391,3 +1390,26 @@ btcanreturn(Relation index, int attno)
{
return true;
}
+
+
+static options_catalog *bt_relopt_catalog = NULL;
+
+static void *
+btgetreloptcatalog(void)
+{
+ if (!bt_relopt_catalog)
+ {
+ bt_relopt_catalog = allocateOptionsCatalog(NULL,
+ sizeof(BTRelOptions), 1);
+
+ optionsCatalogAddItemInt(bt_relopt_catalog, "fillfactor",
+ "Packs btree index pages only to this percentage",
+ ShareUpdateExclusiveLock, /* since it applies only
+ * to later inserts */
+ 0,
+ offsetof(BTRelOptions, fillfactor),
+ BTREE_DEFAULT_FILLFACTOR,
+ BTREE_MIN_FILLFACTOR, 100);
+ }
+ return bt_relopt_catalog;
+}
diff --git a/src/backend/access/nbtree/nbtsort.c b/src/backend/access/nbtree/nbtsort.c
index 3d041c4..92ce827 100644
--- a/src/backend/access/nbtree/nbtsort.c
+++ b/src/backend/access/nbtree/nbtsort.c
@@ -344,8 +344,8 @@ _bt_pagestate(BTWriteState *wstate, uint32 level)
if (level > 0)
state->btps_full = (BLCKSZ * (100 - BTREE_NONLEAF_FILLFACTOR) / 100);
else
- state->btps_full = RelationGetTargetPageFreeSpace(wstate->index,
- BTREE_DEFAULT_FILLFACTOR);
+ state->btps_full = (BLCKSZ * (100 - BTGetFillFactor(wstate->index)) / 100);
+
/* no parent level, yet */
state->btps_next = NULL;
diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c
index 5b259a3..867d3e6 100644
--- a/src/backend/access/nbtree/nbtutils.c
+++ b/src/backend/access/nbtree/nbtutils.c
@@ -18,7 +18,7 @@
#include <time.h>
#include "access/nbtree.h"
-#include "access/reloptions.h"
+#include "storage/lock.h"
#include "access/relscan.h"
#include "miscadmin.h"
#include "utils/array.h"
@@ -2038,12 +2038,6 @@ BTreeShmemInit(void)
Assert(found);
}
-bytea *
-btoptions(Datum reloptions, bool validate)
-{
- return default_reloptions(reloptions, validate, RELOPT_KIND_BTREE);
-}
-
/*
* btproperty() -- Check boolean properties of indexes.
*
diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c
index e57ac49..a6416d7 100644
--- a/src/backend/access/spgist/spgutils.c
+++ b/src/backend/access/spgist/spgutils.c
@@ -15,7 +15,7 @@
#include "postgres.h"
-#include "access/reloptions.h"
+#include "access/options.h"
#include "access/spgist_private.h"
#include "access/transam.h"
#include "access/xact.h"
@@ -59,7 +59,6 @@ spghandler(PG_FUNCTION_ARGS)
amroutine->amvacuumcleanup = spgvacuumcleanup;
amroutine->amcanreturn = spgcanreturn;
amroutine->amcostestimate = spgcostestimate;
- amroutine->amoptions = spgoptions;
amroutine->amproperty = NULL;
amroutine->amvalidate = spgvalidate;
amroutine->ambeginscan = spgbeginscan;
@@ -69,6 +68,7 @@ spghandler(PG_FUNCTION_ARGS)
amroutine->amendscan = spgendscan;
amroutine->ammarkpos = NULL;
amroutine->amrestrpos = NULL;
+ amroutine->amrelopt_catalog = spggetreloptcatalog;
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
@@ -372,8 +372,8 @@ SpGistGetBuffer(Relation index, int flags, int needSpace, bool *isNew)
* related to the ones already on it. But fillfactor mustn't cause an
* error for requests that would otherwise be legal.
*/
- needSpace += RelationGetTargetPageFreeSpace(index,
- SPGIST_DEFAULT_FILLFACTOR);
+ needSpace += (BLCKSZ * (100 - SpGistGetFillFactor(index)) / 100);
+
needSpace = Min(needSpace, SPGIST_PAGE_CAPACITY);
/* Get the cache entry for this flags setting */
@@ -537,15 +537,6 @@ SpGistInitMetapage(Page page)
}
/*
- * reloptions processing for SPGiST
- */
-bytea *
-spgoptions(Datum reloptions, bool validate)
-{
- return default_reloptions(reloptions, validate, RELOPT_KIND_SPGIST);
-}
-
-/*
* Get the space needed to store a non-null datum of the indicated type.
* Note the result is already rounded up to a MAXALIGN boundary.
* Also, we follow the SPGiST convention that pass-by-val types are
@@ -911,3 +902,27 @@ SpGistPageAddNewItem(SpGistState *state, Page page, Item item, Size size,
return offnum;
}
+
+
+
+static options_catalog *spgist_relopt_catalog = NULL;
+
+void *
+spggetreloptcatalog(void)
+{
+ if (!spgist_relopt_catalog)
+ {
+ spgist_relopt_catalog = allocateOptionsCatalog(NULL,
+ sizeof(SpGistRelOptions), 1);
+
+ optionsCatalogAddItemInt(spgist_relopt_catalog, "fillfactor",
+ "Packs spgist index pages only to this percentage",
+ ShareUpdateExclusiveLock, /* since it applies only
+ * to later inserts */
+ 0,
+ offsetof(SpGistRelOptions, fillfactor),
+ SPGIST_DEFAULT_FILLFACTOR,
+ SPGIST_MIN_FILLFACTOR, 100);
+ }
+ return spgist_relopt_catalog;
+}
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 29756eb..aaa9270 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -36,7 +36,7 @@
/* Potentially set by pg_upgrade_support functions */
Oid binary_upgrade_next_toast_pg_type_oid = InvalidOid;
-static void CheckAndCreateToastTable(Oid relOid, Datum reloptions,
+static bool CheckAndCreateToastTable(Oid relOid, Datum reloptions,
LOCKMODE lockmode, bool check);
static bool create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
Datum reloptions, LOCKMODE lockmode, bool check);
@@ -67,23 +67,26 @@ NewHeapCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmode)
CheckAndCreateToastTable(relOid, reloptions, lockmode, false);
}
-void
+bool
NewRelationCreateToastTable(Oid relOid, Datum reloptions)
{
- CheckAndCreateToastTable(relOid, reloptions, AccessExclusiveLock, false);
+ return CheckAndCreateToastTable(relOid, reloptions,
+ AccessExclusiveLock, false);
}
-static void
+static bool
CheckAndCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmode, bool check)
{
Relation rel;
+ bool success;
rel = heap_open(relOid, lockmode);
/* create_toast_table does all the work */
- (void) create_toast_table(rel, InvalidOid, InvalidOid, reloptions, lockmode, check);
+ success = create_toast_table(rel, InvalidOid, InvalidOid, reloptions, lockmode, check);
heap_close(rel, NoLock);
+ return success;
}
/*
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 20cb646..c549fd4 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 68100df..dd7dcf1 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 4861799..48be80b 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -325,7 +325,7 @@ DefineIndex(Oid relationId,
Form_pg_am accessMethodForm;
IndexAmRoutine *amRoutine;
bool amcanorder;
- amoptions_function amoptions;
+ amrelopt_catalog_function amoption_catalog;
Datum reloptions;
int16 *coloptions;
IndexInfo *indexInfo;
@@ -523,7 +523,7 @@ DefineIndex(Oid relationId,
accessMethodName)));
amcanorder = amRoutine->amcanorder;
- amoptions = amRoutine->amoptions;
+ amoption_catalog = amRoutine->amrelopt_catalog;
pfree(amRoutine);
ReleaseSysCache(tuple);
@@ -537,10 +537,18 @@ DefineIndex(Oid relationId,
/*
* Parse AM-specific options, convert to text array form, validate.
*/
- reloptions = transformRelOptions((Datum) 0, stmt->options,
- NULL, NULL, false, false);
-
- (void) index_reloptions(amoptions, reloptions, true);
+ if (amoption_catalog)
+ {
+ reloptions = transformOptions(amoption_catalog(),
+ (Datum) 0, stmt->options, 0);
+ }
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("access method %s does not support options",
+ accessMethodName)));
+ }
/*
* Prepare arguments for index_create, primarily an IndexInfo structure.
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 4cf2efb..ab43cf7 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -499,7 +499,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
Datum reloptions;
ListCell *listptr;
AttrNumber attnum;
- static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
Oid ofTypeId;
ObjectAddress address;
@@ -585,13 +584,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)
{
@@ -3073,7 +3082,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);
@@ -3115,13 +3125,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)
{
@@ -3330,7 +3342,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:
@@ -4087,7 +4102,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",
@@ -5927,11 +5942,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));
@@ -9924,7 +9939,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 */
@@ -9953,25 +9968,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,
@@ -9985,7 +10027,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;
@@ -10045,6 +10087,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);
@@ -10068,12 +10112,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));
@@ -10101,6 +10148,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 f9c2620..5a60122 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -332,10 +332,11 @@ CreateTableSpace(CreateTableSpaceStmt *stmt)
nulls[Anum_pg_tablespace_spcacl - 1] = true;
/* Generate new proposed spcoptions (text array) */
- newOptions = transformRelOptions((Datum) 0,
- stmt->options,
- NULL, NULL, false, false);
- (void) tablespace_reloptions(newOptions, true);
+
+ newOptions = transformOptions(
+ get_tablespace_options_catalog(),
+ (Datum) 0, stmt->options, 0);
+
if (newOptions != (Datum) 0)
values[Anum_pg_tablespace_spcoptions - 1] = newOptions;
else
@@ -1023,10 +1024,13 @@ AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt)
/* Generate new proposed spcoptions (text array) */
datum = heap_getattr(tup, Anum_pg_tablespace_spcoptions,
RelationGetDescr(rel), &isnull);
- newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
- stmt->options, NULL, NULL, false,
- stmt->isReset);
- (void) tablespace_reloptions(newOptions, true);
+
+
+ newOptions = transformOptions(get_tablespace_options_catalog(),
+ isnull ? (Datum) 0 : datum,
+ stmt->options,
+ OPTIONS_PARSE_MODE_FOR_ALTER |
+ (stmt->isReset ? OPTIONS_PARSE_MODE_FOR_RESET : 0));
/* Build new tuple. */
memset(repl_null, false, sizeof(repl_null));
diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c
index 5a45558..5e3762c 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 cc88dcc..7ebd1d9 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -139,7 +139,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 4f391d2..484d22b 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -3085,7 +3085,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 1ae43dc..0720a39 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1391,7 +1391,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 33ca749..0156e23 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -2509,6 +2509,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 ||
@@ -2518,8 +2519,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 424be0c..33fb30f 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -1488,7 +1488,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 */
@@ -2989,7 +2989,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
@@ -3025,8 +3025,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 584f4f1..fd08119 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -996,6 +996,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,
@@ -1016,18 +1018,24 @@ ProcessUtilitySlow(ParseState *pstate,
* parse and validate reloptions for the toast
* table
*/
- toast_options = transformRelOptions((Datum) 0,
+
+ optionsDefListValdateNamespaces(
((CreateStmt *) stmt)->options,
- "toast",
- validnsps,
- true,
- false);
- (void) heap_reloptions(RELKIND_TOASTVALUE,
- toast_options,
- true);
+ validnsps);
+
+ toastDefList = optionsDefListFilterNamespaces(
+ ((CreateStmt *) stmt)->options, "toast");
- NewRelationCreateToastTable(address.objectId,
- toast_options);
+ toast_options = transformOptions(
+ get_toast_relopt_catalog(), (Datum) 0,
+ toastDefList, 0);
+
+ success = NewRelationCreateToastTable(
+ address.objectId, toast_options);
+ if (!success && toast_options)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("no TOAST relation was created for a table. Can't set toast.* storage parameters")));
}
else if (IsA(stmt, CreateForeignTableStmt))
{
@@ -1093,8 +1101,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))
{
@@ -1601,6 +1614,7 @@ ProcessUtilitySlow(ParseState *pstate,
case T_AlterPublicationStmt:
AlterPublication((AlterPublicationStmt *) parsetree);
+
/*
* AlterPublication calls EventTriggerCollectSimpleCommand
* directly
diff --git a/src/backend/utils/cache/attoptcache.c b/src/backend/utils/cache/attoptcache.c
index f7f85b5..d20b680 100644
--- a/src/backend/utils/cache/attoptcache.c
+++ b/src/backend/utils/cache/attoptcache.c
@@ -150,7 +150,10 @@ get_attribute_options(Oid attrelid, int attnum)
opts = NULL;
else
{
- bytea *bytea_opts = attribute_reloptions(datum, false);
+ bytea *bytea_opts;
+
+ bytea_opts = optionsTextArrayToBytea(
+ get_attribute_options_catalog(), datum);
opts = MemoryContextAlloc(CacheMemoryContext,
VARSIZE(bytea_opts));
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index bc22098..a5175f5 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 f919cf8..9f3db93 100644
--- a/src/include/access/amapi.h
+++ b/src/include/access/amapi.h
@@ -98,10 +98,6 @@ typedef void (*amcostestimate_function) (struct PlannerInfo *root,
double *indexCorrelation,
double *indexPages);
-/* parse index reloptions */
-typedef bytea *(*amoptions_function) (Datum reloptions,
- bool validate);
-
/* report AM, index, or index column property */
typedef bool (*amproperty_function) (Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
@@ -139,6 +135,9 @@ typedef void (*ammarkpos_function) (IndexScanDesc scan);
/* restore marked scan position */
typedef void (*amrestrpos_function) (IndexScanDesc scan);
+/* get catalog of reloptions definitions */
+typedef void *(*amrelopt_catalog_function) ();
+
/*
* Callback function signatures - for parallel index scans.
*/
@@ -202,7 +201,6 @@ typedef struct IndexAmRoutine
amvacuumcleanup_function amvacuumcleanup;
amcanreturn_function amcanreturn; /* can be NULL */
amcostestimate_function amcostestimate;
- amoptions_function amoptions;
amproperty_function amproperty; /* can be NULL */
amvalidate_function amvalidate;
ambeginscan_function ambeginscan;
@@ -212,6 +210,7 @@ typedef struct IndexAmRoutine
amendscan_function amendscan;
ammarkpos_function ammarkpos; /* can be NULL */
amrestrpos_function amrestrpos; /* can be NULL */
+ amrelopt_catalog_function amrelopt_catalog; /* can be NULL */
/* interface functions to support parallel index scans */
amestimateparallelscan_function amestimateparallelscan; /* can be NULL */
diff --git a/src/include/access/brin.h b/src/include/access/brin.h
index 896824a..c84d6d4 100644
--- a/src/include/access/brin.h
+++ b/src/include/access/brin.h
@@ -25,6 +25,9 @@ typedef struct BrinOptions
} BrinOptions;
#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 abe8877..4c32dd1 100644
--- a/src/include/access/brin_internal.h
+++ b/src/include/access/brin_internal.h
@@ -102,7 +102,6 @@ extern IndexBulkDeleteResult *brinbulkdelete(IndexVacuumInfo *info,
void *callback_state);
extern IndexBulkDeleteResult *brinvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
-extern bytea *brinoptions(Datum reloptions, bool validate);
/* brin_validate.c */
extern bool brinvalidate(Oid opclassoid);
diff --git a/src/include/access/gin_private.h b/src/include/access/gin_private.h
index f2e9c4d..393743b 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 1ad4ed6..6a7c2cb 100644
--- a/src/include/access/gist_private.h
+++ b/src/include/access/gist_private.h
@@ -367,6 +367,24 @@ typedef struct GISTBuildBuffers
int rootlevel;
} GISTBuildBuffers;
+
+/*
+ * Definition of items of enum type. Names and codes. To add or modify item
+ * edit both lists
+ */
+#define GIST_OPTION_BUFFERING_VALUE_NAMES { \
+ "on", \
+ "off", \
+ "auto", \
+ (const char *) NULL \
+}
+typedef enum gist_option_buffering_value_numbers
+{
+ GIST_OPTION_BUFFERING_ON = 0,
+ GIST_OPTION_BUFFERING_OFF = 1,
+ GIST_OPTION_BUFFERING_AUTO = 2,
+} gist_option_buffering_value_numbers;
+
/*
* Storage type for GiST's reloptions
*/
@@ -374,7 +392,7 @@ typedef struct GiSTOptions
{
int32 vl_len_; /* varlena header (do not touch directly!) */
int fillfactor; /* page fill factor in percent (0..100) */
- int bufferingModeOffset; /* use buffering build? */
+ int buffering_mode; /* use buffering build? */
} GiSTOptions;
/* gist.c */
@@ -435,7 +453,6 @@ extern bool gistvalidate(Oid opclassoid);
#define GIST_MIN_FILLFACTOR 10
#define GIST_DEFAULT_FILLFACTOR 90
-extern bytea *gistoptions(Datum reloptions, bool validate);
extern bool gistproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
@@ -485,6 +502,7 @@ extern void gistMakeUnionKey(GISTSTATE *giststate, int attno,
Datum *dst, bool *dstisnull);
extern XLogRecPtr gistGetFakeLSN(Relation rel);
+extern void *gistgetreloptcatalog(void);
/* gistvacuum.c */
extern IndexBulkDeleteResult *gistbulkdelete(IndexVacuumInfo *info,
@@ -503,7 +521,6 @@ extern void gistSplitByKey(Relation r, Page page, IndexTuple *itup,
/* gistbuild.c */
extern IndexBuildResult *gistbuild(Relation heap, Relation index,
struct IndexInfo *indexInfo);
-extern void gistValidateBufferingOption(char *value);
/* gistbuildbuffers.c */
extern GISTBuildBuffers *gistInitBuildBuffers(int pagesPerBuffer, int levelStep,
diff --git a/src/include/access/hash.h b/src/include/access/hash.h
index eb1df57..94fab0c 100644
--- a/src/include/access/hash.h
+++ b/src/include/access/hash.h
@@ -208,6 +208,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)
*/
@@ -303,7 +316,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);
@@ -404,4 +416,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 f9304db..0500564 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -426,6 +426,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
*/
@@ -533,7 +544,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 861977a..86dff99 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -1,13 +1,9 @@
/*-------------------------------------------------------------------------
*
* reloptions.h
- * Core support for relation and tablespace options (pg_class.reloptions
+ * Support for relation and tablespace options (pg_class.reloptions
* and pg_tablespace.spcoptions)
*
- * Note: the functions dealing with text-array reloptions values declare
- * them as Datum, not ArrayType *, to avoid needing to include array.h
- * into a lot of low-level code.
- *
*
* Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
@@ -20,263 +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),
- /* if you add a new kind, make sure you update "last_default" too */
- RELOPT_KIND_LAST_DEFAULT = RELOPT_KIND_BRIN,
- /* 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 9dca8fd..9c57eba 100644
--- a/src/include/access/spgist.h
+++ b/src/include/access/spgist.h
@@ -20,10 +20,6 @@
#include "lib/stringinfo.h"
-/* reloption parameters */
-#define SPGIST_MIN_FILLFACTOR 10
-#define SPGIST_DEFAULT_FILLFACTOR 80
-
/* SPGiST opclass support function numbers */
#define SPGIST_CONFIG_PROC 1
#define SPGIST_CHOOSE_PROC 2
@@ -182,9 +178,6 @@ typedef struct spgLeafConsistentOut
} spgLeafConsistentOut;
-/* spgutils.c */
-extern bytea *spgoptions(Datum reloptions, bool validate);
-
/* spginsert.c */
extern IndexBuildResult *spgbuild(Relation heap, Relation index,
struct IndexInfo *indexInfo);
diff --git a/src/include/access/spgist_private.h b/src/include/access/spgist_private.h
index 4072c05..d99adc9 100644
--- a/src/include/access/spgist_private.h
+++ b/src/include/access/spgist_private.h
@@ -21,6 +21,19 @@
#include "utils/relcache.h"
+typedef struct SpGistRelOptions
+{
+ int32 varlena_header_; /* varlena header (do not touch directly!) */
+ int fillfactor; /* page fill factor in percent (0..100) */
+} SpGistRelOptions;
+
+
+#define SpGistGetFillFactor(relation) \
+ ((relation)->rd_options ? \
+ ((SpGistRelOptions *) (relation)->rd_options)->fillfactor : \
+ SPGIST_DEFAULT_FILLFACTOR)
+
+
/* Page numbers of fixed-location pages */
#define SPGIST_METAPAGE_BLKNO (0) /* metapage */
#define SPGIST_ROOT_BLKNO (1) /* root for normal entries */
@@ -380,6 +393,11 @@ typedef SpGistDeadTupleData *SpGistDeadTuple;
#define GBUF_REQ_NULLS(flags) ((flags) & GBUF_NULLS)
/* spgutils.c */
+
+/* reloption parameters */
+#define SPGIST_MIN_FILLFACTOR 10
+#define SPGIST_DEFAULT_FILLFACTOR 80
+
extern SpGistCache *spgGetCache(Relation index);
extern void initSpGistState(SpGistState *state, Relation index);
extern Buffer SpGistNewBuffer(Relation index);
@@ -407,6 +425,7 @@ extern OffsetNumber SpGistPageAddNewItem(SpGistState *state, Page page,
Item item, Size size,
OffsetNumber *startOffset,
bool errorOK);
+extern void *spggetreloptcatalog(void);
/* spgdoinsert.c */
extern void spgUpdateNodeLink(SpGistInnerTuple tup, int nodeN,
diff --git a/src/include/catalog/toasting.h b/src/include/catalog/toasting.h
index 00d0a83..18cff15 100644
--- a/src/include/catalog/toasting.h
+++ b/src/include/catalog/toasting.h
@@ -19,7 +19,7 @@
/*
* toasting.c prototypes
*/
-extern void NewRelationCreateToastTable(Oid relOid, Datum reloptions);
+extern bool NewRelationCreateToastTable(Oid relOid, Datum reloptions);
extern void NewHeapCreateToastTable(Oid relOid, Datum reloptions,
LOCKMODE lockmode);
extern void AlterTableCreateToastTable(Oid relOid, Datum reloptions,
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index f3a9701..33f5782 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -31,7 +31,7 @@ extern Oid AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode);
extern void AlterTable(Oid relid, LOCKMODE lockmode, AlterTableStmt *stmt);
-extern LOCKMODE AlterTableGetLockLevel(List *cmds);
+extern LOCKMODE AlterTableGetLockLevel(Oid relid, List *cmds);
extern void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index ab875bb..47f9aa4 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -273,7 +273,7 @@ typedef struct AutoVacOpts
float8 analyze_scale_factor;
} AutoVacOpts;
-typedef struct StdRdOptions
+typedef struct HeapOptions
{
int32 vl_len_; /* varlena header (do not touch directly!) */
int fillfactor; /* page fill factor in percent (0..100) */
@@ -281,107 +281,116 @@ typedef struct StdRdOptions
bool user_catalog_table; /* use as an additional catalog
* relation */
int parallel_workers; /* max number of parallel workers */
-} StdRdOptions;
+} HeapOptions;
+
+typedef struct ToastOptions
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ AutoVacOpts autovacuum; /* autovacuum-related options */
+} ToastOptions;
#define HEAP_MIN_FILLFACTOR 10
#define HEAP_DEFAULT_FILLFACTOR 100
+#define TOAST_DEFAULT_FILLFACTOR 100 /* Only default is actually used */
+
/*
- * RelationGetFillFactor
+ * HeapGetFillFactor
* Returns the relation's fillfactor. Note multiple eval of argument!
*/
-#define RelationGetFillFactor(relation, defaultff) \
+#define HeapGetFillFactor(relation, defaultff) \
((relation)->rd_options ? \
- ((StdRdOptions *) (relation)->rd_options)->fillfactor : (defaultff))
+ ((HeapOptions *) (relation)->rd_options)->fillfactor : (defaultff))
/*
- * RelationGetTargetPageUsage
- * Returns the relation's desired space usage per page in bytes.
- */
-#define RelationGetTargetPageUsage(relation, defaultff) \
- (BLCKSZ * RelationGetFillFactor(relation, defaultff) / 100)
-
-/*
- * RelationGetTargetPageFreeSpace
+ * HeapGetTargetPageFreeSpace
* Returns the relation's desired freespace per page in bytes.
*/
-#define RelationGetTargetPageFreeSpace(relation, defaultff) \
- (BLCKSZ * (100 - RelationGetFillFactor(relation, defaultff)) / 100)
+#define HeapGetTargetPageFreeSpace(relation, defaultff) \
+ (BLCKSZ * (100 - HeapGetFillFactor(relation, defaultff)) / 100)
/*
- * RelationIsUsedAsCatalogTable
+ * HeapIsUsedAsCatalogTable
* Returns whether the relation should be treated as a catalog table
* from the pov of logical decoding. Note multiple eval of argument!
*/
-#define RelationIsUsedAsCatalogTable(relation) \
+#define HeapIsUsedAsCatalogTable(relation) \
((relation)->rd_options && \
((relation)->rd_rel->relkind == RELKIND_RELATION || \
(relation)->rd_rel->relkind == RELKIND_MATVIEW) ? \
- ((StdRdOptions *) (relation)->rd_options)->user_catalog_table : false)
+ ((HeapOptions *) (relation)->rd_options)->user_catalog_table : false)
/*
- * RelationGetParallelWorkers
+ * HeapGetParallelWorkers
* Returns the relation's parallel_workers reloption setting.
* Note multiple eval of argument!
*/
-#define RelationGetParallelWorkers(relation, defaultpw) \
+#define HeapGetParallelWorkers(relation, defaultpw) \
((relation)->rd_options ? \
- ((StdRdOptions *) (relation)->rd_options)->parallel_workers : (defaultpw))
+ ((HeapOptions *) (relation)->rd_options)->parallel_workers : (defaultpw))
+#define ToastGetTargetPageFreeSpace() \
+ (BLCKSZ * (100 - TOAST_DEFAULT_FILLFACTOR) / 100)
/*
* ViewOptions
* Contents of rd_options for views
*/
+
+/*
+ * Definition of items of enum type. Names and codes. To add or modify item
+ * edit both lists
+ */
+#define VIEW_OPTION_CHECK_OPTION_VALUE_NAMES { \
+ "local", \
+ "cascaded", \
+ (const char *) NULL \
+}
+
+typedef enum view_option_check_option_value_numbers
+{
+ VIEW_OPTION_CHECK_OPTION_NOT_SET = -1,
+ VIEW_OPTION_CHECK_OPTION_LOCAL = 0,
+ VIEW_OPTION_CHECK_OPTION_CASCADED = 1,
+} view_option_check_option_value_numbers;
+
typedef struct ViewOptions
{
int32 vl_len_; /* varlena header (do not touch directly!) */
bool security_barrier;
- int check_option_offset;
+ int check_option;
} ViewOptions;
/*
- * RelationIsSecurityView
- * Returns whether the relation is security view, or not. Note multiple
- * eval of argument!
+ * ViewIsSecurityView
+ * Returns whether the view is security view, or not. Note multiple eval
+ * of argument!
*/
-#define RelationIsSecurityView(relation) \
+#define ViewIsSecurityView(relation) \
((relation)->rd_options ? \
- ((ViewOptions *) (relation)->rd_options)->security_barrier : false)
+ ((ViewOptions *) (relation)->rd_options)->security_barrier : false)
/*
- * RelationHasCheckOption
- * Returns true if the relation is a view defined with either the local
- * or the cascaded check option. Note multiple eval of argument!
+ * ViewHasCheckOption
+ * Returns true if view is defined with either the local or the cascaded
+ * check option. Note multiple eval of argument!
*/
-#define RelationHasCheckOption(relation) \
- ((relation)->rd_options && \
- ((ViewOptions *) (relation)->rd_options)->check_option_offset != 0)
-/*
- * RelationHasLocalCheckOption
- * Returns true if the relation is a view defined with the local check
- * option. Note multiple eval of argument!
- */
-#define RelationHasLocalCheckOption(relation) \
- ((relation)->rd_options && \
- ((ViewOptions *) (relation)->rd_options)->check_option_offset != 0 ? \
- strcmp((char *) (relation)->rd_options + \
- ((ViewOptions *) (relation)->rd_options)->check_option_offset, \
- "local") == 0 : false)
+#define ViewHasCheckOption(relation) \
+((relation)->rd_options && \
+ ((ViewOptions *) (relation)->rd_options)->check_option != \
+ VIEW_OPTION_CHECK_OPTION_NOT_SET)
/*
- * RelationHasCascadedCheckOption
- * Returns true if the relation is a view defined with the cascaded check
- * option. Note multiple eval of argument!
+ * ViewHasCascadedCheckOption
+ * Returns true if the view is defined with the cascaded check option.
+ * Note multiple eval of argument!
*/
-#define RelationHasCascadedCheckOption(relation) \
- ((relation)->rd_options && \
- ((ViewOptions *) (relation)->rd_options)->check_option_offset != 0 ? \
- strcmp((char *) (relation)->rd_options + \
- ((ViewOptions *) (relation)->rd_options)->check_option_offset, \
- "cascaded") == 0 : false)
+#define ViewHasCascadedCheckOption(relation) \
+((relation)->rd_options && \
+ ((ViewOptions *) (relation)->rd_options)->check_option == \
+ VIEW_OPTION_CHECK_OPTION_CASCADED)
/*
* RelationIsValid
@@ -560,7 +569,7 @@ typedef struct ViewOptions
#define RelationIsAccessibleInLogicalDecoding(relation) \
(XLogLogicalInfoActive() && \
RelationNeedsWAL(relation) && \
- (IsCatalogRelation(relation) || RelationIsUsedAsCatalogTable(relation)))
+ (IsCatalogRelation(relation) || HeapIsUsedAsCatalogTable(relation)))
/*
* RelationIsLogicallyLogged
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 2227f2d..0a17ae9 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -2285,7 +2285,9 @@ ERROR: unrecognized parameter "autovacuum_enabled"
alter view my_locks set (autovacuum_enabled = false);
ERROR: unrecognized parameter "autovacuum_enabled"
alter table my_locks reset (autovacuum_enabled);
+ERROR: unrecognized parameter "autovacuum_enabled"
alter view my_locks reset (autovacuum_enabled);
+ERROR: unrecognized parameter "autovacuum_enabled"
begin;
alter view my_locks set (security_barrier=off);
select * from my_locks order by 1;
@@ -3372,3 +3374,10 @@ ERROR: partition constraint is violated by some row
-- cleanup
drop table p;
drop table p1;
+-- test alter column options
+CREATE TABLE tmp(i integer);
+INSERT INTO tmp VALUES (1);
+ALTER TABLE tmp ALTER COLUMN i SET (n_distinct = 1, n_distinct_inherited = 2);
+ALTER TABLE tmp ALTER COLUMN i RESET (n_distinct_inherited);
+ANALYZE tmp;
+DROP TABLE tmp;
diff --git a/src/test/regress/expected/brin.out b/src/test/regress/expected/brin.out
index f0008dd3..c52b022 100644
--- a/src/test/regress/expected/brin.out
+++ b/src/test/regress/expected/brin.out
@@ -406,3 +406,8 @@ SELECT brin_summarize_new_values('brinidx'); -- ok, no change expected
0
(1 row)
+-- 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/create_index.out b/src/test/regress/expected/create_index.out
index 26cd059..7b8f805 100644
--- a/src/test/regress/expected/create_index.out
+++ b/src/test/regress/expected/create_index.out
@@ -2337,7 +2337,7 @@ Options: fastupdate=on, gin_pending_list_limit=128
CREATE INDEX hash_i4_index ON hash_i4_heap USING hash (random int4_ops);
CREATE INDEX hash_name_index ON hash_name_heap USING hash (random name_ops);
CREATE INDEX hash_txt_index ON hash_txt_heap USING hash (random text_ops);
-CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_ops);
+CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=60);
CREATE UNLOGGED TABLE unlogged_hash_table (id int4);
CREATE INDEX unlogged_hash_index ON unlogged_hash_table USING hash (id int4_ops);
DROP TABLE unlogged_hash_table;
diff --git a/src/test/regress/expected/gist.out b/src/test/regress/expected/gist.out
index c7181b0..19f1881 100644
--- a/src/test/regress/expected/gist.out
+++ b/src/test/regress/expected/gist.out
@@ -12,6 +12,20 @@ insert into gist_point_tbl (id, p)
select g+100000, point(g*10+1, g*10+1) from generate_series(1, 10000) g;
-- To test vacuum, delete some entries from all over the index.
delete from gist_point_tbl where id % 2 = 1;
+-- Test buffering and fillfactor reloption takes valid values
+create index gist_pointidx2 on gist_point_tbl using gist(p) with (buffering = on, fillfactor=50);
+create index gist_pointidx3 on gist_point_tbl using gist(p) with (buffering = off);
+create index gist_pointidx4 on gist_point_tbl using gist(p) with (buffering = auto);
+--Test buffering and fillfactor for refising invalid values
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (buffering = invalid_value);
+ERROR: invalid value for "buffering" option
+DETAIL: Valid values are "on", "off", "auto".
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfactor=9);
+ERROR: value 9 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfactor=101);
+ERROR: value 101 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
-- And also delete some concentration of values. (GiST doesn't currently
-- attempt to delete pages even when they become empty, but if it did, this
-- would exercise it)
diff --git a/src/test/regress/expected/hash_index.out b/src/test/regress/expected/hash_index.out
index 0a18efa..c67b61a 100644
--- a/src/test/regress/expected/hash_index.out
+++ b/src/test/regress/expected/hash_index.out
@@ -230,3 +230,12 @@ CREATE TABLE hash_heap_float4 (x float4, y int);
INSERT INTO hash_heap_float4 VALUES (1.1,1);
CREATE INDEX hash_idx ON hash_heap_float4 USING hash (x);
DROP TABLE hash_heap_float4 CASCADE;
+-- Check reloptions min max values and that it is not allowed to ALTER
+CREATE INDEX hash_f8_index2 ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=9);
+ERROR: value 9 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+CREATE INDEX hash_f8_index2 ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=101);
+ERROR: value 101 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+ALTER INDEX hash_f8_index SET (fillfactor=99);
+ERROR: changing parameter "fillfactor" is not allowed
diff --git a/src/test/regress/expected/reloptions.out b/src/test/regress/expected/reloptions.out
new file mode 100644
index 0000000..835d72f
--- /dev/null
+++ b/src/test/regress/expected/reloptions.out
@@ -0,0 +1,193 @@
+-- Simple create
+CREATE TABLE reloptions_test(i INT) WITH (fillfactor=30,autovacuum_enabled = false, autovacuum_analyze_scale_factor = 0.2);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+ reloptions
+------------------------------------------------------------------------------
+ {fillfactor=30,autovacuum_enabled=false,autovacuum_analyze_scale_factor=0.2}
+(1 row)
+
+-- Test сase insensitive
+CREATE TABLE reloptions_test3(i INT) WITH (FiLlFaCtoR=40);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test3'::regclass;
+ reloptions
+-----------------
+ {fillfactor=40}
+(1 row)
+
+-- Fail on min/max values check
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=2);
+ERROR: value 2 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=110);
+ERROR: value 110 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor = -10.0);
+ERROR: value -10.0 out of bounds for option "autovacuum_analyze_scale_factor"
+DETAIL: Valid values are between "0.000000" and "100.000000".
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor = 110.0);
+ERROR: value 110.0 out of bounds for option "autovacuum_analyze_scale_factor"
+DETAIL: Valid values are between "0.000000" and "100.000000".
+-- Fail when option and namespase do not exist
+CREATE TABLE reloptions_test2(i INT) WITH (not_existing_option=2);
+ERROR: unrecognized parameter "not_existing_option"
+CREATE TABLE reloptions_test2(i INT) WITH (not_existing_namespace.fillfactor=2);
+ERROR: unrecognized parameter namespace "not_existing_namespace"
+-- Fail while setting unproper value
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=30.5);
+ERROR: invalid value for integer option "fillfactor": 30.5
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor='string');
+ERROR: invalid value for integer option "fillfactor": string
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=true);
+ERROR: invalid value for integer option "fillfactor": true
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled=12);
+ERROR: invalid value for boolean option "autovacuum_enabled": 12
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled=30.5);
+ERROR: invalid value for boolean option "autovacuum_enabled": 30.5
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled='string');
+ERROR: invalid value for boolean option "autovacuum_enabled": string
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor='string');
+ERROR: invalid value for floating point option "autovacuum_analyze_scale_factor": string
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor=true);
+ERROR: invalid value for floating point option "autovacuum_analyze_scale_factor": true
+-- Specifing name only should fail as if there was =ture after it
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor);
+ERROR: invalid value for integer option "fillfactor": true
+-- Simple ALTER TABLE
+ALTER TABLE reloptions_test SET (fillfactor=31, autovacuum_analyze_scale_factor = 0.3);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+ reloptions
+------------------------------------------------------------------------------
+ {autovacuum_enabled=false,fillfactor=31,autovacuum_analyze_scale_factor=0.3}
+(1 row)
+
+-- Check that we cat set boolean option to true just by mentioning it
+ALTER TABLE reloptions_test SET (autovacuum_enabled, fillfactor=32);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+ reloptions
+-----------------------------------------------------------------------------
+ {autovacuum_analyze_scale_factor=0.3,autovacuum_enabled=true,fillfactor=32}
+(1 row)
+
+-- Check that RESET works well
+ALTER TABLE reloptions_test RESET (fillfactor);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+ reloptions
+---------------------------------------------------------------
+ {autovacuum_analyze_scale_factor=0.3,autovacuum_enabled=true}
+(1 row)
+
+-- Check that RESETting all values make NULL reloptions record in pg_class
+ALTER TABLE reloptions_test RESET (autovacuum_enabled, autovacuum_analyze_scale_factor);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass AND reloptions IS NULL;
+ reloptions
+------------
+
+(1 row)
+
+-- Check RESET fails on att=value
+ALTER TABLE reloptions_test RESET (fillfactor=12);
+ERROR: RESET must not include values for parameters
+-- Check oids options is ignored
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test(i INT) WITH (fillfactor=20, oids=true);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+ reloptions
+-----------------
+ {fillfactor=20}
+(1 row)
+
+-- Now testing toast.* options
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test (s VARCHAR) WITH (toast.autovacuum_vacuum_cost_delay = 23 );
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+ reloptions
+-----------------------------------
+ {autovacuum_vacuum_cost_delay=23}
+(1 row)
+
+ALTER TABLE reloptions_test SET (toast.autovacuum_vacuum_cost_delay = 24);
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+ reloptions
+-----------------------------------
+ {autovacuum_vacuum_cost_delay=24}
+(1 row)
+
+ALTER TABLE reloptions_test RESET (toast.autovacuum_vacuum_cost_delay);
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+ reloptions
+------------
+
+(1 row)
+
+-- Fail on unexisting options in toast namespace
+CREATE TABLE reloptions_test2 (i int) WITH (toast.not_existing_option = 42 );
+ERROR: unrecognized parameter "toast.not_existing_option"
+-- Fail on setting reloption to a table that does not have a TOAST relation
+CREATE TABLE reloptions_test2 (i int) WITH (toast.autovacuum_vacuum_cost_delay = 23 );
+ERROR: no TOAST relation was created for a table. Can't set toast.* storage parameters
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test(i INT);
+ALTER TABLE reloptions_test SET (toast.autovacuum_vacuum_cost_delay = 23);
+ERROR: TOAST relation for this table does not exist. Can't change toast.* storage parameters
+ALTER TABLE reloptions_test RESET (toast.autovacuum_vacuum_cost_delay);
+ERROR: TOAST relation for this table does not exist. Can't change toast.* storage parameters
+-- autovacuum_analyze_scale_factor and autovacuum_analyze_threshold should be
+-- accepted by heap but rejected by toast (special case)
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test (s VARCHAR) WITH (autovacuum_analyze_scale_factor=1, autovacuum_analyze_threshold=1);
+CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analyze_scale_factor=1);
+ERROR: unrecognized parameter "toast.autovacuum_analyze_scale_factor"
+CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analyze_threshold=1);
+ERROR: unrecognized parameter "toast.autovacuum_analyze_threshold"
+-- And now mixed toast + heap
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test (s VARCHAR) WITH (toast.autovacuum_vacuum_cost_delay = 23, autovacuum_vacuum_cost_delay = 24, fillfactor = 40);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+ reloptions
+-------------------------------------------------
+ {autovacuum_vacuum_cost_delay=24,fillfactor=40}
+(1 row)
+
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+ reloptions
+-----------------------------------
+ {autovacuum_vacuum_cost_delay=23}
+(1 row)
+
+-- Same FOR CREATE and ALTER INDEX for btree indexes
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (fillfactor=30);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx'::regclass;
+ reloptions
+-----------------
+ {fillfactor=30}
+(1 row)
+
+-- Fail when option and namespase do not exist
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_existing_option=2);
+ERROR: unrecognized parameter "not_existing_option"
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_existing_option.fillfactor=2);
+ERROR: unrecognized parameter namespace "not_existing_option"
+-- Check ranges
+CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfactor=1);
+ERROR: value 1 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfactor=130);
+ERROR: value 130 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+-- Check alter
+ALTER INDEX reloptions_test_idx SET (fillfactor=40);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx'::regclass;
+ reloptions
+-----------------
+ {fillfactor=40}
+(1 row)
+
+-- Check alter on empty relop list
+CREATE INDEX reloptions_test_idx3 ON reloptions_test (s);
+ALTER INDEX reloptions_test_idx3 SET (fillfactor=40);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx3'::regclass;
+ reloptions
+-----------------
+ {fillfactor=40}
+(1 row)
+
diff --git a/src/test/regress/expected/spgist.out b/src/test/regress/expected/spgist.out
index 0691e91..bb58fb7 100644
--- a/src/test/regress/expected/spgist.out
+++ b/src/test/regress/expected/spgist.out
@@ -4,7 +4,7 @@
-- There are other tests to test different SP-GiST opclasses. This is for
-- testing SP-GiST code itself.
create table spgist_point_tbl(id int4, p point);
-create index spgist_point_idx on spgist_point_tbl using spgist(p);
+create index spgist_point_idx on spgist_point_tbl using spgist(p) with (fillfactor = 75);
-- Test vacuum-root operation. It gets invoked when the root is also a leaf,
-- i.e. the index is very small.
insert into spgist_point_tbl (id, p)
@@ -37,3 +37,12 @@ select g, 'baaaaaaaaaaaaaar' || g from generate_series(1, 1000) g;
-- tuple to be moved to another page.
insert into spgist_text_tbl (id, t)
select -g, 'f' || repeat('o', 100-g) || 'surprise' from generate_series(1, 100) g;
+-- Check reloptions min, max values
+create index spgist_point_idx2 on spgist_point_tbl using spgist(p) with (fillfactor = 9);
+ERROR: value 9 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+create index spgist_point_idx2 on spgist_point_tbl using spgist(p) with (fillfactor = 101);
+ERROR: value 101 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+-- ALTER reloption should work
+alter index spgist_point_idx set (fillfactor = 76);
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out
index aa06d1d..287dd84 100644
--- a/src/test/regress/expected/updatable_views.out
+++ b/src/test/regress/expected/updatable_views.out
@@ -1551,7 +1551,7 @@ SELECT * FROM base_tbl;
ALTER VIEW rw_view1 SET (check_option=here); -- invalid
ERROR: invalid value for "check_option" option
-DETAIL: Valid values are "local" and "cascaded".
+DETAIL: Valid values are "local", "cascaded".
ALTER VIEW rw_view1 SET (check_option=local);
INSERT INTO rw_view2 VALUES (-20); -- should fail
ERROR: new row violates check option for view "rw_view1"
diff --git a/src/test/regress/input/tablespace.source b/src/test/regress/input/tablespace.source
index 03a62bd..7f7934b 100644
--- a/src/test/regress/input/tablespace.source
+++ b/src/test/regress/input/tablespace.source
@@ -12,10 +12,10 @@ DROP TABLESPACE regress_tblspacewith;
CREATE TABLESPACE regress_tblspace LOCATION '@testtablespace@';
-- try setting and resetting some properties for the new tablespace
-ALTER TABLESPACE regress_tblspace SET (random_page_cost = 1.0);
+ALTER TABLESPACE regress_tblspace SET (random_page_cost = 1.0, seq_page_cost = 1.1);
ALTER TABLESPACE regress_tblspace SET (some_nonexistent_parameter = true); -- fail
ALTER TABLESPACE regress_tblspace RESET (random_page_cost = 2.0); -- fail
-ALTER TABLESPACE regress_tblspace RESET (random_page_cost, seq_page_cost); -- ok
+ALTER TABLESPACE regress_tblspace RESET (random_page_cost, effective_io_concurrency); -- ok
-- create a schema we can use
CREATE SCHEMA testschema;
diff --git a/src/test/regress/output/tablespace.source b/src/test/regress/output/tablespace.source
index aaedf5f..2443511 100644
--- a/src/test/regress/output/tablespace.source
+++ b/src/test/regress/output/tablespace.source
@@ -14,12 +14,12 @@ DROP TABLESPACE regress_tblspacewith;
-- create a tablespace we can use
CREATE TABLESPACE regress_tblspace LOCATION '@testtablespace@';
-- try setting and resetting some properties for the new tablespace
-ALTER TABLESPACE regress_tblspace SET (random_page_cost = 1.0);
+ALTER TABLESPACE regress_tblspace SET (random_page_cost = 1.0, seq_page_cost = 1.1);
ALTER TABLESPACE regress_tblspace SET (some_nonexistent_parameter = true); -- fail
ERROR: unrecognized parameter "some_nonexistent_parameter"
ALTER TABLESPACE regress_tblspace RESET (random_page_cost = 2.0); -- fail
ERROR: RESET must not include values for parameters
-ALTER TABLESPACE regress_tblspace RESET (random_page_cost, seq_page_cost); -- ok
+ALTER TABLESPACE regress_tblspace RESET (random_page_cost, effective_io_concurrency); -- ok
-- create a schema we can use
CREATE SCHEMA testschema;
-- try a table
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 9f95b01..6d0ba9e 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -109,7 +109,7 @@ test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combo
# NB: temp.sql does a reconnect which transiently uses 2 connections,
# so keep this parallel group to at most 19 tests
# ----------
-test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml
+test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml reloptions
# event triggers cannot run concurrently with any test that runs DDL
test: event_trigger
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index e026b7c..083adae 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -176,5 +176,6 @@ test: returning
test: largeobject
test: with
test: xml
+test: reloptions
test: event_trigger
test: stats
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index 8cd6786..bd59482 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -2228,3 +2228,11 @@ alter table p attach partition p1 for values from (1, 2) to (1, 10);
-- cleanup
drop table p;
drop table p1;
+
+-- test alter column options
+CREATE TABLE tmp(i integer);
+INSERT INTO tmp VALUES (1);
+ALTER TABLE tmp ALTER COLUMN i SET (n_distinct = 1, n_distinct_inherited = 2);
+ALTER TABLE tmp ALTER COLUMN i RESET (n_distinct_inherited);
+ANALYZE tmp;
+DROP TABLE tmp;
diff --git a/src/test/regress/sql/brin.sql b/src/test/regress/sql/brin.sql
index 5bf5387..6353c82 100644
--- a/src/test/regress/sql/brin.sql
+++ b/src/test/regress/sql/brin.sql
@@ -409,3 +409,7 @@ UPDATE brintest SET textcol = '' WHERE textcol IS NOT NULL;
SELECT brin_summarize_new_values('brintest'); -- error, not an index
SELECT brin_summarize_new_values('tenk1_unique1'); -- error, not a BRIN index
SELECT brin_summarize_new_values('brinidx'); -- ok, no change expected
+
+-- Check that changing reloptions for brin index is not allowed
+ALTER INDEX brinidx SET (pages_per_range = 10);
+ALTER INDEX brinidx RESET (pages_per_range);
diff --git a/src/test/regress/sql/create_index.sql b/src/test/regress/sql/create_index.sql
index 1648072..1f8d101 100644
--- a/src/test/regress/sql/create_index.sql
+++ b/src/test/regress/sql/create_index.sql
@@ -682,7 +682,7 @@ CREATE INDEX hash_name_index ON hash_name_heap USING hash (random name_ops);
CREATE INDEX hash_txt_index ON hash_txt_heap USING hash (random text_ops);
-CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_ops);
+CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=60);
CREATE UNLOGGED TABLE unlogged_hash_table (id int4);
CREATE INDEX unlogged_hash_index ON unlogged_hash_table USING hash (id int4_ops);
diff --git a/src/test/regress/sql/gist.sql b/src/test/regress/sql/gist.sql
index 9d4ff1e..ce1ac1a 100644
--- a/src/test/regress/sql/gist.sql
+++ b/src/test/regress/sql/gist.sql
@@ -17,6 +17,17 @@ select g+100000, point(g*10+1, g*10+1) from generate_series(1, 10000) g;
-- To test vacuum, delete some entries from all over the index.
delete from gist_point_tbl where id % 2 = 1;
+-- Test buffering and fillfactor reloption takes valid values
+create index gist_pointidx2 on gist_point_tbl using gist(p) with (buffering = on, fillfactor=50);
+create index gist_pointidx3 on gist_point_tbl using gist(p) with (buffering = off);
+create index gist_pointidx4 on gist_point_tbl using gist(p) with (buffering = auto);
+--Test buffering and fillfactor for refising invalid values
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (buffering = invalid_value);
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfactor=9);
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfactor=101);
+
+
+
-- And also delete some concentration of values. (GiST doesn't currently
-- attempt to delete pages even when they become empty, but if it did, this
-- would exercise it)
diff --git a/src/test/regress/sql/hash_index.sql b/src/test/regress/sql/hash_index.sql
index 15a3b06..b034efb 100644
--- a/src/test/regress/sql/hash_index.sql
+++ b/src/test/regress/sql/hash_index.sql
@@ -194,3 +194,8 @@ CREATE TABLE hash_heap_float4 (x float4, y int);
INSERT INTO hash_heap_float4 VALUES (1.1,1);
CREATE INDEX hash_idx ON hash_heap_float4 USING hash (x);
DROP TABLE hash_heap_float4 CASCADE;
+
+-- Check reloptions min max values and that it is not allowed to ALTER
+CREATE INDEX hash_f8_index2 ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=9);
+CREATE INDEX hash_f8_index2 ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=101);
+ALTER INDEX hash_f8_index SET (fillfactor=99);
\ No newline at end of file
diff --git a/src/test/regress/sql/reloptions.sql b/src/test/regress/sql/reloptions.sql
new file mode 100644
index 0000000..0bfec28
--- /dev/null
+++ b/src/test/regress/sql/reloptions.sql
@@ -0,0 +1,153 @@
+
+-- Simple create
+CREATE TABLE reloptions_test(i INT) WITH (fillfactor=30,autovacuum_enabled = false, autovacuum_analyze_scale_factor = 0.2);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+
+-- Test сase insensitive
+CREATE TABLE reloptions_test3(i INT) WITH (FiLlFaCtoR=40);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test3'::regclass;
+
+-- Fail on min/max values check
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=2);
+
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=110);
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor = -10.0);
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor = 110.0);
+
+-- Fail when option and namespase do not exist
+
+CREATE TABLE reloptions_test2(i INT) WITH (not_existing_option=2);
+
+CREATE TABLE reloptions_test2(i INT) WITH (not_existing_namespace.fillfactor=2);
+
+-- Fail while setting unproper value
+
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=30.5);
+
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor='string');
+
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=true);
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled=12);
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled=30.5);
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled='string');
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor='string');
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor=true);
+
+-- Specifing name only should fail as if there was =ture after it
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor);
+
+-- Simple ALTER TABLE
+
+ALTER TABLE reloptions_test SET (fillfactor=31, autovacuum_analyze_scale_factor = 0.3);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+
+-- Check that we cat set boolean option to true just by mentioning it
+
+ALTER TABLE reloptions_test SET (autovacuum_enabled, fillfactor=32);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+
+-- Check that RESET works well
+
+ALTER TABLE reloptions_test RESET (fillfactor);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+
+-- Check that RESETting all values make NULL reloptions record in pg_class
+ALTER TABLE reloptions_test RESET (autovacuum_enabled, autovacuum_analyze_scale_factor);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass AND reloptions IS NULL;
+
+-- Check RESET fails on att=value
+
+ALTER TABLE reloptions_test RESET (fillfactor=12);
+
+-- Check oids options is ignored
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test(i INT) WITH (fillfactor=20, oids=true);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+
+-- Now testing toast.* options
+DROP TABLE reloptions_test;
+
+CREATE TABLE reloptions_test (s VARCHAR) WITH (toast.autovacuum_vacuum_cost_delay = 23 );
+
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+
+ALTER TABLE reloptions_test SET (toast.autovacuum_vacuum_cost_delay = 24);
+
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+
+ALTER TABLE reloptions_test RESET (toast.autovacuum_vacuum_cost_delay);
+
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+
+-- Fail on unexisting options in toast namespace
+CREATE TABLE reloptions_test2 (i int) WITH (toast.not_existing_option = 42 );
+
+-- Fail on setting reloption to a table that does not have a TOAST relation
+CREATE TABLE reloptions_test2 (i int) WITH (toast.autovacuum_vacuum_cost_delay = 23 );
+DROP TABLE reloptions_test;
+
+CREATE TABLE reloptions_test(i INT);
+ALTER TABLE reloptions_test SET (toast.autovacuum_vacuum_cost_delay = 23);
+ALTER TABLE reloptions_test RESET (toast.autovacuum_vacuum_cost_delay);
+
+-- autovacuum_analyze_scale_factor and autovacuum_analyze_threshold should be
+-- accepted by heap but rejected by toast (special case)
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test (s VARCHAR) WITH (autovacuum_analyze_scale_factor=1, autovacuum_analyze_threshold=1);
+
+CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analyze_scale_factor=1);
+CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analyze_threshold=1);
+
+-- And now mixed toast + heap
+DROP TABLE reloptions_test;
+
+CREATE TABLE reloptions_test (s VARCHAR) WITH (toast.autovacuum_vacuum_cost_delay = 23, autovacuum_vacuum_cost_delay = 24, fillfactor = 40);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+
+-- Same FOR CREATE and ALTER INDEX for btree indexes
+
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (fillfactor=30);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx'::regclass;
+
+-- Fail when option and namespase do not exist
+
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_existing_option=2);
+
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_existing_option.fillfactor=2);
+
+-- Check ranges
+
+CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfactor=1);
+
+CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfactor=130);
+
+-- Check alter
+
+ALTER INDEX reloptions_test_idx SET (fillfactor=40);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx'::regclass;
+
+-- Check alter on empty relop list
+
+CREATE INDEX reloptions_test_idx3 ON reloptions_test (s);
+
+ALTER INDEX reloptions_test_idx3 SET (fillfactor=40);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx3'::regclass;
+
+
diff --git a/src/test/regress/sql/spgist.sql b/src/test/regress/sql/spgist.sql
index 5896b50..8d26e49 100644
--- a/src/test/regress/sql/spgist.sql
+++ b/src/test/regress/sql/spgist.sql
@@ -5,7 +5,7 @@
-- testing SP-GiST code itself.
create table spgist_point_tbl(id int4, p point);
-create index spgist_point_idx on spgist_point_tbl using spgist(p);
+create index spgist_point_idx on spgist_point_tbl using spgist(p) with (fillfactor = 75);
-- Test vacuum-root operation. It gets invoked when the root is also a leaf,
-- i.e. the index is very small.
@@ -48,3 +48,10 @@ select g, 'baaaaaaaaaaaaaar' || g from generate_series(1, 1000) g;
-- tuple to be moved to another page.
insert into spgist_text_tbl (id, t)
select -g, 'f' || repeat('o', 100-g) || 'surprise' from generate_series(1, 100) g;
+
+-- Check reloptions min, max values
+create index spgist_point_idx2 on spgist_point_tbl using spgist(p) with (fillfactor = 9);
+create index spgist_point_idx2 on spgist_point_tbl using spgist(p) with (fillfactor = 101);
+
+-- ALTER reloption should work
+alter index spgist_point_idx set (fillfactor = 76);
Hi Nikolay,
On 2017-03-29 15:51:43 +0300, Nikolay Shaplov wrote:
Here is a new version of the patch.
Thanks for the new version of the patch. As this commitfest is running
out of time, and as the patch has been submitted late in the v10
development cycle, I've moved this patch to the next commitfest.
Regards,
Andres
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
В письме от 3 апреля 2017 11:31:28 пользователь Andres Freund написал:
Here is a new version of the patch.
Thanks for the new version of the patch. As this commitfest is running
out of time, and as the patch has been submitted late in the v10
development cycle, I've moved this patch to the next commitfest.
Alas...
But I am quite patient, so here is the same patch reabesed on current master,
And it also includes all changes that have been done to reloptions since
previous one. (autosummarize for brin and no options for partitioned tables)
--
Nikolay Shaplov, independent Perl & C/C++ developer. Available for hire.
Attachments:
reloptions7a.difftext/x-patch; charset=UTF-8; name=reloptions7a.diffDownload
diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h
index 0cfe49a..96edc9a 100644
--- a/contrib/bloom/bloom.h
+++ b/contrib/bloom/bloom.h
@@ -204,7 +204,6 @@ extern IndexBulkDeleteResult *blbulkdelete(IndexVacuumInfo *info,
void *callback_state);
extern IndexBulkDeleteResult *blvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
-extern bytea *bloptions(Datum reloptions, bool validate);
extern void blcostestimate(PlannerInfo *root, IndexPath *path,
double loop_count, Cost *indexStartupCost,
Cost *indexTotalCost, Selectivity *indexSelectivity,
diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.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 cbc50f7..7e3bbc0 100644
--- a/contrib/bloom/expected/bloom.out
+++ b/contrib/bloom/expected/bloom.out
@@ -210,3 +210,37 @@ ORDER BY 1;
text_ops | t
(2 rows)
+-- reloptions test
+DROP INDEX bloomidx;
+CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (length=7, col1 = 4);
+SELECT reloptions FROM pg_class WHERE oid = 'bloomidx'::regclass;
+ reloptions
+-------------------
+ {length=7,col1=4}
+(1 row)
+
+-- check for min and max values
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=0);
+ERROR: value 0 out of bounds for option "length"
+DETAIL: Valid values are between "1" and "4096".
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=4097);
+ERROR: value 4097 out of bounds for option "length"
+DETAIL: Valid values are between "1" and "4096".
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=0);
+ERROR: value 0 out of bounds for option "col1"
+DETAIL: Valid values are between "1" and "4095".
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=4096);
+ERROR: value 4096 out of bounds for option "col1"
+DETAIL: Valid values are between "1" and "4095".
+-- check post_validate for colN<lengh
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=10,col1=11);
+ERROR: col0 should not be grater than length
+-- check that ALTERing of reloptions is forbidden
+ALTER INDEX bloomidx SET (length=4);
+ERROR: changing parameter "length" is not allowed
+ALTER INDEX bloomidx SET (col1=4);
+ERROR: changing parameter "col1" is not allowed
+ALTER INDEX bloomidx RESET (length);
+ERROR: changing parameter "length" is not allowed
+ALTER INDEX bloomidx RESET (col1);
+ERROR: changing parameter "col1" is not allowed
diff --git a/contrib/bloom/sql/bloom.sql b/contrib/bloom/sql/bloom.sql
index 2227460..dbac21c 100644
--- a/contrib/bloom/sql/bloom.sql
+++ b/contrib/bloom/sql/bloom.sql
@@ -81,3 +81,23 @@ SELECT opcname, amvalidate(opc.oid)
FROM pg_opclass opc JOIN pg_am am ON am.oid = opcmethod
WHERE amname = 'bloom'
ORDER BY 1;
+
+-- reloptions test
+
+DROP INDEX bloomidx;
+CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (length=7, col1 = 4);
+SELECT reloptions FROM pg_class WHERE oid = 'bloomidx'::regclass;
+-- check for min and max values
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=0);
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=4097);
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=0);
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=4096);
+-- check post_validate for colN<lengh
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=10,col1=11);
+
+-- check that ALTERing of reloptions is forbidden
+
+ALTER INDEX bloomidx SET (length=4);
+ALTER INDEX bloomidx SET (col1=4);
+ALTER INDEX bloomidx RESET (length);
+ALTER INDEX bloomidx RESET (col1);
diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
index 44b67da..0865dd9 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -1955,7 +1955,7 @@ PG_FUNCTION_INFO_V1(dblink_fdw_validator);
Datum
dblink_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid context = PG_GETARG_OID(1);
ListCell *cell;
diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 277639f..876b8bb 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -193,7 +193,7 @@ file_fdw_handler(PG_FUNCTION_ARGS)
Datum
file_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid catalog = PG_GETARG_OID(1);
char *filename = NULL;
DefElem *force_not_null = NULL;
diff --git a/contrib/postgres_fdw/option.c b/contrib/postgres_fdw/option.c
index e24db56..be15189 100644
--- a/contrib/postgres_fdw/option.c
+++ b/contrib/postgres_fdw/option.c
@@ -65,7 +65,7 @@ PG_FUNCTION_INFO_V1(postgres_fdw_validator);
Datum
postgres_fdw_validator(PG_FUNCTION_ARGS)
{
- List *options_list = untransformRelOptions(PG_GETARG_DATUM(0));
+ List *options_list = optionsTextArrayToDefList(PG_GETARG_DATUM(0));
Oid catalog = PG_GETARG_OID(1);
ListCell *cell;
diff --git a/src/backend/access/brin/brin.c b/src/backend/access/brin/brin.c
index 2594407..0555919 100644
--- a/src/backend/access/brin/brin.c
+++ b/src/backend/access/brin/brin.c
@@ -19,7 +19,7 @@
#include "access/brin_page.h"
#include "access/brin_pageops.h"
#include "access/brin_xlog.h"
-#include "access/reloptions.h"
+#include "access/options.h"
#include "access/relscan.h"
#include "access/xloginsert.h"
#include "catalog/index.h"
@@ -73,6 +73,7 @@ static void union_tuples(BrinDesc *bdesc, BrinMemTuple *a,
BrinTuple *b);
static void brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy);
+static void *bringetreloptcatalog(void);
/*
* BRIN handler function: return IndexAmRoutine with access method parameters
@@ -106,7 +107,6 @@ brinhandler(PG_FUNCTION_ARGS)
amroutine->amvacuumcleanup = brinvacuumcleanup;
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = brincostestimate;
- amroutine->amoptions = brinoptions;
amroutine->amproperty = NULL;
amroutine->amvalidate = brinvalidate;
amroutine->ambeginscan = brinbeginscan;
@@ -116,6 +116,7 @@ brinhandler(PG_FUNCTION_ARGS)
amroutine->amendscan = brinendscan;
amroutine->ammarkpos = NULL;
amroutine->amrestrpos = NULL;
+ amroutine->amrelopt_catalog = bringetreloptcatalog;
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
@@ -797,37 +798,6 @@ brinvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
}
/*
- * reloptions processor for BRIN indexes
- */
-bytea *
-brinoptions(Datum reloptions, bool validate)
-{
- relopt_value *options;
- BrinOptions *rdopts;
- int numoptions;
- static const relopt_parse_elt tab[] = {
- {"pages_per_range", RELOPT_TYPE_INT, offsetof(BrinOptions, pagesPerRange)},
- {"autosummarize", RELOPT_TYPE_BOOL, offsetof(BrinOptions, autosummarize)}
- };
-
- options = parseRelOptions(reloptions, validate, RELOPT_KIND_BRIN,
- &numoptions);
-
- /* if none set, we're done */
- if (numoptions == 0)
- return NULL;
-
- rdopts = allocateReloptStruct(sizeof(BrinOptions), options, numoptions);
-
- fillRelOptions((void *) rdopts, sizeof(BrinOptions), options, numoptions,
- validate, tab, lengthof(tab));
-
- pfree(options);
-
- return (bytea *) rdopts;
-}
-
-/*
* SQL-callable function to scan through an index and summarize all ranges
* that are not currently summarized.
*/
@@ -1424,3 +1394,33 @@ brin_vacuum_scan(Relation idxrel, BufferAccessStrategy strategy)
if (vacuum_fsm)
FreeSpaceMapVacuum(idxrel);
}
+
+static options_catalog *brin_relopt_catalog = NULL;
+
+static void *
+bringetreloptcatalog(void)
+{
+ if (!brin_relopt_catalog)
+ {
+ brin_relopt_catalog = allocateOptionsCatalog(NULL,
+ sizeof(BrinOptions), 2);
+
+ optionsCatalogAddItemInt(brin_relopt_catalog, "pages_per_range",
+ "Number of pages that each page range covers in a BRIN index",
+ NoLock, /* since ALTER is not allowed
+ * no lock needed */
+ OPTION_DEFINITION_FLAG_FORBID_ALTER,
+ offsetof(BrinOptions, pagesPerRange),
+ BRIN_DEFAULT_PAGES_PER_RANGE,
+ BRIN_MIN_PAGES_PER_RANGE,
+ BRIN_MAX_PAGES_PER_RANGE);
+
+ optionsCatalogAddItemBool(brin_relopt_catalog, "autosummarize",
+ "Enables automatic summarization on this BRIN index",
+ AccessExclusiveLock,
+ 0,
+ offsetof(BrinOptions, autosummarize),
+ false);
+ }
+ return brin_relopt_catalog;
+}
diff --git a/src/backend/access/common/Makefile b/src/backend/access/common/Makefile
index fb27944..c9fb063 100644
--- a/src/backend/access/common/Makefile
+++ b/src/backend/access/common/Makefile
@@ -12,7 +12,7 @@ subdir = src/backend/access/common
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
-OBJS = bufmask.o heaptuple.o indextuple.o printsimple.o printtup.o \
+OBJS = bufmask.o heaptuple.o indextuple.o options.o printsimple.o printtup.o \
reloptions.o scankey.o tupconvert.o tupdesc.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/common/options.c b/src/backend/access/common/options.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 6d1f22f..19816d5 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -1,7 +1,10 @@
/*-------------------------------------------------------------------------
*
* reloptions.c
- * Core support for relation options (pg_class.reloptions)
+ * Support for options relotions (pg_class.reloptions).
+ * Reloptions for non-Access Metod relations are defined here.
+ * There is also extractRelOptions function to extract reloptions
+ * from pg_class table for all kind of relations.
*
* Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
@@ -17,928 +20,16 @@
#include <float.h>
-#include "access/gist_private.h"
-#include "access/hash.h"
#include "access/htup_details.h"
-#include "access/nbtree.h"
+#include "access/options.h"
#include "access/reloptions.h"
-#include "access/spgist.h"
-#include "catalog/pg_type.h"
-#include "commands/defrem.h"
#include "commands/tablespace.h"
-#include "commands/view.h"
-#include "nodes/makefuncs.h"
-#include "postmaster/postmaster.h"
-#include "utils/array.h"
#include "utils/attoptcache.h"
-#include "utils/builtins.h"
-#include "utils/guc.h"
-#include "utils/memutils.h"
#include "utils/rel.h"
+#include "storage/bufmgr.h"
-/*
- * Contents of pg_class.reloptions
- *
- * To add an option:
- *
- * (i) decide on a type (integer, real, bool, string), name, default value,
- * upper and lower bounds (if applicable); for strings, consider a validation
- * routine.
- * (ii) add a record below (or use add_<type>_reloption).
- * (iii) add it to the appropriate options struct (perhaps StdRdOptions)
- * (iv) add it to the appropriate handling routine (perhaps
- * default_reloptions)
- * (v) make sure the lock level is set correctly for that operation
- * (vi) don't forget to document the option
- *
- * Note that we don't handle "oids" in relOpts because it is handled by
- * interpretOidsOption().
- *
- * The default choice for any new option should be AccessExclusiveLock.
- * In some cases the lock level can be reduced from there, but the lock
- * level chosen should always conflict with itself to ensure that multiple
- * changes aren't lost when we attempt concurrent changes.
- * The choice of lock level depends completely upon how that parameter
- * is used within the server, not upon how and when you'd like to change it.
- * Safety first. Existing choices are documented here, and elsewhere in
- * backend code where the parameters are used.
- *
- * In general, anything that affects the results obtained from a SELECT must be
- * protected by AccessExclusiveLock.
- *
- * Autovacuum related parameters can be set at ShareUpdateExclusiveLock
- * since they are only used by the AV procs and don't change anything
- * currently executing.
- *
- * Fillfactor can be set because it applies only to subsequent changes made to
- * data blocks, as documented in heapio.c
- *
- * n_distinct options can be set at ShareUpdateExclusiveLock because they
- * are only used during ANALYZE, which uses a ShareUpdateExclusiveLock,
- * so the ANALYZE will not be affected by in-flight changes. Changing those
- * values has no affect until the next ANALYZE, so no need for stronger lock.
- *
- * Planner-related parameters can be set with ShareUpdateExclusiveLock because
- * they only affect planning and not the correctness of the execution. Plans
- * cannot be changed in mid-flight, so changes here could not easily result in
- * new improved plans in any case. So we allow existing queries to continue
- * and existing plans to survive, a small price to pay for allowing better
- * plans to be introduced concurrently without interfering with users.
- *
- * Setting parallel_workers is safe, since it acts the same as
- * max_parallel_workers_per_gather which is a USERSET parameter that doesn't
- * affect existing plans or queries.
- */
-
-static relopt_bool boolRelOpts[] =
-{
- {
- {
- "autosummarize",
- "Enables automatic summarization on this BRIN index",
- RELOPT_KIND_BRIN,
- AccessExclusiveLock
- },
- false
- },
- {
- {
- "autovacuum_enabled",
- "Enables autovacuum in this relation",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
- ShareUpdateExclusiveLock
- },
- true
- },
- {
- {
- "user_catalog_table",
- "Declare a table as an additional catalog table, e.g. for the purpose of logical replication",
- RELOPT_KIND_HEAP,
- AccessExclusiveLock
- },
- false
- },
- {
- {
- "fastupdate",
- "Enables \"fast update\" feature for this GIN index",
- RELOPT_KIND_GIN,
- AccessExclusiveLock
- },
- true
- },
- {
- {
- "security_barrier",
- "View acts as a row security barrier",
- RELOPT_KIND_VIEW,
- AccessExclusiveLock
- },
- false
- },
- /* list terminator */
- {{NULL}}
-};
-
-static relopt_int intRelOpts[] =
-{
- {
- {
- "fillfactor",
- "Packs table pages only to this percentage",
- RELOPT_KIND_HEAP,
- ShareUpdateExclusiveLock /* since it applies only to later
- * inserts */
- },
- HEAP_DEFAULT_FILLFACTOR, HEAP_MIN_FILLFACTOR, 100
- },
- {
- {
- "fillfactor",
- "Packs btree index pages only to this percentage",
- RELOPT_KIND_BTREE,
- ShareUpdateExclusiveLock /* since it applies only to later
- * inserts */
- },
- BTREE_DEFAULT_FILLFACTOR, BTREE_MIN_FILLFACTOR, 100
- },
- {
- {
- "fillfactor",
- "Packs hash index pages only to this percentage",
- RELOPT_KIND_HASH,
- ShareUpdateExclusiveLock /* since it applies only to later
- * inserts */
- },
- HASH_DEFAULT_FILLFACTOR, HASH_MIN_FILLFACTOR, 100
- },
- {
- {
- "fillfactor",
- "Packs gist index pages only to this percentage",
- RELOPT_KIND_GIST,
- ShareUpdateExclusiveLock /* since it applies only to later
- * inserts */
- },
- GIST_DEFAULT_FILLFACTOR, GIST_MIN_FILLFACTOR, 100
- },
- {
- {
- "fillfactor",
- "Packs spgist index pages only to this percentage",
- RELOPT_KIND_SPGIST,
- ShareUpdateExclusiveLock /* since it applies only to later
- * inserts */
- },
- SPGIST_DEFAULT_FILLFACTOR, SPGIST_MIN_FILLFACTOR, 100
- },
- {
- {
- "autovacuum_vacuum_threshold",
- "Minimum number of tuple updates or deletes prior to vacuum",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
- ShareUpdateExclusiveLock
- },
- -1, 0, INT_MAX
- },
- {
- {
- "autovacuum_analyze_threshold",
- "Minimum number of tuple inserts, updates or deletes prior to analyze",
- RELOPT_KIND_HEAP,
- ShareUpdateExclusiveLock
- },
- -1, 0, INT_MAX
- },
- {
- {
- "autovacuum_vacuum_cost_delay",
- "Vacuum cost delay in milliseconds, for autovacuum",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
- ShareUpdateExclusiveLock
- },
- -1, 0, 100
- },
- {
- {
- "autovacuum_vacuum_cost_limit",
- "Vacuum cost amount available before napping, for autovacuum",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
- ShareUpdateExclusiveLock
- },
- -1, 1, 10000
- },
- {
- {
- "autovacuum_freeze_min_age",
- "Minimum age at which VACUUM should freeze a table row, for autovacuum",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
- ShareUpdateExclusiveLock
- },
- -1, 0, 1000000000
- },
- {
- {
- "autovacuum_multixact_freeze_min_age",
- "Minimum multixact age at which VACUUM should freeze a row multixact's, for autovacuum",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
- ShareUpdateExclusiveLock
- },
- -1, 0, 1000000000
- },
- {
- {
- "autovacuum_freeze_max_age",
- "Age at which to autovacuum a table to prevent transaction ID wraparound",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
- ShareUpdateExclusiveLock
- },
- -1, 100000, 2000000000
- },
- {
- {
- "autovacuum_multixact_freeze_max_age",
- "Multixact age at which to autovacuum a table to prevent multixact wraparound",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
- ShareUpdateExclusiveLock
- },
- -1, 10000, 2000000000
- },
- {
- {
- "autovacuum_freeze_table_age",
- "Age at which VACUUM should perform a full table sweep to freeze row versions",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
- ShareUpdateExclusiveLock
- }, -1, 0, 2000000000
- },
- {
- {
- "autovacuum_multixact_freeze_table_age",
- "Age of multixact at which VACUUM should perform a full table sweep to freeze row versions",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
- ShareUpdateExclusiveLock
- }, -1, 0, 2000000000
- },
- {
- {
- "log_autovacuum_min_duration",
- "Sets the minimum execution time above which autovacuum actions will be logged",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
- ShareUpdateExclusiveLock
- },
- -1, -1, INT_MAX
- },
- {
- {
- "pages_per_range",
- "Number of pages that each page range covers in a BRIN index",
- RELOPT_KIND_BRIN,
- AccessExclusiveLock
- }, 128, 1, 131072
- },
- {
- {
- "gin_pending_list_limit",
- "Maximum size of the pending list for this GIN index, in kilobytes.",
- RELOPT_KIND_GIN,
- AccessExclusiveLock
- },
- -1, 64, MAX_KILOBYTES
- },
- {
- {
- "effective_io_concurrency",
- "Number of simultaneous requests that can be handled efficiently by the disk subsystem.",
- RELOPT_KIND_TABLESPACE,
- ShareUpdateExclusiveLock
- },
-#ifdef USE_PREFETCH
- -1, 0, MAX_IO_CONCURRENCY
-#else
- 0, 0, 0
-#endif
- },
- {
- {
- "parallel_workers",
- "Number of parallel processes that can be used per executor node for this relation.",
- RELOPT_KIND_HEAP,
- ShareUpdateExclusiveLock
- },
- -1, 0, 1024
- },
-
- /* list terminator */
- {{NULL}}
-};
-
-static relopt_real realRelOpts[] =
-{
- {
- {
- "autovacuum_vacuum_scale_factor",
- "Number of tuple updates or deletes prior to vacuum as a fraction of reltuples",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
- ShareUpdateExclusiveLock
- },
- -1, 0.0, 100.0
- },
- {
- {
- "autovacuum_analyze_scale_factor",
- "Number of tuple inserts, updates or deletes prior to analyze as a fraction of reltuples",
- RELOPT_KIND_HEAP,
- ShareUpdateExclusiveLock
- },
- -1, 0.0, 100.0
- },
- {
- {
- "seq_page_cost",
- "Sets the planner's estimate of the cost of a sequentially fetched disk page.",
- RELOPT_KIND_TABLESPACE,
- ShareUpdateExclusiveLock
- },
- -1, 0.0, DBL_MAX
- },
- {
- {
- "random_page_cost",
- "Sets the planner's estimate of the cost of a nonsequentially fetched disk page.",
- RELOPT_KIND_TABLESPACE,
- ShareUpdateExclusiveLock
- },
- -1, 0.0, DBL_MAX
- },
- {
- {
- "n_distinct",
- "Sets the planner's estimate of the number of distinct values appearing in a column (excluding child relations).",
- RELOPT_KIND_ATTRIBUTE,
- ShareUpdateExclusiveLock
- },
- 0, -1.0, DBL_MAX
- },
- {
- {
- "n_distinct_inherited",
- "Sets the planner's estimate of the number of distinct values appearing in a column (including child relations).",
- RELOPT_KIND_ATTRIBUTE,
- ShareUpdateExclusiveLock
- },
- 0, -1.0, DBL_MAX
- },
- /* list terminator */
- {{NULL}}
-};
-
-static relopt_string stringRelOpts[] =
-{
- {
- {
- "buffering",
- "Enables buffering build for this GiST index",
- RELOPT_KIND_GIST,
- AccessExclusiveLock
- },
- 4,
- false,
- gistValidateBufferingOption,
- "auto"
- },
- {
- {
- "check_option",
- "View has WITH CHECK OPTION defined (local or cascaded).",
- RELOPT_KIND_VIEW,
- AccessExclusiveLock
- },
- 0,
- true,
- validateWithCheckOption,
- NULL
- },
- /* list terminator */
- {{NULL}}
-};
-
-static relopt_gen **relOpts = NULL;
-static bits32 last_assigned_kind = RELOPT_KIND_LAST_DEFAULT;
-
-static int num_custom_options = 0;
-static relopt_gen **custom_options = NULL;
-static bool need_initialization = true;
-
-static void initialize_reloptions(void);
-static void parse_one_reloption(relopt_value *option, char *text_str,
- int text_len, bool validate);
-
-/*
- * initialize_reloptions
- * initialization routine, must be called before parsing
- *
- * Initialize the relOpts array and fill each variable's type and name length.
- */
-static void
-initialize_reloptions(void)
-{
- int i;
- int j;
-
- j = 0;
- for (i = 0; boolRelOpts[i].gen.name; i++)
- {
- Assert(DoLockModesConflict(boolRelOpts[i].gen.lockmode,
- boolRelOpts[i].gen.lockmode));
- j++;
- }
- for (i = 0; intRelOpts[i].gen.name; i++)
- {
- Assert(DoLockModesConflict(intRelOpts[i].gen.lockmode,
- intRelOpts[i].gen.lockmode));
- j++;
- }
- for (i = 0; realRelOpts[i].gen.name; i++)
- {
- Assert(DoLockModesConflict(realRelOpts[i].gen.lockmode,
- realRelOpts[i].gen.lockmode));
- j++;
- }
- for (i = 0; stringRelOpts[i].gen.name; i++)
- {
- Assert(DoLockModesConflict(stringRelOpts[i].gen.lockmode,
- stringRelOpts[i].gen.lockmode));
- j++;
- }
- j += num_custom_options;
-
- if (relOpts)
- pfree(relOpts);
- relOpts = MemoryContextAlloc(TopMemoryContext,
- (j + 1) * sizeof(relopt_gen *));
-
- j = 0;
- for (i = 0; boolRelOpts[i].gen.name; i++)
- {
- relOpts[j] = &boolRelOpts[i].gen;
- relOpts[j]->type = RELOPT_TYPE_BOOL;
- relOpts[j]->namelen = strlen(relOpts[j]->name);
- j++;
- }
-
- for (i = 0; intRelOpts[i].gen.name; i++)
- {
- relOpts[j] = &intRelOpts[i].gen;
- relOpts[j]->type = RELOPT_TYPE_INT;
- relOpts[j]->namelen = strlen(relOpts[j]->name);
- j++;
- }
-
- for (i = 0; realRelOpts[i].gen.name; i++)
- {
- relOpts[j] = &realRelOpts[i].gen;
- relOpts[j]->type = RELOPT_TYPE_REAL;
- relOpts[j]->namelen = strlen(relOpts[j]->name);
- j++;
- }
-
- for (i = 0; stringRelOpts[i].gen.name; i++)
- {
- relOpts[j] = &stringRelOpts[i].gen;
- relOpts[j]->type = RELOPT_TYPE_STRING;
- relOpts[j]->namelen = strlen(relOpts[j]->name);
- j++;
- }
-
- for (i = 0; i < num_custom_options; i++)
- {
- relOpts[j] = custom_options[i];
- j++;
- }
-
- /* add a list terminator */
- relOpts[j] = NULL;
-
- /* flag the work is complete */
- need_initialization = false;
-}
-
-/*
- * add_reloption_kind
- * Create a new relopt_kind value, to be used in custom reloptions by
- * user-defined AMs.
- */
-relopt_kind
-add_reloption_kind(void)
-{
- /* don't hand out the last bit so that the enum's behavior is portable */
- if (last_assigned_kind >= RELOPT_KIND_MAX)
- ereport(ERROR,
- (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
- errmsg("user-defined relation parameter types limit exceeded")));
- last_assigned_kind <<= 1;
- return (relopt_kind) last_assigned_kind;
-}
-
-/*
- * add_reloption
- * Add an already-created custom reloption to the list, and recompute the
- * main parser table.
- */
-static void
-add_reloption(relopt_gen *newoption)
-{
- static int max_custom_options = 0;
-
- if (num_custom_options >= max_custom_options)
- {
- MemoryContext oldcxt;
-
- oldcxt = MemoryContextSwitchTo(TopMemoryContext);
-
- if (max_custom_options == 0)
- {
- max_custom_options = 8;
- custom_options = palloc(max_custom_options * sizeof(relopt_gen *));
- }
- else
- {
- max_custom_options *= 2;
- custom_options = repalloc(custom_options,
- max_custom_options * sizeof(relopt_gen *));
- }
- MemoryContextSwitchTo(oldcxt);
- }
- custom_options[num_custom_options++] = newoption;
-
- need_initialization = true;
-}
-
-/*
- * allocate_reloption
- * Allocate a new reloption and initialize the type-agnostic fields
- * (for types other than string)
- */
-static relopt_gen *
-allocate_reloption(bits32 kinds, int type, char *name, char *desc)
-{
- MemoryContext oldcxt;
- size_t size;
- relopt_gen *newoption;
-
- oldcxt = MemoryContextSwitchTo(TopMemoryContext);
-
- switch (type)
- {
- case RELOPT_TYPE_BOOL:
- size = sizeof(relopt_bool);
- break;
- case RELOPT_TYPE_INT:
- size = sizeof(relopt_int);
- break;
- case RELOPT_TYPE_REAL:
- size = sizeof(relopt_real);
- break;
- case RELOPT_TYPE_STRING:
- size = sizeof(relopt_string);
- break;
- default:
- elog(ERROR, "unsupported reloption type %d", type);
- return NULL; /* keep compiler quiet */
- }
-
- newoption = palloc(size);
-
- newoption->name = pstrdup(name);
- if (desc)
- newoption->desc = pstrdup(desc);
- else
- newoption->desc = NULL;
- newoption->kinds = kinds;
- newoption->namelen = strlen(name);
- newoption->type = type;
-
- MemoryContextSwitchTo(oldcxt);
-
- return newoption;
-}
-
-/*
- * add_bool_reloption
- * Add a new boolean reloption
- */
-void
-add_bool_reloption(bits32 kinds, char *name, char *desc, bool default_val)
-{
- relopt_bool *newoption;
-
- newoption = (relopt_bool *) allocate_reloption(kinds, RELOPT_TYPE_BOOL,
- name, desc);
- newoption->default_val = default_val;
-
- add_reloption((relopt_gen *) newoption);
-}
-
-/*
- * add_int_reloption
- * Add a new integer reloption
- */
-void
-add_int_reloption(bits32 kinds, char *name, char *desc, int default_val,
- int min_val, int max_val)
-{
- relopt_int *newoption;
-
- newoption = (relopt_int *) allocate_reloption(kinds, RELOPT_TYPE_INT,
- name, desc);
- newoption->default_val = default_val;
- newoption->min = min_val;
- newoption->max = max_val;
-
- add_reloption((relopt_gen *) newoption);
-}
-
-/*
- * add_real_reloption
- * Add a new float reloption
- */
-void
-add_real_reloption(bits32 kinds, char *name, char *desc, double default_val,
- double min_val, double max_val)
-{
- relopt_real *newoption;
-
- newoption = (relopt_real *) allocate_reloption(kinds, RELOPT_TYPE_REAL,
- name, desc);
- newoption->default_val = default_val;
- newoption->min = min_val;
- newoption->max = max_val;
-
- add_reloption((relopt_gen *) newoption);
-}
-
-/*
- * add_string_reloption
- * Add a new string reloption
- *
- * "validator" is an optional function pointer that can be used to test the
- * validity of the values. It must elog(ERROR) when the argument string is
- * not acceptable for the variable. Note that the default value must pass
- * the validation.
- */
-void
-add_string_reloption(bits32 kinds, char *name, char *desc, char *default_val,
- validate_string_relopt validator)
-{
- relopt_string *newoption;
-
- /* make sure the validator/default combination is sane */
- if (validator)
- (validator) (default_val);
-
- newoption = (relopt_string *) allocate_reloption(kinds, RELOPT_TYPE_STRING,
- name, desc);
- newoption->validate_cb = validator;
- if (default_val)
- {
- newoption->default_val = MemoryContextStrdup(TopMemoryContext,
- default_val);
- newoption->default_len = strlen(default_val);
- newoption->default_isnull = false;
- }
- else
- {
- newoption->default_val = "";
- newoption->default_len = 0;
- newoption->default_isnull = true;
- }
-
- add_reloption((relopt_gen *) newoption);
-}
-
-/*
- * Transform a relation options list (list of DefElem) into the text array
- * format that is kept in pg_class.reloptions, including only those options
- * that are in the passed namespace. The output values do not include the
- * namespace.
- *
- * This is used for three cases: CREATE TABLE/INDEX, ALTER TABLE SET, and
- * ALTER TABLE RESET. In the ALTER cases, oldOptions is the existing
- * reloptions value (possibly NULL), and we replace or remove entries
- * as needed.
- *
- * If ignoreOids is true, then we should ignore any occurrence of "oids"
- * in the list (it will be or has been handled by interpretOidsOption()).
- *
- * Note that this is not responsible for determining whether the options
- * are valid, but it does check that namespaces for all the options given are
- * listed in validnsps. The NULL namespace is always valid and need not be
- * explicitly listed. Passing a NULL pointer means that only the NULL
- * namespace is valid.
- *
- * Both oldOptions and the result are text arrays (or NULL for "default"),
- * but we declare them as Datums to avoid including array.h in reloptions.h.
- */
-Datum
-transformRelOptions(Datum oldOptions, List *defList, char *namspace,
- char *validnsps[], bool ignoreOids, bool isReset)
-{
- Datum result;
- ArrayBuildState *astate;
- ListCell *cell;
-
- /* no change if empty list */
- if (defList == NIL)
- return oldOptions;
-
- /* We build new array using accumArrayResult */
- astate = NULL;
-
- /* Copy any oldOptions that aren't to be replaced */
- if (PointerIsValid(DatumGetPointer(oldOptions)))
- {
- ArrayType *array = DatumGetArrayTypeP(oldOptions);
- Datum *oldoptions;
- int noldoptions;
- int i;
-
- deconstruct_array(array, TEXTOID, -1, false, 'i',
- &oldoptions, NULL, &noldoptions);
-
- for (i = 0; i < noldoptions; i++)
- {
- char *text_str = VARDATA(oldoptions[i]);
- int text_len = VARSIZE(oldoptions[i]) - VARHDRSZ;
-
- /* Search for a match in defList */
- foreach(cell, defList)
- {
- DefElem *def = (DefElem *) lfirst(cell);
- int kw_len;
-
- /* ignore if not in the same namespace */
- if (namspace == NULL)
- {
- if (def->defnamespace != NULL)
- continue;
- }
- else if (def->defnamespace == NULL)
- continue;
- else if (pg_strcasecmp(def->defnamespace, namspace) != 0)
- continue;
-
- kw_len = strlen(def->defname);
- if (text_len > kw_len && text_str[kw_len] == '=' &&
- pg_strncasecmp(text_str, def->defname, kw_len) == 0)
- break;
- }
- if (!cell)
- {
- /* No match, so keep old option */
- astate = accumArrayResult(astate, oldoptions[i],
- false, TEXTOID,
- CurrentMemoryContext);
- }
- }
- }
-
- /*
- * If CREATE/SET, add new options to array; if RESET, just check that the
- * user didn't say RESET (option=val). (Must do this because the grammar
- * doesn't enforce it.)
- */
- foreach(cell, defList)
- {
- DefElem *def = (DefElem *) lfirst(cell);
-
- if (isReset)
- {
- if (def->arg != NULL)
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("RESET must not include values for parameters")));
- }
- else
- {
- text *t;
- const char *value;
- Size len;
-
- /*
- * Error out if the namespace is not valid. A NULL namespace is
- * always valid.
- */
- if (def->defnamespace != NULL)
- {
- bool valid = false;
- int i;
-
- if (validnsps)
- {
- for (i = 0; validnsps[i]; i++)
- {
- if (pg_strcasecmp(def->defnamespace,
- validnsps[i]) == 0)
- {
- valid = true;
- break;
- }
- }
- }
-
- if (!valid)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("unrecognized parameter namespace \"%s\"",
- def->defnamespace)));
- }
-
- if (ignoreOids && pg_strcasecmp(def->defname, "oids") == 0)
- continue;
-
- /* ignore if not in the same namespace */
- if (namspace == NULL)
- {
- if (def->defnamespace != NULL)
- continue;
- }
- else if (def->defnamespace == NULL)
- continue;
- else if (pg_strcasecmp(def->defnamespace, namspace) != 0)
- continue;
-
- /*
- * Flatten the DefElem into a text string like "name=arg". If we
- * have just "name", assume "name=true" is meant. Note: the
- * namespace is not output.
- */
- if (def->arg != NULL)
- value = defGetString(def);
- else
- value = "true";
- len = VARHDRSZ + strlen(def->defname) + 1 + strlen(value);
- /* +1 leaves room for sprintf's trailing null */
- t = (text *) palloc(len + 1);
- SET_VARSIZE(t, len);
- sprintf(VARDATA(t), "%s=%s", def->defname, value);
-
- astate = accumArrayResult(astate, PointerGetDatum(t),
- false, TEXTOID,
- CurrentMemoryContext);
- }
- }
-
- if (astate)
- result = makeArrayResult(astate, CurrentMemoryContext);
- else
- result = (Datum) 0;
-
- return result;
-}
-
-
-/*
- * Convert the text-array format of reloptions into a List of DefElem.
- * This is the inverse of transformRelOptions().
- */
-List *
-untransformRelOptions(Datum options)
-{
- List *result = NIL;
- ArrayType *array;
- Datum *optiondatums;
- int noptions;
- int i;
-
- /* Nothing to do if no options */
- if (!PointerIsValid(DatumGetPointer(options)))
- return result;
-
- array = DatumGetArrayTypeP(options);
-
- deconstruct_array(array, TEXTOID, -1, false, 'i',
- &optiondatums, NULL, &noptions);
-
- for (i = 0; i < noptions; i++)
- {
- char *s;
- char *p;
- Node *val = NULL;
-
- s = TextDatumGetCString(optiondatums[i]);
- p = strchr(s, '=');
- if (p)
- {
- *p++ = '\0';
- val = (Node *) makeString(pstrdup(p));
- }
- result = lappend(result, makeDefElem(pstrdup(s), val, -1));
- }
-
- return result;
-}
+void add_autovacuum_options(options_catalog * catalog, int base_offset,
+ bool for_toast);
/*
* Extract and parse reloptions from a pg_class tuple.
@@ -954,11 +45,12 @@ untransformRelOptions(Datum options)
*/
bytea *
extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
- amoptions_function amoptions)
+ amrelopt_catalog_function catalog_fun)
{
- bytea *options;
+ bytea *options = NULL;
bool isnull;
Datum datum;
+ options_catalog *catalog = NULL;
Form_pg_class classForm;
datum = fastgetattr(tuple,
@@ -974,548 +66,321 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
switch (classForm->relkind)
{
case RELKIND_RELATION:
- case RELKIND_TOASTVALUE:
case RELKIND_MATVIEW:
- case RELKIND_PARTITIONED_TABLE:
- options = heap_reloptions(classForm->relkind, datum, false);
+ catalog = get_heap_relopt_catalog();
+ break;
+ case RELKIND_TOASTVALUE:
+ catalog = get_toast_relopt_catalog();
break;
case RELKIND_VIEW:
- options = view_reloptions(datum, false);
+ catalog = get_view_relopt_catalog();
break;
case RELKIND_INDEX:
- options = index_reloptions(amoptions, datum, false);
+ if (catalog_fun)
+ catalog = catalog_fun();
break;
case RELKIND_FOREIGN_TABLE:
options = NULL;
break;
+ case RELKIND_PARTITIONED_TABLE:
+ catalog = NULL; /* No options for parted table for now */
+ break;
default:
Assert(false); /* can't get here */
options = NULL; /* keep compiler quiet */
break;
}
+ if (catalog)
+ options = optionsTextArrayToBytea(catalog, datum);
return options;
}
-/*
- * Interpret reloptions that are given in text-array format.
- *
- * options is a reloption text array as constructed by transformRelOptions.
- * kind specifies the family of options to be processed.
- *
- * The return value is a relopt_value * array on which the options actually
- * set in the options array are marked with isset=true. The length of this
- * array is returned in *numrelopts. Options not set are also present in the
- * array; this is so that the caller can easily locate the default values.
- *
- * If there are no options of the given kind, numrelopts is set to 0 and NULL
- * is returned (unless options are illegally supplied despite none being
- * defined, in which case an error occurs).
- *
- * Note: values of type int, bool and real are allocated as part of the
- * returned array. Values of type string are allocated separately and must
- * be freed by the caller.
- */
-relopt_value *
-parseRelOptions(Datum options, bool validate, relopt_kind kind,
- int *numrelopts)
-{
- relopt_value *reloptions = NULL;
- int numoptions = 0;
- int i;
- int j;
-
- if (need_initialization)
- initialize_reloptions();
-
- /* Build a list of expected options, based on kind */
-
- for (i = 0; relOpts[i]; i++)
- if (relOpts[i]->kinds & kind)
- numoptions++;
-
- if (numoptions > 0)
- {
- reloptions = palloc(numoptions * sizeof(relopt_value));
-
- for (i = 0, j = 0; relOpts[i]; i++)
- {
- if (relOpts[i]->kinds & kind)
- {
- reloptions[j].gen = relOpts[i];
- reloptions[j].isset = false;
- j++;
- }
- }
- }
-
- /* Done if no options */
- if (PointerIsValid(DatumGetPointer(options)))
- {
- ArrayType *array = DatumGetArrayTypeP(options);
- Datum *optiondatums;
- int noptions;
-
- deconstruct_array(array, TEXTOID, -1, false, 'i',
- &optiondatums, NULL, &noptions);
-
- for (i = 0; i < noptions; i++)
- {
- char *text_str = VARDATA(optiondatums[i]);
- int text_len = VARSIZE(optiondatums[i]) - VARHDRSZ;
- int j;
-
- /* Search for a match in reloptions */
- for (j = 0; j < numoptions; j++)
- {
- int kw_len = reloptions[j].gen->namelen;
-
- if (text_len > kw_len && text_str[kw_len] == '=' &&
- pg_strncasecmp(text_str, reloptions[j].gen->name,
- kw_len) == 0)
- {
- parse_one_reloption(&reloptions[j], text_str, text_len,
- validate);
- break;
- }
- }
-
- if (j >= numoptions && validate)
- {
- char *s;
- char *p;
-
- s = TextDatumGetCString(optiondatums[i]);
- p = strchr(s, '=');
- if (p)
- *p = '\0';
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("unrecognized parameter \"%s\"", s)));
- }
- }
-
- /* It's worth avoiding memory leaks in this function */
- pfree(optiondatums);
- if (((void *) array) != DatumGetPointer(options))
- pfree(array);
- }
-
- *numrelopts = numoptions;
- return reloptions;
-}
/*
- * Subroutine for parseRelOptions, to parse and validate a single option's
- * value
+ * Here goes functions that build catalogs for non-index relations. Catalogs
+ * for index relations are available from Access Methods, all other
+ * relation catalogs are here
*/
-static void
-parse_one_reloption(relopt_value *option, char *text_str, int text_len,
- bool validate)
-{
- char *value;
- int value_len;
- bool parsed;
- bool nofree = false;
-
- if (option->isset && validate)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("parameter \"%s\" specified more than once",
- option->gen->name)));
-
- value_len = text_len - option->gen->namelen - 1;
- value = (char *) palloc(value_len + 1);
- memcpy(value, text_str + option->gen->namelen + 1, value_len);
- value[value_len] = '\0';
-
- switch (option->gen->type)
- {
- case RELOPT_TYPE_BOOL:
- {
- parsed = parse_bool(value, &option->values.bool_val);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for boolean option \"%s\": %s",
- option->gen->name, value)));
- }
- break;
- case RELOPT_TYPE_INT:
- {
- relopt_int *optint = (relopt_int *) option->gen;
-
- parsed = parse_int(value, &option->values.int_val, 0, NULL);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for integer option \"%s\": %s",
- option->gen->name, value)));
- if (validate && (option->values.int_val < optint->min ||
- option->values.int_val > optint->max))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("value %s out of bounds for option \"%s\"",
- value, option->gen->name),
- errdetail("Valid values are between \"%d\" and \"%d\".",
- optint->min, optint->max)));
- }
- break;
- case RELOPT_TYPE_REAL:
- {
- relopt_real *optreal = (relopt_real *) option->gen;
-
- parsed = parse_real(value, &option->values.real_val);
- if (validate && !parsed)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for floating point option \"%s\": %s",
- option->gen->name, value)));
- if (validate && (option->values.real_val < optreal->min ||
- option->values.real_val > optreal->max))
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("value %s out of bounds for option \"%s\"",
- value, option->gen->name),
- errdetail("Valid values are between \"%f\" and \"%f\".",
- optreal->min, optreal->max)));
- }
- break;
- case RELOPT_TYPE_STRING:
- {
- relopt_string *optstring = (relopt_string *) option->gen;
-
- option->values.string_val = value;
- nofree = true;
- if (validate && optstring->validate_cb)
- (optstring->validate_cb) (value);
- parsed = true;
- }
- break;
- default:
- elog(ERROR, "unsupported reloption type %d", option->gen->type);
- parsed = true; /* quiet compiler */
- break;
- }
-
- if (parsed)
- option->isset = true;
- if (!nofree)
- pfree(value);
-}
/*
- * Given the result from parseRelOptions, allocate a struct that's of the
- * specified base size plus any extra space that's needed for string variables.
+ * Both heap and toast relation have almost the same autovacuum set of options.
+ * These autovacuum options are stored in the same structure (AutoVacOpts)
+ * that is a part of both HeapOptions and ToastOptions.
*
- * "base" should be sizeof(struct) of the reloptions struct (StdRdOptions or
- * equivalent).
- */
-void *
-allocateReloptStruct(Size base, relopt_value *options, int numoptions)
-{
- Size size = base;
- int i;
-
- for (i = 0; i < numoptions; i++)
- if (options[i].gen->type == RELOPT_TYPE_STRING)
- size += GET_STRING_RELOPTION_LEN(options[i]) + 1;
-
- return palloc0(size);
-}
-
-/*
- * Given the result of parseRelOptions and a parsing table, fill in the
- * struct (previously allocated with allocateReloptStruct) with the parsed
- * values.
+ * Function add_autovacuum_options adds to the "catalog" catalog autovacuum
+ * options definition. In binary mode this options are saved into AutoVacOpts
+ * structure, which is located in result bytea chunk with base_offset offset.
*
- * rdopts is the pointer to the allocated struct to be filled.
- * basesize is the sizeof(struct) that was passed to allocateReloptStruct.
- * options, of length numoptions, is parseRelOptions' output.
- * elems, of length numelems, is the table describing the allowed options.
- * When validate is true, it is expected that all options appear in elems.
+ * Heap has two options (autovacuum_analyze_threshold and
+ * autovacuum_analyze_scale_factor) that is not used for toast. So for toast
+ * case this options are added to catalog with OPTION_DEFINITION_FLAG_REJECT
+ * flag set, so postgres will reject this toast options from the CREATE or
+ * ALTER command, but still will save default value in binary representation
*/
+
void
-fillRelOptions(void *rdopts, Size basesize,
- relopt_value *options, int numoptions,
- bool validate,
- const relopt_parse_elt *elems, int numelems)
+add_autovacuum_options(options_catalog * catalog, int base_offset,
+ bool for_toast)
{
- int i;
- int offset = basesize;
-
- for (i = 0; i < numoptions; i++)
- {
- int j;
- bool found = false;
-
- for (j = 0; j < numelems; j++)
- {
- if (pg_strcasecmp(options[i].gen->name, elems[j].optname) == 0)
- {
- relopt_string *optstring;
- char *itempos = ((char *) rdopts) + elems[j].offset;
- char *string_val;
-
- switch (options[i].gen->type)
- {
- case RELOPT_TYPE_BOOL:
- *(bool *) itempos = options[i].isset ?
- options[i].values.bool_val :
- ((relopt_bool *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_INT:
- *(int *) itempos = options[i].isset ?
- options[i].values.int_val :
- ((relopt_int *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_REAL:
- *(double *) itempos = options[i].isset ?
- options[i].values.real_val :
- ((relopt_real *) options[i].gen)->default_val;
- break;
- case RELOPT_TYPE_STRING:
- optstring = (relopt_string *) options[i].gen;
- if (options[i].isset)
- string_val = options[i].values.string_val;
- else if (!optstring->default_isnull)
- string_val = optstring->default_val;
- else
- string_val = NULL;
-
- if (string_val == NULL)
- *(int *) itempos = 0;
- else
- {
- strcpy((char *) rdopts + offset, string_val);
- *(int *) itempos = offset;
- offset += strlen(string_val) + 1;
- }
- break;
- default:
- elog(ERROR, "unsupported reloption type %d",
- options[i].gen->type);
- break;
- }
- found = true;
- break;
- }
- }
- if (validate && !found)
- elog(ERROR, "reloption \"%s\" not found in parse table",
- options[i].gen->name);
- }
- SET_VARSIZE(rdopts, offset);
+ optionsCatalogAddItemBool(catalog, "autovacuum_enabled",
+ "Enables autovacuum in this relation",
+ ShareUpdateExclusiveLock,
+ 0, base_offset + offsetof(AutoVacOpts, enabled), true);
+
+ optionsCatalogAddItemInt(catalog, "autovacuum_vacuum_threshold",
+ "Minimum number of tuple updates or deletes prior to vacuum",
+ ShareUpdateExclusiveLock,
+ 0, base_offset + offsetof(AutoVacOpts, vacuum_threshold),
+ -1, 0, INT_MAX);
+
+ optionsCatalogAddItemInt(catalog, "autovacuum_analyze_threshold",
+ "Minimum number of tuple updates or deletes prior to vacuum",
+ ShareUpdateExclusiveLock,
+ for_toast ? OPTION_DEFINITION_FLAG_REJECT : 0,
+ base_offset + offsetof(AutoVacOpts, analyze_threshold),
+ -1, 0, INT_MAX);
+
+ optionsCatalogAddItemInt(catalog, "autovacuum_vacuum_cost_delay",
+ "Vacuum cost delay in milliseconds, for autovacuum",
+ ShareUpdateExclusiveLock,
+ 0, base_offset + offsetof(AutoVacOpts, vacuum_cost_delay),
+ -1, 0, 100);
+
+ optionsCatalogAddItemInt(catalog, "autovacuum_vacuum_cost_limit",
+ "Vacuum cost amount available before napping, for autovacuum",
+ ShareUpdateExclusiveLock,
+ 0, base_offset + offsetof(AutoVacOpts, vacuum_cost_limit),
+ -1, 0, 10000);
+
+ optionsCatalogAddItemInt(catalog, "autovacuum_freeze_min_age",
+ "Minimum age at which VACUUM should freeze a table row, for autovacuum",
+ ShareUpdateExclusiveLock,
+ 0, base_offset + offsetof(AutoVacOpts, freeze_min_age),
+ -1, 0, 1000000000);
+
+ optionsCatalogAddItemInt(catalog, "autovacuum_freeze_max_age",
+ "Age at which to autovacuum a table to prevent transaction ID wraparound",
+ ShareUpdateExclusiveLock,
+ 0, base_offset + offsetof(AutoVacOpts, freeze_max_age),
+ -1, 100000, 2000000000);
+
+ optionsCatalogAddItemInt(catalog, "autovacuum_freeze_table_age",
+ "Age at which VACUUM should perform a full table sweep to freeze row versions",
+ ShareUpdateExclusiveLock,
+ 0, base_offset + offsetof(AutoVacOpts, freeze_table_age),
+ -1, 0, 2000000000);
+
+ optionsCatalogAddItemInt(catalog, "autovacuum_multixact_freeze_min_age",
+ "Minimum multixact age at which VACUUM should freeze a row multixact's, for autovacuum",
+ ShareUpdateExclusiveLock,
+ 0, base_offset + offsetof(AutoVacOpts, multixact_freeze_min_age),
+ -1, 0, 1000000000);
+
+ optionsCatalogAddItemInt(catalog, "autovacuum_multixact_freeze_max_age",
+ "Multixact age at which to autovacuum a table to prevent multixact wraparound",
+ ShareUpdateExclusiveLock,
+ 0, base_offset + offsetof(AutoVacOpts, multixact_freeze_max_age),
+ -1, 10000, 2000000000);
+
+ optionsCatalogAddItemInt(catalog, "autovacuum_multixact_freeze_table_age",
+ "Age of multixact at which VACUUM should perform a full table sweep to freeze row versions",
+ ShareUpdateExclusiveLock,
+ 0, base_offset + offsetof(AutoVacOpts, multixact_freeze_table_age),
+ -1, 0, 2000000000);
+
+ optionsCatalogAddItemInt(catalog, "log_autovacuum_min_duration",
+ "Sets the minimum execution time above which autovacuum actions will be logged",
+ ShareUpdateExclusiveLock,
+ 0, base_offset + offsetof(AutoVacOpts, log_min_duration),
+ -1, -1, INT_MAX);
+
+ optionsCatalogAddItemReal(catalog, "autovacuum_vacuum_scale_factor",
+ "Number of tuple updates or deletes prior to vacuum as a fraction of reltuples",
+ ShareUpdateExclusiveLock,
+ 0, base_offset + offsetof(AutoVacOpts, vacuum_scale_factor),
+ -1, 0.0, 100.0);
+
+ optionsCatalogAddItemReal(catalog, "autovacuum_analyze_scale_factor",
+ "Number of tuple inserts, updates or deletes prior to analyze as a fraction of reltuples",
+ ShareUpdateExclusiveLock,
+ for_toast ? OPTION_DEFINITION_FLAG_REJECT : 0,
+ base_offset + offsetof(AutoVacOpts, analyze_scale_factor),
+ -1, 0.0, 100.0);
}
-
/*
- * Option parser for anything that uses StdRdOptions.
+ * get_heap_relopt_catalog
+ * Returns an options catalog for heap relation.
*/
-bytea *
-default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
-{
- relopt_value *options;
- StdRdOptions *rdopts;
- int numoptions;
- static const relopt_parse_elt tab[] = {
- {"fillfactor", RELOPT_TYPE_INT, offsetof(StdRdOptions, fillfactor)},
- {"autovacuum_enabled", RELOPT_TYPE_BOOL,
- offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, enabled)},
- {"autovacuum_vacuum_threshold", RELOPT_TYPE_INT,
- offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, vacuum_threshold)},
- {"autovacuum_analyze_threshold", RELOPT_TYPE_INT,
- offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, analyze_threshold)},
- {"autovacuum_vacuum_cost_delay", RELOPT_TYPE_INT,
- offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, vacuum_cost_delay)},
- {"autovacuum_vacuum_cost_limit", RELOPT_TYPE_INT,
- offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, vacuum_cost_limit)},
- {"autovacuum_freeze_min_age", RELOPT_TYPE_INT,
- offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, freeze_min_age)},
- {"autovacuum_freeze_max_age", RELOPT_TYPE_INT,
- offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, freeze_max_age)},
- {"autovacuum_freeze_table_age", RELOPT_TYPE_INT,
- offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, freeze_table_age)},
- {"autovacuum_multixact_freeze_min_age", RELOPT_TYPE_INT,
- offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, multixact_freeze_min_age)},
- {"autovacuum_multixact_freeze_max_age", RELOPT_TYPE_INT,
- offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, multixact_freeze_max_age)},
- {"autovacuum_multixact_freeze_table_age", RELOPT_TYPE_INT,
- offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, multixact_freeze_table_age)},
- {"log_autovacuum_min_duration", RELOPT_TYPE_INT,
- offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, log_min_duration)},
- {"autovacuum_vacuum_scale_factor", RELOPT_TYPE_REAL,
- offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, vacuum_scale_factor)},
- {"autovacuum_analyze_scale_factor", RELOPT_TYPE_REAL,
- offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, analyze_scale_factor)},
- {"user_catalog_table", RELOPT_TYPE_BOOL,
- offsetof(StdRdOptions, user_catalog_table)},
- {"parallel_workers", RELOPT_TYPE_INT,
- offsetof(StdRdOptions, parallel_workers)}
- };
+static options_catalog *heap_relopt_catalog = NULL;
- options = parseRelOptions(reloptions, validate, kind, &numoptions);
-
- /* if none set, we're done */
- if (numoptions == 0)
- return NULL;
-
- rdopts = allocateReloptStruct(sizeof(StdRdOptions), options, numoptions);
-
- fillRelOptions((void *) rdopts, sizeof(StdRdOptions), options, numoptions,
- validate, tab, lengthof(tab));
-
- pfree(options);
-
- return (bytea *) rdopts;
+options_catalog *
+get_heap_relopt_catalog(void)
+{
+ if (!heap_relopt_catalog)
+ {
+ heap_relopt_catalog = allocateOptionsCatalog(NULL, sizeof(HeapOptions),
+ 14 + 4); /* 14 - for autovacuum
+ * options, 4 - core
+ * heap options */
+
+ optionsCatalogAddItemInt(heap_relopt_catalog, "fillfactor",
+ "Packs table pages only to this percentag",
+ ShareUpdateExclusiveLock, /* since it applies only
+ * to later inserts */
+ 0, offsetof(HeapOptions, fillfactor),
+ HEAP_DEFAULT_FILLFACTOR, HEAP_MIN_FILLFACTOR, 100);
+
+ add_autovacuum_options(heap_relopt_catalog,
+ offsetof(HeapOptions, autovacuum), false);
+
+ optionsCatalogAddItemBool(heap_relopt_catalog, "user_catalog_table",
+ "Declare a table as an additional catalog table, e.g. for the purpose of logical replication",
+ AccessExclusiveLock,
+ 0, offsetof(HeapOptions, user_catalog_table),
+ false);
+
+ optionsCatalogAddItemInt(heap_relopt_catalog, "parallel_workers",
+ "Number of parallel processes that can be used per executor node for this relation",
+ ShareUpdateExclusiveLock,
+ 0, offsetof(HeapOptions, parallel_workers),
+ -1, 0, 1024);
+
+ /*
+ * A WITH OID / WITHOUT OIDS expressions are converted by syntax
+ * parser into "oids" relation option, but this option is processed by
+ * interpretOidsOption(), that does not use core options code. That is
+ * why here we should just ignore this option as if it does not exist.
+ * Do it with OPTION_DEFINITION_FLAG_IGNORE flag
+ */
+ optionsCatalogAddItemBool(heap_relopt_catalog, "oids",
+ "Option used for WITH OIDS expression. Processed elsewhere",
+ NoLock,
+ OPTION_DEFINITION_FLAG_IGNORE, -1,
+ false);
+ }
+ return heap_relopt_catalog;
}
/*
- * Option parser for views
+ * get_toast_relopt_catalog
+ * Returns an options catalog for toast relation.
*/
-bytea *
-view_reloptions(Datum reloptions, bool validate)
-{
- relopt_value *options;
- ViewOptions *vopts;
- int numoptions;
- static const relopt_parse_elt tab[] = {
- {"security_barrier", RELOPT_TYPE_BOOL,
- offsetof(ViewOptions, security_barrier)},
- {"check_option", RELOPT_TYPE_STRING,
- offsetof(ViewOptions, check_option_offset)}
- };
-
- options = parseRelOptions(reloptions, validate, RELOPT_KIND_VIEW, &numoptions);
-
- /* if none set, we're done */
- if (numoptions == 0)
- return NULL;
- vopts = allocateReloptStruct(sizeof(ViewOptions), options, numoptions);
+static options_catalog *toast_relopt_catalog = NULL;
- fillRelOptions((void *) vopts, sizeof(ViewOptions), options, numoptions,
- validate, tab, lengthof(tab));
-
- pfree(options);
-
- return (bytea *) vopts;
-}
-
-/*
- * Parse options for heaps, views and toast tables.
- */
-bytea *
-heap_reloptions(char relkind, Datum reloptions, bool validate)
+options_catalog *
+get_toast_relopt_catalog(void)
{
- StdRdOptions *rdopts;
-
- switch (relkind)
+ if (!toast_relopt_catalog)
{
- case RELKIND_TOASTVALUE:
- rdopts = (StdRdOptions *)
- default_reloptions(reloptions, validate, RELOPT_KIND_TOAST);
- if (rdopts != NULL)
- {
- /* adjust default-only parameters for TOAST relations */
- rdopts->fillfactor = 100;
- rdopts->autovacuum.analyze_threshold = -1;
- rdopts->autovacuum.analyze_scale_factor = -1;
- }
- return (bytea *) rdopts;
- case RELKIND_RELATION:
- case RELKIND_MATVIEW:
- return default_reloptions(reloptions, validate, RELOPT_KIND_HEAP);
- case RELKIND_PARTITIONED_TABLE:
- return default_reloptions(reloptions, validate,
- RELOPT_KIND_PARTITIONED);
- default:
- /* other relkinds are not supported */
- return NULL;
+ toast_relopt_catalog = allocateOptionsCatalog("toast", sizeof(ToastOptions),
+ 14); /* 14 - for autovacuum
+ * options */
+ add_autovacuum_options(toast_relopt_catalog,
+ offsetof(ToastOptions, autovacuum), true);
}
+ return toast_relopt_catalog;
}
-
/*
- * Parse options for indexes.
- *
- * amoptions index AM's option parser function
- * reloptions options as text[] datum
- * validate error flag
+ * get_view_relopt_catalog
+ * Returns an options catalog for view relation.
*/
-bytea *
-index_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
+static options_catalog *view_relopt_catalog = NULL;
+
+options_catalog *
+get_view_relopt_catalog(void)
{
- Assert(amoptions != NULL);
+ static const char *enum_names[] = VIEW_OPTION_CHECK_OPTION_VALUE_NAMES;
- /* Assume function is strict */
- if (!PointerIsValid(DatumGetPointer(reloptions)))
- return NULL;
+ if (!view_relopt_catalog)
+ {
+ view_relopt_catalog = allocateOptionsCatalog(NULL,
+ sizeof(ViewOptions), 2);
- return amoptions(reloptions, validate);
+ optionsCatalogAddItemBool(view_relopt_catalog, "security_barrier",
+ "View acts as a row security barrier",
+ AccessExclusiveLock,
+ 0, offsetof(ViewOptions, security_barrier), false);
+
+ optionsCatalogAddItemEnum(view_relopt_catalog, "check_option",
+ "View has WITH CHECK OPTION defined (local or cascaded)",
+ AccessExclusiveLock, 0,
+ offsetof(ViewOptions, check_option),
+ enum_names,
+ VIEW_OPTION_CHECK_OPTION_NOT_SET);
+ }
+ return view_relopt_catalog;
}
/*
- * Option parser for attribute reloptions
+ * get_attribute_options_catalog
+ * Returns an options catalog for heap attributes
*/
-bytea *
-attribute_reloptions(Datum reloptions, bool validate)
-{
- relopt_value *options;
- AttributeOpts *aopts;
- int numoptions;
- static const relopt_parse_elt tab[] = {
- {"n_distinct", RELOPT_TYPE_REAL, offsetof(AttributeOpts, n_distinct)},
- {"n_distinct_inherited", RELOPT_TYPE_REAL, offsetof(AttributeOpts, n_distinct_inherited)}
- };
-
- options = parseRelOptions(reloptions, validate, RELOPT_KIND_ATTRIBUTE,
- &numoptions);
-
- /* if none set, we're done */
- if (numoptions == 0)
- return NULL;
-
- aopts = allocateReloptStruct(sizeof(AttributeOpts), options, numoptions);
+static options_catalog *attribute_options_catalog = NULL;
- fillRelOptions((void *) aopts, sizeof(AttributeOpts), options, numoptions,
- validate, tab, lengthof(tab));
+options_catalog *
+get_attribute_options_catalog(void)
+{
+ if (!attribute_options_catalog)
+ {
+ attribute_options_catalog = allocateOptionsCatalog(NULL,
+ sizeof(AttributeOpts), 2);
- pfree(options);
+ optionsCatalogAddItemReal(attribute_options_catalog, "n_distinct",
+ "Sets the planner's estimate of the number of distinct values appearing in a column (excluding child relations)",
+ ShareUpdateExclusiveLock,
+ 0, offsetof(AttributeOpts, n_distinct), 0, -1.0, DBL_MAX);
- return (bytea *) aopts;
+ optionsCatalogAddItemReal(attribute_options_catalog,
+ "n_distinct_inherited",
+ "Sets the planner's estimate of the number of distinct values appearing in a column (including child relations",
+ ShareUpdateExclusiveLock,
+ 0, offsetof(AttributeOpts, n_distinct_inherited), 0, -1.0, DBL_MAX);
+ }
+ return attribute_options_catalog;
}
/*
- * Option parser for tablespace reloptions
+ * get_tablespace_options_catalog
+ * Returns an options catalog for tablespaces
*/
-bytea *
-tablespace_reloptions(Datum reloptions, bool validate)
-{
- relopt_value *options;
- TableSpaceOpts *tsopts;
- int numoptions;
- static const relopt_parse_elt tab[] = {
- {"random_page_cost", RELOPT_TYPE_REAL, offsetof(TableSpaceOpts, random_page_cost)},
- {"seq_page_cost", RELOPT_TYPE_REAL, offsetof(TableSpaceOpts, seq_page_cost)},
- {"effective_io_concurrency", RELOPT_TYPE_INT, offsetof(TableSpaceOpts, effective_io_concurrency)}
- };
-
- options = parseRelOptions(reloptions, validate, RELOPT_KIND_TABLESPACE,
- &numoptions);
-
- /* if none set, we're done */
- if (numoptions == 0)
- return NULL;
-
- tsopts = allocateReloptStruct(sizeof(TableSpaceOpts), options, numoptions);
-
- fillRelOptions((void *) tsopts, sizeof(TableSpaceOpts), options, numoptions,
- validate, tab, lengthof(tab));
-
- pfree(options);
+static options_catalog *tablespace_options_catalog = NULL;
- return (bytea *) tsopts;
+options_catalog *
+get_tablespace_options_catalog(void)
+{
+ if (!tablespace_options_catalog)
+ {
+ tablespace_options_catalog = allocateOptionsCatalog(NULL,
+ sizeof(TableSpaceOpts), 3);
+
+ optionsCatalogAddItemReal(tablespace_options_catalog,
+ "random_page_cost",
+ "Sets the planner's estimate of the cost of a nonsequentially fetched disk page",
+ ShareUpdateExclusiveLock,
+ 0, offsetof(TableSpaceOpts, random_page_cost), -1, 0.0, DBL_MAX);
+
+ optionsCatalogAddItemReal(tablespace_options_catalog, "seq_page_cost",
+ "Sets the planner's estimate of the cost of a sequentially fetched disk page",
+ ShareUpdateExclusiveLock,
+ 0, offsetof(TableSpaceOpts, seq_page_cost), -1, 0.0, DBL_MAX);
+
+ optionsCatalogAddItemInt(tablespace_options_catalog,
+ "effective_io_concurrency",
+ "Number of simultaneous requests that can be handled efficiently by the disk subsystem",
+ ShareUpdateExclusiveLock,
+ 0, offsetof(TableSpaceOpts, effective_io_concurrency),
+#ifdef USE_PREFETCH
+ -1, 0, MAX_IO_CONCURRENCY
+#else
+ 0, 0, 0
+#endif
+ );
+ }
+ return tablespace_options_catalog;
}
/*
@@ -1525,33 +390,56 @@ tablespace_reloptions(Datum reloptions, bool validate)
* for a longer explanation of how this works.
*/
LOCKMODE
-AlterTableGetRelOptionsLockLevel(List *defList)
+AlterTableGetRelOptionsLockLevel(Relation rel, List *defList)
{
LOCKMODE lockmode = NoLock;
ListCell *cell;
+ options_catalog *catalog = NULL;
if (defList == NIL)
return AccessExclusiveLock;
- if (need_initialization)
- initialize_reloptions();
+ switch (rel->rd_rel->relkind)
+ {
+ case RELKIND_TOASTVALUE:
+ catalog = get_toast_relopt_catalog();
+ break;
+ case RELKIND_RELATION:
+ case RELKIND_MATVIEW:
+ catalog = get_heap_relopt_catalog();
+ break;
+ case RELKIND_INDEX:
+ catalog = rel->rd_amroutine->amrelopt_catalog();
+ break;
+ case RELKIND_VIEW:
+ catalog = get_view_relopt_catalog();
+ break;
+ case RELKIND_PARTITIONED_TABLE:
+ catalog = NULL; /* No options for parted table for now */
+ break;
+ default:
+ Assert(false); /* can't get here */
+ break;
+ }
+
+ Assert(catalog); /* No catalog - no reloption change. Should
+ * not get here */
foreach(cell, defList)
{
DefElem *def = (DefElem *) lfirst(cell);
+
int i;
- for (i = 0; relOpts[i]; i++)
+ for (i = 0; i < catalog->num; i++)
{
- if (pg_strncasecmp(relOpts[i]->name,
- def->defname,
- relOpts[i]->namelen + 1) == 0)
- {
- if (lockmode < relOpts[i]->lockmode)
- lockmode = relOpts[i]->lockmode;
- }
+ option_definition_basic *gen = catalog->definitions[i];
+
+ if (pg_strcasecmp(gen->name,
+ def->defname) == 0)
+ if (lockmode < gen->lockmode)
+ lockmode = gen->lockmode;
}
}
-
return lockmode;
}
diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c
index d03d59d..a942738 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -16,7 +16,7 @@
#include "access/gin_private.h"
#include "access/ginxlog.h"
-#include "access/reloptions.h"
+#include "access/options.h"
#include "access/xloginsert.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_type.h"
@@ -26,7 +26,8 @@
#include "utils/builtins.h"
#include "utils/index_selfuncs.h"
#include "utils/typcache.h"
-
+#include "utils/memutils.h"
+#include "utils/guc.h"
/*
* GIN handler function: return IndexAmRoutine with access method parameters
@@ -60,7 +61,6 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amvacuumcleanup = ginvacuumcleanup;
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = gincostestimate;
- amroutine->amoptions = ginoptions;
amroutine->amproperty = NULL;
amroutine->amvalidate = ginvalidate;
amroutine->ambeginscan = ginbeginscan;
@@ -70,6 +70,7 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amendscan = ginendscan;
amroutine->ammarkpos = NULL;
amroutine->amrestrpos = NULL;
+ amroutine->amrelopt_catalog = gingetreloptcatalog;
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
@@ -593,35 +594,6 @@ ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
return entries;
}
-bytea *
-ginoptions(Datum reloptions, bool validate)
-{
- relopt_value *options;
- GinOptions *rdopts;
- int numoptions;
- static const relopt_parse_elt tab[] = {
- {"fastupdate", RELOPT_TYPE_BOOL, offsetof(GinOptions, useFastUpdate)},
- {"gin_pending_list_limit", RELOPT_TYPE_INT, offsetof(GinOptions,
- pendingListCleanupSize)}
- };
-
- options = parseRelOptions(reloptions, validate, RELOPT_KIND_GIN,
- &numoptions);
-
- /* if none set, we're done */
- if (numoptions == 0)
- return NULL;
-
- rdopts = allocateReloptStruct(sizeof(GinOptions), options, numoptions);
-
- fillRelOptions((void *) rdopts, sizeof(GinOptions), options, numoptions,
- validate, tab, lengthof(tab));
-
- pfree(options);
-
- return (bytea *) rdopts;
-}
-
/*
* Fetch index's statistical data into *stats
*
@@ -698,3 +670,32 @@ ginUpdateStats(Relation index, const GinStatsData *stats)
END_CRIT_SECTION();
}
+
+
+static options_catalog *gin_relopt_catalog = NULL;
+
+void *
+gingetreloptcatalog(void)
+{
+ if (!gin_relopt_catalog)
+ {
+ gin_relopt_catalog = allocateOptionsCatalog(NULL,
+ sizeof(GinOptions), 2);
+
+ optionsCatalogAddItemBool(gin_relopt_catalog, "fastupdate",
+ "Enables \"fast update\" feature for this GIN index",
+ AccessExclusiveLock,
+ 0,
+ offsetof(GinOptions, useFastUpdate),
+ GIN_DEFAULT_USE_FASTUPDATE);
+
+ optionsCatalogAddItemInt(gin_relopt_catalog, "gin_pending_list_limit",
+ "Maximum size of the pending list for this GIN index, in kilobytes",
+ AccessExclusiveLock,
+ 0,
+ offsetof(GinOptions, pendingListCleanupSize),
+ -1, 64, MAX_KILOBYTES);
+
+ }
+ return gin_relopt_catalog;
+}
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index 6593771..c366c77 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -81,7 +81,6 @@ gisthandler(PG_FUNCTION_ARGS)
amroutine->amvacuumcleanup = gistvacuumcleanup;
amroutine->amcanreturn = gistcanreturn;
amroutine->amcostestimate = gistcostestimate;
- amroutine->amoptions = gistoptions;
amroutine->amproperty = gistproperty;
amroutine->amvalidate = gistvalidate;
amroutine->ambeginscan = gistbeginscan;
@@ -91,6 +90,7 @@ gisthandler(PG_FUNCTION_ARGS)
amroutine->amendscan = gistendscan;
amroutine->ammarkpos = NULL;
amroutine->amrestrpos = NULL;
+ amroutine->amrelopt_catalog = gistgetreloptcatalog;
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
diff --git a/src/backend/access/gist/gistbuild.c b/src/backend/access/gist/gistbuild.c
index f1f08bb..09d0c68 100644
--- a/src/backend/access/gist/gistbuild.c
+++ b/src/backend/access/gist/gistbuild.c
@@ -126,11 +126,10 @@ gistbuild(Relation heap, Relation index, IndexInfo *indexInfo)
{
/* Get buffering mode from the options string */
GiSTOptions *options = (GiSTOptions *) index->rd_options;
- char *bufferingMode = (char *) options + options->bufferingModeOffset;
- if (strcmp(bufferingMode, "on") == 0)
+ if (options->buffering_mode == GIST_OPTION_BUFFERING_ON)
buildstate.bufferingMode = GIST_BUFFERING_STATS;
- else if (strcmp(bufferingMode, "off") == 0)
+ else if (options->buffering_mode == GIST_OPTION_BUFFERING_OFF)
buildstate.bufferingMode = GIST_BUFFERING_DISABLED;
else
buildstate.bufferingMode = GIST_BUFFERING_AUTO;
@@ -234,25 +233,6 @@ gistbuild(Relation heap, Relation index, IndexInfo *indexInfo)
}
/*
- * Validator for "buffering" reloption on GiST indexes. Allows "on", "off"
- * and "auto" values.
- */
-void
-gistValidateBufferingOption(char *value)
-{
- if (value == NULL ||
- (strcmp(value, "on") != 0 &&
- strcmp(value, "off") != 0 &&
- strcmp(value, "auto") != 0))
- {
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid value for \"buffering\" option"),
- errdetail("Valid values are \"on\", \"off\", and \"auto\".")));
- }
-}
-
-/*
* Attempt to switch to buffering mode.
*
* If there is not enough memory for buffering build, sets bufferingMode
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
index cbdaec9..fddb863 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -18,14 +18,13 @@
#include "access/gist_private.h"
#include "access/htup_details.h"
-#include "access/reloptions.h"
+#include "access/options.h"
#include "catalog/pg_opclass.h"
#include "storage/indexfsm.h"
#include "storage/lmgr.h"
#include "utils/builtins.h"
#include "utils/syscache.h"
-
/*
* Write itup vector to page, has no control of free space.
*/
@@ -812,34 +811,6 @@ gistNewBuffer(Relation r)
return buffer;
}
-bytea *
-gistoptions(Datum reloptions, bool validate)
-{
- relopt_value *options;
- GiSTOptions *rdopts;
- int numoptions;
- static const relopt_parse_elt tab[] = {
- {"fillfactor", RELOPT_TYPE_INT, offsetof(GiSTOptions, fillfactor)},
- {"buffering", RELOPT_TYPE_STRING, offsetof(GiSTOptions, bufferingModeOffset)}
- };
-
- options = parseRelOptions(reloptions, validate, RELOPT_KIND_GIST,
- &numoptions);
-
- /* if none set, we're done */
- if (numoptions == 0)
- return NULL;
-
- rdopts = allocateReloptStruct(sizeof(GiSTOptions), options, numoptions);
-
- fillRelOptions((void *) rdopts, sizeof(GiSTOptions), options, numoptions,
- validate, tab, lengthof(tab));
-
- pfree(options);
-
- return (bytea *) rdopts;
-}
-
/*
* gistproperty() -- Check boolean properties of indexes.
*
@@ -965,3 +936,33 @@ gistGetFakeLSN(Relation rel)
return GetFakeLSNForUnloggedRel();
}
}
+
+static options_catalog *gist_relopt_catalog = NULL;
+
+void *
+gistgetreloptcatalog(void)
+{
+ static const char *enum_names[] = GIST_OPTION_BUFFERING_VALUE_NAMES;
+
+ if (!gist_relopt_catalog)
+ {
+ gist_relopt_catalog = allocateOptionsCatalog(NULL,
+ sizeof(GiSTOptions), 2);
+
+ optionsCatalogAddItemInt(gist_relopt_catalog, "fillfactor",
+ "Packs gist index pages only to this percentage",
+ NoLock, /* No ALTER, no lock */
+ OPTION_DEFINITION_FLAG_FORBID_ALTER,
+ offsetof(GiSTOptions, fillfactor),
+ GIST_DEFAULT_FILLFACTOR,
+ GIST_MIN_FILLFACTOR, 100);
+ optionsCatalogAddItemEnum(gist_relopt_catalog, "buffering",
+ "Enables buffering build for this GiST index",
+ NoLock, /* No ALTER, no lock */
+ OPTION_DEFINITION_FLAG_FORBID_ALTER,
+ offsetof(GiSTOptions, buffering_mode),
+ enum_names,
+ GIST_OPTION_BUFFERING_AUTO);
+ }
+ return gist_relopt_catalog;
+}
diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c
index 3eb5b1d..2091dbd 100644
--- a/src/backend/access/hash/hash.c
+++ b/src/backend/access/hash/hash.c
@@ -79,7 +79,6 @@ hashhandler(PG_FUNCTION_ARGS)
amroutine->amvacuumcleanup = hashvacuumcleanup;
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = hashcostestimate;
- amroutine->amoptions = hashoptions;
amroutine->amproperty = NULL;
amroutine->amvalidate = hashvalidate;
amroutine->ambeginscan = hashbeginscan;
@@ -89,6 +88,7 @@ hashhandler(PG_FUNCTION_ARGS)
amroutine->amendscan = hashendscan;
amroutine->ammarkpos = NULL;
amroutine->amrestrpos = NULL;
+ amroutine->amrelopt_catalog = hashgetreloptcatalog;
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
diff --git a/src/backend/access/hash/hashpage.c b/src/backend/access/hash/hashpage.c
index 3cd4daa..bf87861 100644
--- a/src/backend/access/hash/hashpage.c
+++ b/src/backend/access/hash/hashpage.c
@@ -360,7 +360,7 @@ _hash_init(Relation rel, double num_tuples, ForkNumber forkNum)
data_width = sizeof(uint32);
item_width = MAXALIGN(sizeof(IndexTupleData)) + MAXALIGN(data_width) +
sizeof(ItemIdData); /* include the line pointer */
- ffactor = RelationGetTargetPageUsage(rel, HASH_DEFAULT_FILLFACTOR) / item_width;
+ ffactor = (BLCKSZ * HashGetFillFactor(rel) / 100) / item_width;
/* keep to a sane range */
if (ffactor < 10)
ffactor = 10;
diff --git a/src/backend/access/hash/hashutil.c b/src/backend/access/hash/hashutil.c
index 9f832f2..131c5d1 100644
--- a/src/backend/access/hash/hashutil.c
+++ b/src/backend/access/hash/hashutil.c
@@ -15,7 +15,7 @@
#include "postgres.h"
#include "access/hash.h"
-#include "access/reloptions.h"
+#include "access/options.h"
#include "access/relscan.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
@@ -286,12 +286,6 @@ _hash_checkpage(Relation rel, Buffer buf, int flags)
}
}
-bytea *
-hashoptions(Datum reloptions, bool validate)
-{
- return default_reloptions(reloptions, validate, RELOPT_KIND_HASH);
-}
-
/*
* _hash_get_indextuple_hashkey - get the hash index tuple's hash key value
*/
@@ -580,3 +574,28 @@ _hash_kill_items(IndexScanDesc scan)
MarkBufferDirtyHint(so->hashso_curbuf, true);
}
}
+
+static options_catalog *hash_relopt_catalog = NULL;
+
+void *
+hashgetreloptcatalog(void)
+{
+ if (!hash_relopt_catalog)
+ {
+ hash_relopt_catalog = allocateOptionsCatalog(NULL,
+ sizeof(HashRelOptions), 1);
+
+ optionsCatalogAddItemInt(hash_relopt_catalog, "fillfactor",
+ "Packs hash index pages only to this percentage",
+ NoLock, /* No ALTER -- no lock */
+ OPTION_DEFINITION_FLAG_FORBID_ALTER, /* fillfactor is actualy
+ * stored in metapage
+ * and should not be
+ * changed once index is
+ * created */
+ offsetof(HashRelOptions, fillfactor),
+ HASH_DEFAULT_FILLFACTOR,
+ HASH_MIN_FILLFACTOR, 100);
+ }
+ return hash_relopt_catalog;
+}
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 0c3e2b0..1d0e762 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -2670,7 +2670,10 @@ heap_multi_insert(Relation relation, HeapTuple *tuples, int ntuples,
bool need_cids = RelationIsAccessibleInLogicalDecoding(relation);
needwal = !(options & HEAP_INSERT_SKIP_WAL) && RelationNeedsWAL(relation);
- saveFreeSpace = RelationGetTargetPageFreeSpace(relation,
+ if (IsToastRelation(relation))
+ saveFreeSpace = ToastGetTargetPageFreeSpace();
+ else
+ saveFreeSpace = HeapGetTargetPageFreeSpace(relation,
HEAP_DEFAULT_FILLFACTOR);
/* Toast and set header data in all the tuples */
diff --git a/src/backend/access/heap/hio.c b/src/backend/access/heap/hio.c
index 6529fe3..6668bd2 100644
--- a/src/backend/access/heap/hio.c
+++ b/src/backend/access/heap/hio.c
@@ -323,7 +323,10 @@ RelationGetBufferForTuple(Relation relation, Size len,
len, MaxHeapTupleSize)));
/* Compute desired extra freespace due to fillfactor option */
- saveFreeSpace = RelationGetTargetPageFreeSpace(relation,
+ if (IsToastRelation(relation))
+ saveFreeSpace = ToastGetTargetPageFreeSpace();
+ else
+ saveFreeSpace = HeapGetTargetPageFreeSpace(relation,
HEAP_DEFAULT_FILLFACTOR);
if (otherBuffer != InvalidBuffer)
diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c
index d69a266..9735ec7 100644
--- a/src/backend/access/heap/pruneheap.c
+++ b/src/backend/access/heap/pruneheap.c
@@ -131,7 +131,11 @@ heap_page_prune_opt(Relation relation, Buffer buffer)
* important than sometimes getting a wrong answer in what is after all
* just a heuristic estimate.
*/
- minfree = RelationGetTargetPageFreeSpace(relation,
+
+ if (IsToastRelation(relation))
+ minfree = ToastGetTargetPageFreeSpace();
+ else
+ minfree = HeapGetTargetPageFreeSpace(relation,
HEAP_DEFAULT_FILLFACTOR);
minfree = Max(minfree, BLCKSZ / 10);
diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c
index 60dcb67..6bd6df0 100644
--- a/src/backend/access/heap/rewriteheap.c
+++ b/src/backend/access/heap/rewriteheap.c
@@ -667,7 +667,10 @@ raw_heap_insert(RewriteState state, HeapTuple tup)
len, MaxHeapTupleSize)));
/* Compute desired extra freespace due to fillfactor option */
- saveFreeSpace = RelationGetTargetPageFreeSpace(state->rs_new_rel,
+ if (IsToastRelation(state->rs_new_rel))
+ saveFreeSpace = ToastGetTargetPageFreeSpace();
+ else
+ saveFreeSpace = HeapGetTargetPageFreeSpace(state->rs_new_rel,
HEAP_DEFAULT_FILLFACTOR);
/* Now we can check to see if there's enough free space already. */
diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access/nbtree/nbtinsert.c
index 6dca810..6b12304 100644
--- a/src/backend/access/nbtree/nbtinsert.c
+++ b/src/backend/access/nbtree/nbtinsert.c
@@ -1433,8 +1433,7 @@ _bt_findsplitloc(Relation rel,
state.is_rightmost = P_RIGHTMOST(opaque);
state.have_split = false;
if (state.is_leaf)
- state.fillfactor = RelationGetFillFactor(rel,
- BTREE_DEFAULT_FILLFACTOR);
+ state.fillfactor = BTGetFillFactor(rel);
else
state.fillfactor = BTREE_NONLEAF_FILLFACTOR;
state.newitemonleft = false; /* these just to keep compiler quiet */
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index 775f2ff..27d274e 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -21,6 +21,7 @@
#include "access/nbtree.h"
#include "access/relscan.h"
#include "access/xlog.h"
+#include "access/reloptions.h"
#include "catalog/index.h"
#include "commands/vacuum.h"
#include "pgstat.h"
@@ -34,7 +35,6 @@
#include "utils/index_selfuncs.h"
#include "utils/memutils.h"
-
/* Working state for btbuild and its callback */
typedef struct
{
@@ -116,8 +116,7 @@ static void btvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
BTCycleId cycleid);
static void btvacuumpage(BTVacState *vstate, BlockNumber blkno,
BlockNumber orig_blkno);
-
-
+static void * btgetreloptcatalog (void);
/*
* Btree handler function: return IndexAmRoutine with access method parameters
* and callbacks.
@@ -150,7 +149,6 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amvacuumcleanup = btvacuumcleanup;
amroutine->amcanreturn = btcanreturn;
amroutine->amcostestimate = btcostestimate;
- amroutine->amoptions = btoptions;
amroutine->amproperty = btproperty;
amroutine->amvalidate = btvalidate;
amroutine->ambeginscan = btbeginscan;
@@ -160,6 +158,7 @@ bthandler(PG_FUNCTION_ARGS)
amroutine->amendscan = btendscan;
amroutine->ammarkpos = btmarkpos;
amroutine->amrestrpos = btrestrpos;
+ amroutine->amrelopt_catalog = btgetreloptcatalog;
amroutine->amestimateparallelscan = btestimateparallelscan;
amroutine->aminitparallelscan = btinitparallelscan;
amroutine->amparallelrescan = btparallelrescan;
@@ -1391,3 +1390,26 @@ btcanreturn(Relation index, int attno)
{
return true;
}
+
+
+static options_catalog *bt_relopt_catalog = NULL;
+
+static void *
+btgetreloptcatalog(void)
+{
+ if (!bt_relopt_catalog)
+ {
+ bt_relopt_catalog = allocateOptionsCatalog(NULL,
+ sizeof(BTRelOptions), 1);
+
+ optionsCatalogAddItemInt(bt_relopt_catalog, "fillfactor",
+ "Packs btree index pages only to this percentage",
+ ShareUpdateExclusiveLock, /* since it applies only
+ * to later inserts */
+ 0,
+ offsetof(BTRelOptions, fillfactor),
+ BTREE_DEFAULT_FILLFACTOR,
+ BTREE_MIN_FILLFACTOR, 100);
+ }
+ return bt_relopt_catalog;
+}
diff --git a/src/backend/access/nbtree/nbtsort.c b/src/backend/access/nbtree/nbtsort.c
index 3d041c4..92ce827 100644
--- a/src/backend/access/nbtree/nbtsort.c
+++ b/src/backend/access/nbtree/nbtsort.c
@@ -344,8 +344,8 @@ _bt_pagestate(BTWriteState *wstate, uint32 level)
if (level > 0)
state->btps_full = (BLCKSZ * (100 - BTREE_NONLEAF_FILLFACTOR) / 100);
else
- state->btps_full = RelationGetTargetPageFreeSpace(wstate->index,
- BTREE_DEFAULT_FILLFACTOR);
+ state->btps_full = (BLCKSZ * (100 - BTGetFillFactor(wstate->index)) / 100);
+
/* no parent level, yet */
state->btps_next = NULL;
diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c
index 5b259a3..867d3e6 100644
--- a/src/backend/access/nbtree/nbtutils.c
+++ b/src/backend/access/nbtree/nbtutils.c
@@ -18,7 +18,7 @@
#include <time.h>
#include "access/nbtree.h"
-#include "access/reloptions.h"
+#include "storage/lock.h"
#include "access/relscan.h"
#include "miscadmin.h"
#include "utils/array.h"
@@ -2038,12 +2038,6 @@ BTreeShmemInit(void)
Assert(found);
}
-bytea *
-btoptions(Datum reloptions, bool validate)
-{
- return default_reloptions(reloptions, validate, RELOPT_KIND_BTREE);
-}
-
/*
* btproperty() -- Check boolean properties of indexes.
*
diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/spgist/spgutils.c
index e57ac49..a6416d7 100644
--- a/src/backend/access/spgist/spgutils.c
+++ b/src/backend/access/spgist/spgutils.c
@@ -15,7 +15,7 @@
#include "postgres.h"
-#include "access/reloptions.h"
+#include "access/options.h"
#include "access/spgist_private.h"
#include "access/transam.h"
#include "access/xact.h"
@@ -59,7 +59,6 @@ spghandler(PG_FUNCTION_ARGS)
amroutine->amvacuumcleanup = spgvacuumcleanup;
amroutine->amcanreturn = spgcanreturn;
amroutine->amcostestimate = spgcostestimate;
- amroutine->amoptions = spgoptions;
amroutine->amproperty = NULL;
amroutine->amvalidate = spgvalidate;
amroutine->ambeginscan = spgbeginscan;
@@ -69,6 +68,7 @@ spghandler(PG_FUNCTION_ARGS)
amroutine->amendscan = spgendscan;
amroutine->ammarkpos = NULL;
amroutine->amrestrpos = NULL;
+ amroutine->amrelopt_catalog = spggetreloptcatalog;
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
@@ -372,8 +372,8 @@ SpGistGetBuffer(Relation index, int flags, int needSpace, bool *isNew)
* related to the ones already on it. But fillfactor mustn't cause an
* error for requests that would otherwise be legal.
*/
- needSpace += RelationGetTargetPageFreeSpace(index,
- SPGIST_DEFAULT_FILLFACTOR);
+ needSpace += (BLCKSZ * (100 - SpGistGetFillFactor(index)) / 100);
+
needSpace = Min(needSpace, SPGIST_PAGE_CAPACITY);
/* Get the cache entry for this flags setting */
@@ -537,15 +537,6 @@ SpGistInitMetapage(Page page)
}
/*
- * reloptions processing for SPGiST
- */
-bytea *
-spgoptions(Datum reloptions, bool validate)
-{
- return default_reloptions(reloptions, validate, RELOPT_KIND_SPGIST);
-}
-
-/*
* Get the space needed to store a non-null datum of the indicated type.
* Note the result is already rounded up to a MAXALIGN boundary.
* Also, we follow the SPGiST convention that pass-by-val types are
@@ -911,3 +902,27 @@ SpGistPageAddNewItem(SpGistState *state, Page page, Item item, Size size,
return offnum;
}
+
+
+
+static options_catalog *spgist_relopt_catalog = NULL;
+
+void *
+spggetreloptcatalog(void)
+{
+ if (!spgist_relopt_catalog)
+ {
+ spgist_relopt_catalog = allocateOptionsCatalog(NULL,
+ sizeof(SpGistRelOptions), 1);
+
+ optionsCatalogAddItemInt(spgist_relopt_catalog, "fillfactor",
+ "Packs spgist index pages only to this percentage",
+ ShareUpdateExclusiveLock, /* since it applies only
+ * to later inserts */
+ 0,
+ offsetof(SpGistRelOptions, fillfactor),
+ SPGIST_DEFAULT_FILLFACTOR,
+ SPGIST_MIN_FILLFACTOR, 100);
+ }
+ return spgist_relopt_catalog;
+}
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 29756eb..aaa9270 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -36,7 +36,7 @@
/* Potentially set by pg_upgrade_support functions */
Oid binary_upgrade_next_toast_pg_type_oid = InvalidOid;
-static void CheckAndCreateToastTable(Oid relOid, Datum reloptions,
+static bool CheckAndCreateToastTable(Oid relOid, Datum reloptions,
LOCKMODE lockmode, bool check);
static bool create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
Datum reloptions, LOCKMODE lockmode, bool check);
@@ -67,23 +67,26 @@ NewHeapCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmode)
CheckAndCreateToastTable(relOid, reloptions, lockmode, false);
}
-void
+bool
NewRelationCreateToastTable(Oid relOid, Datum reloptions)
{
- CheckAndCreateToastTable(relOid, reloptions, AccessExclusiveLock, false);
+ return CheckAndCreateToastTable(relOid, reloptions,
+ AccessExclusiveLock, false);
}
-static void
+static bool
CheckAndCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmode, bool check)
{
Relation rel;
+ bool success;
rel = heap_open(relOid, lockmode);
/* create_toast_table does all the work */
- (void) create_toast_table(rel, InvalidOid, InvalidOid, reloptions, lockmode, check);
+ success = create_toast_table(rel, InvalidOid, InvalidOid, reloptions, lockmode, check);
heap_close(rel, NoLock);
+ return success;
}
/*
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 06425cc..52f4c49 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -89,6 +89,8 @@ create_ctas_internal(List *attrList, IntoClause *into)
Datum toast_options;
static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
ObjectAddress intoRelationAddr;
+ List *toastDefList;
+ bool toast_created;
/* This code supports both CREATE TABLE AS and CREATE MATERIALIZED VIEW */
is_matview = (into->viewQuery != NULL);
@@ -122,15 +124,20 @@ create_ctas_internal(List *attrList, IntoClause *into)
CommandCounterIncrement();
/* parse and validate reloptions for the toast table */
- toast_options = transformRelOptions((Datum) 0,
- create->options,
- "toast",
- validnsps,
- true, false);
- (void) heap_reloptions(RELKIND_TOASTVALUE, toast_options, true);
- NewRelationCreateToastTable(intoRelationAddr.objectId, toast_options);
+ optionsDefListValdateNamespaces(create->options, validnsps);
+ toastDefList = optionsDefListFilterNamespaces(create->options, "toast");
+
+ toast_options = transformOptions(get_toast_relopt_catalog(), (Datum) 0,
+ toastDefList, 0);
+
+ toast_created = NewRelationCreateToastTable(intoRelationAddr.objectId,
+ toast_options);
+ if (!toast_created && toast_options)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("no TOAST relation was created for a table. Can't set toast.* storage parameters")));
/* Create the "view" part of a materialized view. */
if (is_matview)
diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c
index 96cf296..65652f9 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -111,7 +111,7 @@ transformGenericOptions(Oid catalogId,
List *options,
Oid fdwvalidator)
{
- List *resultOptions = untransformRelOptions(oldOptions);
+ List *resultOptions = optionsTextArrayToDefList(oldOptions);
ListCell *optcell;
Datum result;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 4861799..48be80b 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -325,7 +325,7 @@ DefineIndex(Oid relationId,
Form_pg_am accessMethodForm;
IndexAmRoutine *amRoutine;
bool amcanorder;
- amoptions_function amoptions;
+ amrelopt_catalog_function amoption_catalog;
Datum reloptions;
int16 *coloptions;
IndexInfo *indexInfo;
@@ -523,7 +523,7 @@ DefineIndex(Oid relationId,
accessMethodName)));
amcanorder = amRoutine->amcanorder;
- amoptions = amRoutine->amoptions;
+ amoption_catalog = amRoutine->amrelopt_catalog;
pfree(amRoutine);
ReleaseSysCache(tuple);
@@ -537,10 +537,18 @@ DefineIndex(Oid relationId,
/*
* Parse AM-specific options, convert to text array form, validate.
*/
- reloptions = transformRelOptions((Datum) 0, stmt->options,
- NULL, NULL, false, false);
-
- (void) index_reloptions(amoptions, reloptions, true);
+ if (amoption_catalog)
+ {
+ reloptions = transformOptions(amoption_catalog(),
+ (Datum) 0, stmt->options, 0);
+ }
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("access method %s does not support options",
+ accessMethodName)));
+ }
/*
* Prepare arguments for index_create, primarily an IndexInfo structure.
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index a02904c..23e6716 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -505,7 +505,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
Datum reloptions;
ListCell *listptr;
AttrNumber attnum;
- static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
Oid ofTypeId;
ObjectAddress address;
@@ -591,13 +590,23 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
/*
* Parse and validate reloptions, if any.
*/
- reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
- true, false);
-
if (relkind == RELKIND_VIEW)
- (void) view_reloptions(reloptions, true);
+ {
+ reloptions = transformOptions(
+ get_view_relopt_catalog(),
+ (Datum) 0, stmt->options, 0);
+ }
else
- (void) heap_reloptions(relkind, reloptions, true);
+ {
+ /* If it is not view, then it if heap */
+ char *namespaces[] = HEAP_RELOPT_NAMESPACES;
+ List *heapDefList;
+
+ optionsDefListValdateNamespaces(stmt->options, namespaces);
+ heapDefList = optionsDefListFilterNamespaces(stmt->options, NULL);
+ reloptions = transformOptions(get_heap_relopt_catalog(),
+ (Datum) 0, heapDefList, 0);
+ }
if (stmt->ofTypename)
{
@@ -3117,7 +3126,8 @@ void
AlterTableInternal(Oid relid, List *cmds, bool recurse)
{
Relation rel;
- LOCKMODE lockmode = AlterTableGetLockLevel(cmds);
+
+ LOCKMODE lockmode = AlterTableGetLockLevel(relid, cmds);
rel = relation_open(relid, lockmode);
@@ -3159,13 +3169,15 @@ AlterTableInternal(Oid relid, List *cmds, bool recurse)
* otherwise we might end up with an inconsistent dump that can't restore.
*/
LOCKMODE
-AlterTableGetLockLevel(List *cmds)
+
+AlterTableGetLockLevel(Oid relid, List *cmds)
{
/*
* This only works if we read catalog tables using MVCC snapshots.
*/
ListCell *lcmd;
LOCKMODE lockmode = ShareUpdateExclusiveLock;
+ Relation rel;
foreach(lcmd, cmds)
{
@@ -3377,7 +3389,10 @@ AlterTableGetLockLevel(List *cmds)
* getTables() */
case AT_ResetRelOptions: /* Uses MVCC in getIndexes() and
* getTables() */
- cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
+ rel = relation_open(relid, NoLock);
+ cmd_lockmode = AlterTableGetRelOptionsLockLevel(rel,
+ castNode(List, cmd->def));
+ relation_close(rel,NoLock);
break;
case AT_AttachPartition:
@@ -4155,7 +4170,7 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode)
errmsg("cannot rewrite system relation \"%s\"",
RelationGetRelationName(OldHeap))));
- if (RelationIsUsedAsCatalogTable(OldHeap))
+ if (HeapIsUsedAsCatalogTable(OldHeap))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot rewrite table \"%s\" used as a catalog table",
@@ -6250,11 +6265,11 @@ ATExecSetOptions(Relation rel, const char *colName, Node *options,
/* Generate new proposed attoptions (text array) */
datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
&isnull);
- newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
- castNode(List, options), NULL, NULL,
- false, isReset);
- /* Validate new options */
- (void) attribute_reloptions(newOptions, true);
+
+ newOptions = transformOptions(get_attribute_options_catalog(),
+ isnull ? (Datum) 0 : datum,
+ castNode(List, options), OPTIONS_PARSE_MODE_FOR_ALTER |
+ (isReset ? OPTIONS_PARSE_MODE_FOR_RESET : 0));
/* Build new tuple. */
memset(repl_null, false, sizeof(repl_null));
@@ -10256,7 +10271,7 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
Datum repl_val[Natts_pg_class];
bool repl_null[Natts_pg_class];
bool repl_repl[Natts_pg_class];
- static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
+ options_parse_mode parse_mode;
if (defList == NIL && operation != AT_ReplaceRelOptions)
return; /* nothing to do */
@@ -10285,25 +10300,52 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
&isnull);
}
- /* Generate new proposed reloptions (text array) */
- newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
- defList, NULL, validnsps, false,
- operation == AT_ResetRelOptions);
-
/* Validate */
+ parse_mode = OPTIONS_PARSE_MODE_FOR_ALTER;
+ if (operation == AT_ResetRelOptions)
+ parse_mode |= OPTIONS_PARSE_MODE_FOR_RESET;
+
switch (rel->rd_rel->relkind)
{
case RELKIND_RELATION:
case RELKIND_TOASTVALUE:
case RELKIND_MATVIEW:
case RELKIND_PARTITIONED_TABLE:
- (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
+ {
+ char *namespaces[] = HEAP_RELOPT_NAMESPACES;
+ List *heapDefList;
+
+ optionsDefListValdateNamespaces(defList, namespaces);
+ heapDefList = optionsDefListFilterNamespaces(
+ defList, NULL);
+ newOptions = transformOptions(get_heap_relopt_catalog(),
+ isnull ? (Datum) 0 : datum,
+ heapDefList, parse_mode);
+ }
break;
case RELKIND_VIEW:
- (void) view_reloptions(newOptions, true);
+ {
+ newOptions = transformOptions(
+ get_view_relopt_catalog(),
+ isnull ? (Datum) 0 : datum,
+ defList, parse_mode);
+ }
break;
case RELKIND_INDEX:
- (void) index_reloptions(rel->rd_amroutine->amoptions, newOptions, true);
+ if (rel->rd_amroutine->amrelopt_catalog)
+ {
+ newOptions = transformOptions(
+ rel->rd_amroutine->amrelopt_catalog(),
+ isnull ? (Datum) 0 : datum,
+ defList, parse_mode);
+ }
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("index %s does not support options",
+ RelationGetRelationName(rel))));
+ }
break;
default:
ereport(ERROR,
@@ -10317,7 +10359,7 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
if (rel->rd_rel->relkind == RELKIND_VIEW)
{
Query *view_query = get_view_query(rel);
- List *view_options = untransformRelOptions(newOptions);
+ List *view_options = optionsTextArrayToDefList(newOptions);
ListCell *cell;
bool check_option = false;
@@ -10377,6 +10419,8 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
{
Relation toastrel;
Oid toastid = rel->rd_rel->reltoastrelid;
+ List *toastDefList;
+ options_parse_mode parse_mode;
toastrel = heap_open(toastid, lockmode);
@@ -10400,12 +10444,15 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
&isnull);
}
+ parse_mode = OPTIONS_PARSE_MODE_FOR_ALTER;
+ if (operation == AT_ResetRelOptions)
+ parse_mode |= OPTIONS_PARSE_MODE_FOR_RESET;
- newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
- defList, "toast", validnsps, false,
- operation == AT_ResetRelOptions);
+ toastDefList = optionsDefListFilterNamespaces(defList, "toast");
- (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
+ newOptions = transformOptions(get_toast_relopt_catalog(),
+ isnull ? (Datum) 0 : datum,
+ toastDefList, parse_mode);
memset(repl_val, 0, sizeof(repl_val));
memset(repl_null, false, sizeof(repl_null));
@@ -10433,6 +10480,18 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
heap_close(toastrel, NoLock);
}
+ else
+ {
+ List *toastDefList;
+
+ toastDefList = optionsDefListFilterNamespaces(defList, "toast");
+ if (toastDefList)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("TOAST relation for this table does not exist. Can't change toast.* storage parameters")));
+ }
+ }
heap_close(pgclass, RowExclusiveLock);
}
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
index f9c2620..5a60122 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -332,10 +332,11 @@ CreateTableSpace(CreateTableSpaceStmt *stmt)
nulls[Anum_pg_tablespace_spcacl - 1] = true;
/* Generate new proposed spcoptions (text array) */
- newOptions = transformRelOptions((Datum) 0,
- stmt->options,
- NULL, NULL, false, false);
- (void) tablespace_reloptions(newOptions, true);
+
+ newOptions = transformOptions(
+ get_tablespace_options_catalog(),
+ (Datum) 0, stmt->options, 0);
+
if (newOptions != (Datum) 0)
values[Anum_pg_tablespace_spcoptions - 1] = newOptions;
else
@@ -1023,10 +1024,13 @@ AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt)
/* Generate new proposed spcoptions (text array) */
datum = heap_getattr(tup, Anum_pg_tablespace_spcoptions,
RelationGetDescr(rel), &isnull);
- newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
- stmt->options, NULL, NULL, false,
- stmt->isReset);
- (void) tablespace_reloptions(newOptions, true);
+
+
+ newOptions = transformOptions(get_tablespace_options_catalog(),
+ isnull ? (Datum) 0 : datum,
+ stmt->options,
+ OPTIONS_PARSE_MODE_FOR_ALTER |
+ (stmt->isReset ? OPTIONS_PARSE_MODE_FOR_RESET : 0));
/* Build new tuple. */
memset(repl_null, false, sizeof(repl_null));
diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.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 9207c8d..ac176c0 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -140,7 +140,7 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
&rel->pages, &rel->tuples, &rel->allvisfrac);
/* Retrieve the parallel_workers reloption, or -1 if not set. */
- rel->rel_parallel_workers = RelationGetParallelWorkers(relation, -1);
+ rel->rel_parallel_workers = HeapGetParallelWorkers(relation, -1);
/*
* Make list of indexes. Ignore indexes on system catalogs if told to.
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index e268a12..ac7d6be 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -3124,7 +3124,7 @@ transformOnConflictArbiter(ParseState *pstate,
exprLocation((Node *) onConflictClause))));
/* Same applies to table used by logical decoding as catalog table */
- if (RelationIsUsedAsCatalogTable(pstate->p_target_relation))
+ if (HeapIsUsedAsCatalogTable(pstate->p_target_relation))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("ON CONFLICT is not supported on table \"%s\" used as a catalog table",
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index e546194..27cfa7e 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1509,7 +1509,7 @@ generateClonedIndexStmt(CreateStmtContext *cxt, Relation source_idx,
datum = SysCacheGetAttr(RELOID, ht_idxrel,
Anum_pg_class_reloptions, &isnull);
if (!isnull)
- index->options = untransformRelOptions(datum);
+ index->options = optionsTextArrayToDefList(datum);
/* If it's a partial index, decompile and append the predicate */
datum = SysCacheGetAttr(INDEXRELID, ht_idx,
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 89dd3b3..a92d6f0 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -2735,6 +2735,7 @@ extract_autovac_opts(HeapTuple tup, TupleDesc pg_class_desc)
{
bytea *relopts;
AutoVacOpts *av;
+ AutoVacOpts *src;
Assert(((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_RELATION ||
((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_MATVIEW ||
@@ -2744,8 +2745,13 @@ extract_autovac_opts(HeapTuple tup, TupleDesc pg_class_desc)
if (relopts == NULL)
return NULL;
+ if (((Form_pg_class) GETSTRUCT(tup))->relkind == RELKIND_TOASTVALUE)
+ src = &(((ToastOptions *) relopts)->autovacuum);
+ else
+ src = &(((HeapOptions *) relopts)->autovacuum);
+
av = palloc(sizeof(AutoVacOpts));
- memcpy(av, &(((StdRdOptions *) relopts)->autovacuum), sizeof(AutoVacOpts));
+ memcpy(av, src, sizeof(AutoVacOpts));
pfree(relopts);
return av;
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 4dcb713..f26cc54 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -1531,7 +1531,7 @@ ApplyRetrieveRule(Query *parsetree,
rte->rtekind = RTE_SUBQUERY;
rte->relid = InvalidOid;
- rte->security_barrier = RelationIsSecurityView(relation);
+ rte->security_barrier = ViewIsSecurityView(relation);
rte->subquery = rule_action;
rte->inh = false; /* must not be set for a subquery */
@@ -3032,7 +3032,7 @@ rewriteTargetView(Query *parsetree, Relation view)
ChangeVarNodes(viewqual, base_rt_index, new_rt_index, 0);
- if (RelationIsSecurityView(view))
+ if (ViewIsSecurityView(view))
{
/*
* The view's quals go in front of existing barrier quals: those
@@ -3068,8 +3068,9 @@ rewriteTargetView(Query *parsetree, Relation view)
*/
if (parsetree->commandType != CMD_DELETE)
{
- bool has_wco = RelationHasCheckOption(view);
- bool cascaded = RelationHasCascadedCheckOption(view);
+
+ bool has_wco = ViewHasCheckOption(view);
+ bool cascaded = ViewHasCascadedCheckOption(view);
/*
* If the parent view has a cascaded check option, treat this view as
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 24e5c42..8639803 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1003,6 +1003,8 @@ ProcessUtilitySlow(ParseState *pstate,
{
Datum toast_options;
static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
+ List *toastDefList;
+ bool success;
/* Create the table itself */
address = DefineRelation((CreateStmt *) stmt,
@@ -1023,18 +1025,24 @@ ProcessUtilitySlow(ParseState *pstate,
* parse and validate reloptions for the toast
* table
*/
- toast_options = transformRelOptions((Datum) 0,
+
+ optionsDefListValdateNamespaces(
((CreateStmt *) stmt)->options,
- "toast",
- validnsps,
- true,
- false);
- (void) heap_reloptions(RELKIND_TOASTVALUE,
- toast_options,
- true);
+ validnsps);
+
+ toastDefList = optionsDefListFilterNamespaces(
+ ((CreateStmt *) stmt)->options, "toast");
- NewRelationCreateToastTable(address.objectId,
- toast_options);
+ toast_options = transformOptions(
+ get_toast_relopt_catalog(), (Datum) 0,
+ toastDefList, 0);
+
+ success = NewRelationCreateToastTable(
+ address.objectId, toast_options);
+ if (!success && toast_options)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("no TOAST relation was created for a table. Can't set toast.* storage parameters")));
}
else if (IsA(stmt, CreateForeignTableStmt))
{
@@ -1101,8 +1109,13 @@ ProcessUtilitySlow(ParseState *pstate,
* lock on (for example) a relation on which we have no
* permissions.
*/
- lockmode = AlterTableGetLockLevel(atstmt->cmds);
- relid = AlterTableLookupRelation(atstmt, lockmode);
+
+ relid = AlterTableLookupRelation(atstmt, NoLock);
+ if (OidIsValid(relid))
+ {
+ lockmode = AlterTableGetLockLevel(relid, atstmt->cmds);
+ relid = AlterTableLookupRelation(atstmt, lockmode);
+ }
if (OidIsValid(relid))
{
@@ -1610,6 +1623,7 @@ ProcessUtilitySlow(ParseState *pstate,
case T_AlterPublicationStmt:
AlterPublication((AlterPublicationStmt *) parsetree);
+
/*
* AlterPublication calls EventTriggerCollectSimpleCommand
* directly
diff --git a/src/backend/utils/cache/attoptcache.c b/src/backend/utils/cache/attoptcache.c
index f7f85b5..d20b680 100644
--- a/src/backend/utils/cache/attoptcache.c
+++ b/src/backend/utils/cache/attoptcache.c
@@ -150,7 +150,10 @@ get_attribute_options(Oid attrelid, int attnum)
opts = NULL;
else
{
- bytea *bytea_opts = attribute_reloptions(datum, false);
+ bytea *bytea_opts;
+
+ bytea_opts = optionsTextArrayToBytea(
+ get_attribute_options_catalog(), datum);
opts = MemoryContextAlloc(CacheMemoryContext,
VARSIZE(bytea_opts));
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 85c6b61..d5d8353 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -460,7 +460,8 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
options = extractRelOptions(tuple,
GetPgClassDescriptor(),
relation->rd_rel->relkind == RELKIND_INDEX ?
- relation->rd_amroutine->amoptions : NULL);
+ relation->rd_amroutine->amrelopt_catalog : NULL
+ );
/*
* Copy parsed data into CacheMemoryContext. To guard against the
diff --git a/src/backend/utils/cache/spccache.c b/src/backend/utils/cache/spccache.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 f919cf8..9f3db93 100644
--- a/src/include/access/amapi.h
+++ b/src/include/access/amapi.h
@@ -98,10 +98,6 @@ typedef void (*amcostestimate_function) (struct PlannerInfo *root,
double *indexCorrelation,
double *indexPages);
-/* parse index reloptions */
-typedef bytea *(*amoptions_function) (Datum reloptions,
- bool validate);
-
/* report AM, index, or index column property */
typedef bool (*amproperty_function) (Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
@@ -139,6 +135,9 @@ typedef void (*ammarkpos_function) (IndexScanDesc scan);
/* restore marked scan position */
typedef void (*amrestrpos_function) (IndexScanDesc scan);
+/* get catalog of reloptions definitions */
+typedef void *(*amrelopt_catalog_function) ();
+
/*
* Callback function signatures - for parallel index scans.
*/
@@ -202,7 +201,6 @@ typedef struct IndexAmRoutine
amvacuumcleanup_function amvacuumcleanup;
amcanreturn_function amcanreturn; /* can be NULL */
amcostestimate_function amcostestimate;
- amoptions_function amoptions;
amproperty_function amproperty; /* can be NULL */
amvalidate_function amvalidate;
ambeginscan_function ambeginscan;
@@ -212,6 +210,7 @@ typedef struct IndexAmRoutine
amendscan_function amendscan;
ammarkpos_function ammarkpos; /* can be NULL */
amrestrpos_function amrestrpos; /* can be NULL */
+ amrelopt_catalog_function amrelopt_catalog; /* can be NULL */
/* interface functions to support parallel index scans */
amestimateparallelscan_function amestimateparallelscan; /* can be NULL */
diff --git a/src/include/access/brin.h b/src/include/access/brin.h
index e03aa08..0f3d4e4 100644
--- a/src/include/access/brin.h
+++ b/src/include/access/brin.h
@@ -37,6 +37,9 @@ typedef struct BrinStatsData
#define BRIN_DEFAULT_PAGES_PER_RANGE 128
+#define BRIN_MIN_PAGES_PER_RANGE 1
+#define BRIN_MAX_PAGES_PER_RANGE 131072
+
#define BrinGetPagesPerRange(relation) \
((relation)->rd_options ? \
((BrinOptions *) (relation)->rd_options)->pagesPerRange : \
diff --git a/src/include/access/brin_internal.h b/src/include/access/brin_internal.h
index abe8877..4c32dd1 100644
--- a/src/include/access/brin_internal.h
+++ b/src/include/access/brin_internal.h
@@ -102,7 +102,6 @@ extern IndexBulkDeleteResult *brinbulkdelete(IndexVacuumInfo *info,
void *callback_state);
extern IndexBulkDeleteResult *brinvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
-extern bytea *brinoptions(Datum reloptions, bool validate);
/* brin_validate.c */
extern bool brinvalidate(Oid opclassoid);
diff --git a/src/include/access/gin_private.h b/src/include/access/gin_private.h
index 986fe6e..6b869f2 100644
--- a/src/include/access/gin_private.h
+++ b/src/include/access/gin_private.h
@@ -84,7 +84,7 @@ typedef struct GinState
/* ginutil.c */
-extern bytea *ginoptions(Datum reloptions, bool validate);
+extern Datum ginhandler(PG_FUNCTION_ARGS);
extern void initGinState(GinState *state, Relation index);
extern Buffer GinNewBuffer(Relation index);
extern void GinInitBuffer(Buffer b, uint32 f);
@@ -103,6 +103,7 @@ extern Datum *ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
extern OffsetNumber gintuple_get_attrnum(GinState *ginstate, IndexTuple tuple);
extern Datum gintuple_get_key(GinState *ginstate, IndexTuple tuple,
GinNullCategory *category);
+extern void *gingetreloptcatalog(void);
/* gininsert.c */
extern IndexBuildResult *ginbuild(Relation heap, Relation index,
diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h
index 1ad4ed6..6a7c2cb 100644
--- a/src/include/access/gist_private.h
+++ b/src/include/access/gist_private.h
@@ -367,6 +367,24 @@ typedef struct GISTBuildBuffers
int rootlevel;
} GISTBuildBuffers;
+
+/*
+ * Definition of items of enum type. Names and codes. To add or modify item
+ * edit both lists
+ */
+#define GIST_OPTION_BUFFERING_VALUE_NAMES { \
+ "on", \
+ "off", \
+ "auto", \
+ (const char *) NULL \
+}
+typedef enum gist_option_buffering_value_numbers
+{
+ GIST_OPTION_BUFFERING_ON = 0,
+ GIST_OPTION_BUFFERING_OFF = 1,
+ GIST_OPTION_BUFFERING_AUTO = 2,
+} gist_option_buffering_value_numbers;
+
/*
* Storage type for GiST's reloptions
*/
@@ -374,7 +392,7 @@ typedef struct GiSTOptions
{
int32 vl_len_; /* varlena header (do not touch directly!) */
int fillfactor; /* page fill factor in percent (0..100) */
- int bufferingModeOffset; /* use buffering build? */
+ int buffering_mode; /* use buffering build? */
} GiSTOptions;
/* gist.c */
@@ -435,7 +453,6 @@ extern bool gistvalidate(Oid opclassoid);
#define GIST_MIN_FILLFACTOR 10
#define GIST_DEFAULT_FILLFACTOR 90
-extern bytea *gistoptions(Datum reloptions, bool validate);
extern bool gistproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
@@ -485,6 +502,7 @@ extern void gistMakeUnionKey(GISTSTATE *giststate, int attno,
Datum *dst, bool *dstisnull);
extern XLogRecPtr gistGetFakeLSN(Relation rel);
+extern void *gistgetreloptcatalog(void);
/* gistvacuum.c */
extern IndexBulkDeleteResult *gistbulkdelete(IndexVacuumInfo *info,
@@ -503,7 +521,6 @@ extern void gistSplitByKey(Relation r, Page page, IndexTuple *itup,
/* gistbuild.c */
extern IndexBuildResult *gistbuild(Relation heap, Relation index,
struct IndexInfo *indexInfo);
-extern void gistValidateBufferingOption(char *value);
/* gistbuildbuffers.c */
extern GISTBuildBuffers *gistInitBuildBuffers(int pagesPerBuffer, int levelStep,
diff --git a/src/include/access/hash.h b/src/include/access/hash.h
index adba224..44e2ac4 100644
--- a/src/include/access/hash.h
+++ b/src/include/access/hash.h
@@ -224,6 +224,19 @@ typedef struct HashMetaPageData
typedef HashMetaPageData *HashMetaPage;
+
+typedef struct HashRelOptions
+{
+ int32 varlena_header_; /* varlena header (do not touch directly!) */
+ int fillfactor; /* page fill factor in percent (0..100) */
+} HashRelOptions;
+
+
+#define HashGetFillFactor(relation) \
+ ((relation)->rd_options ? \
+ ((HashRelOptions *) (relation)->rd_options)->fillfactor : \
+ HASH_DEFAULT_FILLFACTOR)
+
/*
* Maximum size of a hash index item (it's okay to have only one per page)
*/
@@ -319,7 +332,6 @@ extern IndexBulkDeleteResult *hashbulkdelete(IndexVacuumInfo *info,
void *callback_state);
extern IndexBulkDeleteResult *hashvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
-extern bytea *hashoptions(Datum reloptions, bool validate);
extern bool hashvalidate(Oid opclassoid);
extern Datum hash_any(register const unsigned char *k, register int keylen);
@@ -422,4 +434,6 @@ extern void hashbucketcleanup(Relation rel, Bucket cur_bucket,
bool bucket_has_garbage,
IndexBulkDeleteCallback callback, void *callback_state);
+extern void *hashgetreloptcatalog(void);
+
#endif /* HASH_H */
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 15771ce..28d9314 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -427,6 +427,17 @@ typedef BTScanOpaqueData *BTScanOpaque;
#define SK_BT_DESC (INDOPTION_DESC << SK_BT_INDOPTION_SHIFT)
#define SK_BT_NULLS_FIRST (INDOPTION_NULLS_FIRST << SK_BT_INDOPTION_SHIFT)
+typedef struct BTRelOptions
+{
+ int32 varlena_header_; /* varlena header (do not touch directly!) */
+ int fillfactor; /* page fill factor in percent (0..100) */
+} BTRelOptions;
+
+#define BTGetFillFactor(relation) \
+ ((relation)->rd_options ? \
+ ((BTRelOptions *) (relation)->rd_options)->fillfactor : \
+ BTREE_DEFAULT_FILLFACTOR)
+
/*
* external entry points for btree, in nbtree.c
*/
@@ -534,7 +545,6 @@ extern void _bt_end_vacuum(Relation rel);
extern void _bt_end_vacuum_callback(int code, Datum arg);
extern Size BTreeShmemSize(void);
extern void BTreeShmemInit(void);
-extern bytea *btoptions(Datum reloptions, bool validate);
extern bool btproperty(Oid index_oid, int attno,
IndexAMProperty prop, const char *propname,
bool *res, bool *isnull);
diff --git a/src/include/access/options.h b/src/include/access/options.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 91b2cd7..86dff99 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -1,13 +1,9 @@
/*-------------------------------------------------------------------------
*
* reloptions.h
- * Core support for relation and tablespace options (pg_class.reloptions
+ * Support for relation and tablespace options (pg_class.reloptions
* and pg_tablespace.spcoptions)
*
- * Note: the functions dealing with text-array reloptions values declare
- * them as Datum, not ArrayType *, to avoid needing to include array.h
- * into a lot of low-level code.
- *
*
* Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
@@ -20,264 +16,27 @@
#define RELOPTIONS_H
#include "access/amapi.h"
-#include "access/htup.h"
-#include "access/tupdesc.h"
-#include "nodes/pg_list.h"
-#include "storage/lock.h"
-
-/* types supported by reloptions */
-typedef enum relopt_type
-{
- RELOPT_TYPE_BOOL,
- RELOPT_TYPE_INT,
- RELOPT_TYPE_REAL,
- RELOPT_TYPE_STRING
-} relopt_type;
+#include "access/options.h"
-/* kinds supported by reloptions */
-typedef enum relopt_kind
-{
- RELOPT_KIND_HEAP = (1 << 0),
- RELOPT_KIND_TOAST = (1 << 1),
- RELOPT_KIND_BTREE = (1 << 2),
- RELOPT_KIND_HASH = (1 << 3),
- RELOPT_KIND_GIN = (1 << 4),
- RELOPT_KIND_GIST = (1 << 5),
- RELOPT_KIND_ATTRIBUTE = (1 << 6),
- RELOPT_KIND_TABLESPACE = (1 << 7),
- RELOPT_KIND_SPGIST = (1 << 8),
- RELOPT_KIND_VIEW = (1 << 9),
- RELOPT_KIND_BRIN = (1 << 10),
- RELOPT_KIND_PARTITIONED = (1 << 11),
- /* if you add a new kind, make sure you update "last_default" too */
- RELOPT_KIND_LAST_DEFAULT = RELOPT_KIND_PARTITIONED,
- /* some compilers treat enums as signed ints, so we can't use 1 << 31 */
- RELOPT_KIND_MAX = (1 << 30)
-} relopt_kind;
/* reloption namespaces allowed for heaps -- currently only TOAST */
#define HEAP_RELOPT_NAMESPACES { "toast", NULL }
-/* generic struct to hold shared data */
-typedef struct relopt_gen
-{
- const char *name; /* must be first (used as list termination
- * marker) */
- const char *desc;
- bits32 kinds;
- LOCKMODE lockmode;
- int namelen;
- relopt_type type;
-} relopt_gen;
-
-/* holds a parsed value */
-typedef struct relopt_value
-{
- relopt_gen *gen;
- bool isset;
- union
- {
- bool bool_val;
- int int_val;
- double real_val;
- char *string_val; /* allocated separately */
- } values;
-} relopt_value;
-
-/* reloptions records for specific variable types */
-typedef struct relopt_bool
-{
- relopt_gen gen;
- bool default_val;
-} relopt_bool;
-
-typedef struct relopt_int
-{
- relopt_gen gen;
- int default_val;
- int min;
- int max;
-} relopt_int;
-
-typedef struct relopt_real
-{
- relopt_gen gen;
- double default_val;
- double min;
- double max;
-} relopt_real;
-
-/* validation routines for strings */
-typedef void (*validate_string_relopt) (char *value);
-
-typedef struct relopt_string
-{
- relopt_gen gen;
- int default_len;
- bool default_isnull;
- validate_string_relopt validate_cb;
- char *default_val;
-} relopt_string;
-
-/* This is the table datatype for fillRelOptions */
-typedef struct
-{
- const char *optname; /* option's name */
- relopt_type opttype; /* option's datatype */
- int offset; /* offset of field in result struct */
-} relopt_parse_elt;
-
-
-/*
- * These macros exist for the convenience of amoptions writers (but consider
- * using fillRelOptions, which is a lot simpler). Beware of multiple
- * evaluation of arguments!
- *
- * The last argument in the HANDLE_*_RELOPTION macros allows the caller to
- * determine whether the option was set (true), or its value acquired from
- * defaults (false); it can be passed as (char *) NULL if the caller does not
- * need this information.
- *
- * optname is the option name (a string), var is the variable
- * on which the value should be stored (e.g. StdRdOptions->fillfactor), and
- * option is a relopt_value pointer.
- *
- * The normal way to use this is to loop on the relopt_value array returned by
- * parseRelOptions:
- * for (i = 0; options[i].gen->name; i++)
- * {
- * if (HAVE_RELOPTION("fillfactor", options[i])
- * {
- * HANDLE_INT_RELOPTION("fillfactor", rdopts->fillfactor, options[i], &isset);
- * continue;
- * }
- * if (HAVE_RELOPTION("default_row_acl", options[i])
- * {
- * ...
- * }
- * ...
- * if (validate)
- * ereport(ERROR,
- * (errmsg("unknown option")));
- * }
- *
- * Note that this is more or less the same that fillRelOptions does, so only
- * use this if you need to do something non-standard within some option's
- * code block.
- */
-#define HAVE_RELOPTION(optname, option) \
- (pg_strncasecmp(option.gen->name, optname, option.gen->namelen + 1) == 0)
-
-#define HANDLE_INT_RELOPTION(optname, var, option, wasset) \
- do { \
- if (option.isset) \
- var = option.values.int_val; \
- else \
- var = ((relopt_int *) option.gen)->default_val; \
- (wasset) != NULL ? *(wasset) = option.isset : (dummyret)NULL; \
- } while (0)
-
-#define HANDLE_BOOL_RELOPTION(optname, var, option, wasset) \
- do { \
- if (option.isset) \
- var = option.values.bool_val; \
- else \
- var = ((relopt_bool *) option.gen)->default_val; \
- (wasset) != NULL ? *(wasset) = option.isset : (dummyret) NULL; \
- } while (0)
-#define HANDLE_REAL_RELOPTION(optname, var, option, wasset) \
- do { \
- if (option.isset) \
- var = option.values.real_val; \
- else \
- var = ((relopt_real *) option.gen)->default_val; \
- (wasset) != NULL ? *(wasset) = option.isset : (dummyret) NULL; \
- } while (0)
-
-/*
- * Note that this assumes that the variable is already allocated at the tail of
- * reloptions structure (StdRdOptions or equivalent).
- *
- * "base" is a pointer to the reloptions structure, and "offset" is an integer
- * variable that must be initialized to sizeof(reloptions structure). This
- * struct must have been allocated with enough space to hold any string option
- * present, including terminating \0 for every option. SET_VARSIZE() must be
- * called on the struct with this offset as the second argument, after all the
- * string options have been processed.
- */
-#define HANDLE_STRING_RELOPTION(optname, var, option, base, offset, wasset) \
- do { \
- relopt_string *optstring = (relopt_string *) option.gen;\
- char *string_val; \
- if (option.isset) \
- string_val = option.values.string_val; \
- else if (!optstring->default_isnull) \
- string_val = optstring->default_val; \
- else \
- string_val = NULL; \
- (wasset) != NULL ? *(wasset) = option.isset : (dummyret) NULL; \
- if (string_val == NULL) \
- var = 0; \
- else \
- { \
- strcpy(((char *)(base)) + (offset), string_val); \
- var = (offset); \
- (offset) += strlen(string_val) + 1; \
- } \
- } while (0)
-
-/*
- * For use during amoptions: get the strlen of a string option
- * (either default or the user defined value)
- */
-#define GET_STRING_RELOPTION_LEN(option) \
- ((option).isset ? strlen((option).values.string_val) : \
- ((relopt_string *) (option).gen)->default_len)
+extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
+ amrelopt_catalog_function catalog_fun);
/*
- * For use by code reading options already parsed: get a pointer to the string
- * value itself. "optstruct" is the StdRdOption struct or equivalent, "member"
- * is the struct member corresponding to the string option
+ * Functions that creates option catalog instances for heap, toast, view
+ * and other object types that are part of the postgres core
*/
-#define GET_STRING_RELOPTION(optstruct, member) \
- ((optstruct)->member == 0 ? NULL : \
- (char *)(optstruct) + (optstruct)->member)
+extern options_catalog *get_heap_relopt_catalog(void);
+extern options_catalog *get_toast_relopt_catalog(void);
-extern relopt_kind add_reloption_kind(void);
-extern void add_bool_reloption(bits32 kinds, char *name, char *desc,
- bool default_val);
-extern void add_int_reloption(bits32 kinds, char *name, char *desc,
- int default_val, int min_val, int max_val);
-extern void add_real_reloption(bits32 kinds, char *name, char *desc,
- double default_val, double min_val, double max_val);
-extern void add_string_reloption(bits32 kinds, char *name, char *desc,
- char *default_val, validate_string_relopt validator);
-
-extern Datum transformRelOptions(Datum oldOptions, List *defList,
- char *namspace, char *validnsps[],
- bool ignoreOids, bool isReset);
-extern List *untransformRelOptions(Datum options);
-extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
- amoptions_function amoptions);
-extern relopt_value *parseRelOptions(Datum options, bool validate,
- relopt_kind kind, int *numrelopts);
-extern void *allocateReloptStruct(Size base, relopt_value *options,
- int numoptions);
-extern void fillRelOptions(void *rdopts, Size basesize,
- relopt_value *options, int numoptions,
- bool validate,
- const relopt_parse_elt *elems, int nelems);
-
-extern bytea *default_reloptions(Datum reloptions, bool validate,
- relopt_kind kind);
-extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
-extern bytea *view_reloptions(Datum reloptions, bool validate);
-extern bytea *index_reloptions(amoptions_function amoptions, Datum reloptions,
- bool validate);
-extern bytea *attribute_reloptions(Datum reloptions, bool validate);
-extern bytea *tablespace_reloptions(Datum reloptions, bool validate);
-extern LOCKMODE AlterTableGetRelOptionsLockLevel(List *defList);
+extern options_catalog *get_view_relopt_catalog(void);
+extern options_catalog *get_attribute_options_catalog(void);
+extern options_catalog *get_tablespace_options_catalog(void);
+extern LOCKMODE AlterTableGetRelOptionsLockLevel(Relation rel, List *defList);
#endif /* RELOPTIONS_H */
diff --git a/src/include/access/spgist.h b/src/include/access/spgist.h
index 9dca8fd..9c57eba 100644
--- a/src/include/access/spgist.h
+++ b/src/include/access/spgist.h
@@ -20,10 +20,6 @@
#include "lib/stringinfo.h"
-/* reloption parameters */
-#define SPGIST_MIN_FILLFACTOR 10
-#define SPGIST_DEFAULT_FILLFACTOR 80
-
/* SPGiST opclass support function numbers */
#define SPGIST_CONFIG_PROC 1
#define SPGIST_CHOOSE_PROC 2
@@ -182,9 +178,6 @@ typedef struct spgLeafConsistentOut
} spgLeafConsistentOut;
-/* spgutils.c */
-extern bytea *spgoptions(Datum reloptions, bool validate);
-
/* spginsert.c */
extern IndexBuildResult *spgbuild(Relation heap, Relation index,
struct IndexInfo *indexInfo);
diff --git a/src/include/access/spgist_private.h b/src/include/access/spgist_private.h
index 4072c05..d99adc9 100644
--- a/src/include/access/spgist_private.h
+++ b/src/include/access/spgist_private.h
@@ -21,6 +21,19 @@
#include "utils/relcache.h"
+typedef struct SpGistRelOptions
+{
+ int32 varlena_header_; /* varlena header (do not touch directly!) */
+ int fillfactor; /* page fill factor in percent (0..100) */
+} SpGistRelOptions;
+
+
+#define SpGistGetFillFactor(relation) \
+ ((relation)->rd_options ? \
+ ((SpGistRelOptions *) (relation)->rd_options)->fillfactor : \
+ SPGIST_DEFAULT_FILLFACTOR)
+
+
/* Page numbers of fixed-location pages */
#define SPGIST_METAPAGE_BLKNO (0) /* metapage */
#define SPGIST_ROOT_BLKNO (1) /* root for normal entries */
@@ -380,6 +393,11 @@ typedef SpGistDeadTupleData *SpGistDeadTuple;
#define GBUF_REQ_NULLS(flags) ((flags) & GBUF_NULLS)
/* spgutils.c */
+
+/* reloption parameters */
+#define SPGIST_MIN_FILLFACTOR 10
+#define SPGIST_DEFAULT_FILLFACTOR 80
+
extern SpGistCache *spgGetCache(Relation index);
extern void initSpGistState(SpGistState *state, Relation index);
extern Buffer SpGistNewBuffer(Relation index);
@@ -407,6 +425,7 @@ extern OffsetNumber SpGistPageAddNewItem(SpGistState *state, Page page,
Item item, Size size,
OffsetNumber *startOffset,
bool errorOK);
+extern void *spggetreloptcatalog(void);
/* spgdoinsert.c */
extern void spgUpdateNodeLink(SpGistInnerTuple tup, int nodeN,
diff --git a/src/include/catalog/toasting.h b/src/include/catalog/toasting.h
index 00d0a83..18cff15 100644
--- a/src/include/catalog/toasting.h
+++ b/src/include/catalog/toasting.h
@@ -19,7 +19,7 @@
/*
* toasting.c prototypes
*/
-extern void NewRelationCreateToastTable(Oid relOid, Datum reloptions);
+extern bool NewRelationCreateToastTable(Oid relOid, Datum reloptions);
extern void NewHeapCreateToastTable(Oid relOid, Datum reloptions,
LOCKMODE lockmode);
extern void AlterTableCreateToastTable(Oid relOid, Datum reloptions,
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index f3a9701..33f5782 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -31,7 +31,7 @@ extern Oid AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode);
extern void AlterTable(Oid relid, LOCKMODE lockmode, AlterTableStmt *stmt);
-extern LOCKMODE AlterTableGetLockLevel(List *cmds);
+extern LOCKMODE AlterTableGetLockLevel(Oid relid, List *cmds);
extern void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index ab875bb..47f9aa4 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -273,7 +273,7 @@ typedef struct AutoVacOpts
float8 analyze_scale_factor;
} AutoVacOpts;
-typedef struct StdRdOptions
+typedef struct HeapOptions
{
int32 vl_len_; /* varlena header (do not touch directly!) */
int fillfactor; /* page fill factor in percent (0..100) */
@@ -281,107 +281,116 @@ typedef struct StdRdOptions
bool user_catalog_table; /* use as an additional catalog
* relation */
int parallel_workers; /* max number of parallel workers */
-} StdRdOptions;
+} HeapOptions;
+
+typedef struct ToastOptions
+{
+ int32 vl_len_; /* varlena header (do not touch directly!) */
+ AutoVacOpts autovacuum; /* autovacuum-related options */
+} ToastOptions;
#define HEAP_MIN_FILLFACTOR 10
#define HEAP_DEFAULT_FILLFACTOR 100
+#define TOAST_DEFAULT_FILLFACTOR 100 /* Only default is actually used */
+
/*
- * RelationGetFillFactor
+ * HeapGetFillFactor
* Returns the relation's fillfactor. Note multiple eval of argument!
*/
-#define RelationGetFillFactor(relation, defaultff) \
+#define HeapGetFillFactor(relation, defaultff) \
((relation)->rd_options ? \
- ((StdRdOptions *) (relation)->rd_options)->fillfactor : (defaultff))
+ ((HeapOptions *) (relation)->rd_options)->fillfactor : (defaultff))
/*
- * RelationGetTargetPageUsage
- * Returns the relation's desired space usage per page in bytes.
- */
-#define RelationGetTargetPageUsage(relation, defaultff) \
- (BLCKSZ * RelationGetFillFactor(relation, defaultff) / 100)
-
-/*
- * RelationGetTargetPageFreeSpace
+ * HeapGetTargetPageFreeSpace
* Returns the relation's desired freespace per page in bytes.
*/
-#define RelationGetTargetPageFreeSpace(relation, defaultff) \
- (BLCKSZ * (100 - RelationGetFillFactor(relation, defaultff)) / 100)
+#define HeapGetTargetPageFreeSpace(relation, defaultff) \
+ (BLCKSZ * (100 - HeapGetFillFactor(relation, defaultff)) / 100)
/*
- * RelationIsUsedAsCatalogTable
+ * HeapIsUsedAsCatalogTable
* Returns whether the relation should be treated as a catalog table
* from the pov of logical decoding. Note multiple eval of argument!
*/
-#define RelationIsUsedAsCatalogTable(relation) \
+#define HeapIsUsedAsCatalogTable(relation) \
((relation)->rd_options && \
((relation)->rd_rel->relkind == RELKIND_RELATION || \
(relation)->rd_rel->relkind == RELKIND_MATVIEW) ? \
- ((StdRdOptions *) (relation)->rd_options)->user_catalog_table : false)
+ ((HeapOptions *) (relation)->rd_options)->user_catalog_table : false)
/*
- * RelationGetParallelWorkers
+ * HeapGetParallelWorkers
* Returns the relation's parallel_workers reloption setting.
* Note multiple eval of argument!
*/
-#define RelationGetParallelWorkers(relation, defaultpw) \
+#define HeapGetParallelWorkers(relation, defaultpw) \
((relation)->rd_options ? \
- ((StdRdOptions *) (relation)->rd_options)->parallel_workers : (defaultpw))
+ ((HeapOptions *) (relation)->rd_options)->parallel_workers : (defaultpw))
+#define ToastGetTargetPageFreeSpace() \
+ (BLCKSZ * (100 - TOAST_DEFAULT_FILLFACTOR) / 100)
/*
* ViewOptions
* Contents of rd_options for views
*/
+
+/*
+ * Definition of items of enum type. Names and codes. To add or modify item
+ * edit both lists
+ */
+#define VIEW_OPTION_CHECK_OPTION_VALUE_NAMES { \
+ "local", \
+ "cascaded", \
+ (const char *) NULL \
+}
+
+typedef enum view_option_check_option_value_numbers
+{
+ VIEW_OPTION_CHECK_OPTION_NOT_SET = -1,
+ VIEW_OPTION_CHECK_OPTION_LOCAL = 0,
+ VIEW_OPTION_CHECK_OPTION_CASCADED = 1,
+} view_option_check_option_value_numbers;
+
typedef struct ViewOptions
{
int32 vl_len_; /* varlena header (do not touch directly!) */
bool security_barrier;
- int check_option_offset;
+ int check_option;
} ViewOptions;
/*
- * RelationIsSecurityView
- * Returns whether the relation is security view, or not. Note multiple
- * eval of argument!
+ * ViewIsSecurityView
+ * Returns whether the view is security view, or not. Note multiple eval
+ * of argument!
*/
-#define RelationIsSecurityView(relation) \
+#define ViewIsSecurityView(relation) \
((relation)->rd_options ? \
- ((ViewOptions *) (relation)->rd_options)->security_barrier : false)
+ ((ViewOptions *) (relation)->rd_options)->security_barrier : false)
/*
- * RelationHasCheckOption
- * Returns true if the relation is a view defined with either the local
- * or the cascaded check option. Note multiple eval of argument!
+ * ViewHasCheckOption
+ * Returns true if view is defined with either the local or the cascaded
+ * check option. Note multiple eval of argument!
*/
-#define RelationHasCheckOption(relation) \
- ((relation)->rd_options && \
- ((ViewOptions *) (relation)->rd_options)->check_option_offset != 0)
-/*
- * RelationHasLocalCheckOption
- * Returns true if the relation is a view defined with the local check
- * option. Note multiple eval of argument!
- */
-#define RelationHasLocalCheckOption(relation) \
- ((relation)->rd_options && \
- ((ViewOptions *) (relation)->rd_options)->check_option_offset != 0 ? \
- strcmp((char *) (relation)->rd_options + \
- ((ViewOptions *) (relation)->rd_options)->check_option_offset, \
- "local") == 0 : false)
+#define ViewHasCheckOption(relation) \
+((relation)->rd_options && \
+ ((ViewOptions *) (relation)->rd_options)->check_option != \
+ VIEW_OPTION_CHECK_OPTION_NOT_SET)
/*
- * RelationHasCascadedCheckOption
- * Returns true if the relation is a view defined with the cascaded check
- * option. Note multiple eval of argument!
+ * ViewHasCascadedCheckOption
+ * Returns true if the view is defined with the cascaded check option.
+ * Note multiple eval of argument!
*/
-#define RelationHasCascadedCheckOption(relation) \
- ((relation)->rd_options && \
- ((ViewOptions *) (relation)->rd_options)->check_option_offset != 0 ? \
- strcmp((char *) (relation)->rd_options + \
- ((ViewOptions *) (relation)->rd_options)->check_option_offset, \
- "cascaded") == 0 : false)
+#define ViewHasCascadedCheckOption(relation) \
+((relation)->rd_options && \
+ ((ViewOptions *) (relation)->rd_options)->check_option == \
+ VIEW_OPTION_CHECK_OPTION_CASCADED)
/*
* RelationIsValid
@@ -560,7 +569,7 @@ typedef struct ViewOptions
#define RelationIsAccessibleInLogicalDecoding(relation) \
(XLogLogicalInfoActive() && \
RelationNeedsWAL(relation) && \
- (IsCatalogRelation(relation) || RelationIsUsedAsCatalogTable(relation)))
+ (IsCatalogRelation(relation) || HeapIsUsedAsCatalogTable(relation)))
/*
* RelationIsLogicallyLogged
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 883a5c9..072783e 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -2285,7 +2285,9 @@ ERROR: unrecognized parameter "autovacuum_enabled"
alter view my_locks set (autovacuum_enabled = false);
ERROR: unrecognized parameter "autovacuum_enabled"
alter table my_locks reset (autovacuum_enabled);
+ERROR: unrecognized parameter "autovacuum_enabled"
alter view my_locks reset (autovacuum_enabled);
+ERROR: unrecognized parameter "autovacuum_enabled"
begin;
alter view my_locks set (security_barrier=off);
select * from my_locks order by 1;
@@ -3378,3 +3380,10 @@ create table parted_validate_test_1 partition of parted_validate_test for values
alter table parted_validate_test add constraint parted_validate_test_chka check (a > 0) not valid;
alter table parted_validate_test validate constraint parted_validate_test_chka;
drop table parted_validate_test;
+-- test alter column options
+CREATE TABLE tmp(i integer);
+INSERT INTO tmp VALUES (1);
+ALTER TABLE tmp ALTER COLUMN i SET (n_distinct = 1, n_distinct_inherited = 2);
+ALTER TABLE tmp ALTER COLUMN i RESET (n_distinct_inherited);
+ANALYZE tmp;
+DROP TABLE tmp;
diff --git a/src/test/regress/expected/brin.out b/src/test/regress/expected/brin.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/create_index.out b/src/test/regress/expected/create_index.out
index 26cd059..7b8f805 100644
--- a/src/test/regress/expected/create_index.out
+++ b/src/test/regress/expected/create_index.out
@@ -2337,7 +2337,7 @@ Options: fastupdate=on, gin_pending_list_limit=128
CREATE INDEX hash_i4_index ON hash_i4_heap USING hash (random int4_ops);
CREATE INDEX hash_name_index ON hash_name_heap USING hash (random name_ops);
CREATE INDEX hash_txt_index ON hash_txt_heap USING hash (random text_ops);
-CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_ops);
+CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=60);
CREATE UNLOGGED TABLE unlogged_hash_table (id int4);
CREATE INDEX unlogged_hash_index ON unlogged_hash_table USING hash (id int4_ops);
DROP TABLE unlogged_hash_table;
diff --git a/src/test/regress/expected/gist.out b/src/test/regress/expected/gist.out
index c7181b0..19f1881 100644
--- a/src/test/regress/expected/gist.out
+++ b/src/test/regress/expected/gist.out
@@ -12,6 +12,20 @@ insert into gist_point_tbl (id, p)
select g+100000, point(g*10+1, g*10+1) from generate_series(1, 10000) g;
-- To test vacuum, delete some entries from all over the index.
delete from gist_point_tbl where id % 2 = 1;
+-- Test buffering and fillfactor reloption takes valid values
+create index gist_pointidx2 on gist_point_tbl using gist(p) with (buffering = on, fillfactor=50);
+create index gist_pointidx3 on gist_point_tbl using gist(p) with (buffering = off);
+create index gist_pointidx4 on gist_point_tbl using gist(p) with (buffering = auto);
+--Test buffering and fillfactor for refising invalid values
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (buffering = invalid_value);
+ERROR: invalid value for "buffering" option
+DETAIL: Valid values are "on", "off", "auto".
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfactor=9);
+ERROR: value 9 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfactor=101);
+ERROR: value 101 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
-- And also delete some concentration of values. (GiST doesn't currently
-- attempt to delete pages even when they become empty, but if it did, this
-- would exercise it)
diff --git a/src/test/regress/expected/hash_index.out b/src/test/regress/expected/hash_index.out
index 0bbaa2a..6d5cf0d 100644
--- a/src/test/regress/expected/hash_index.out
+++ b/src/test/regress/expected/hash_index.out
@@ -229,3 +229,12 @@ CREATE TABLE hash_heap_float4 (x float4, y int);
INSERT INTO hash_heap_float4 VALUES (1.1,1);
CREATE INDEX hash_idx ON hash_heap_float4 USING hash (x);
DROP TABLE hash_heap_float4 CASCADE;
+-- Check reloptions min max values and that it is not allowed to ALTER
+CREATE INDEX hash_f8_index2 ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=9);
+ERROR: value 9 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+CREATE INDEX hash_f8_index2 ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=101);
+ERROR: value 101 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+ALTER INDEX hash_f8_index SET (fillfactor=99);
+ERROR: changing parameter "fillfactor" is not allowed
diff --git a/src/test/regress/expected/reloptions.out b/src/test/regress/expected/reloptions.out
new file mode 100644
index 0000000..835d72f
--- /dev/null
+++ b/src/test/regress/expected/reloptions.out
@@ -0,0 +1,193 @@
+-- Simple create
+CREATE TABLE reloptions_test(i INT) WITH (fillfactor=30,autovacuum_enabled = false, autovacuum_analyze_scale_factor = 0.2);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+ reloptions
+------------------------------------------------------------------------------
+ {fillfactor=30,autovacuum_enabled=false,autovacuum_analyze_scale_factor=0.2}
+(1 row)
+
+-- Test сase insensitive
+CREATE TABLE reloptions_test3(i INT) WITH (FiLlFaCtoR=40);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test3'::regclass;
+ reloptions
+-----------------
+ {fillfactor=40}
+(1 row)
+
+-- Fail on min/max values check
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=2);
+ERROR: value 2 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=110);
+ERROR: value 110 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor = -10.0);
+ERROR: value -10.0 out of bounds for option "autovacuum_analyze_scale_factor"
+DETAIL: Valid values are between "0.000000" and "100.000000".
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor = 110.0);
+ERROR: value 110.0 out of bounds for option "autovacuum_analyze_scale_factor"
+DETAIL: Valid values are between "0.000000" and "100.000000".
+-- Fail when option and namespase do not exist
+CREATE TABLE reloptions_test2(i INT) WITH (not_existing_option=2);
+ERROR: unrecognized parameter "not_existing_option"
+CREATE TABLE reloptions_test2(i INT) WITH (not_existing_namespace.fillfactor=2);
+ERROR: unrecognized parameter namespace "not_existing_namespace"
+-- Fail while setting unproper value
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=30.5);
+ERROR: invalid value for integer option "fillfactor": 30.5
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor='string');
+ERROR: invalid value for integer option "fillfactor": string
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=true);
+ERROR: invalid value for integer option "fillfactor": true
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled=12);
+ERROR: invalid value for boolean option "autovacuum_enabled": 12
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled=30.5);
+ERROR: invalid value for boolean option "autovacuum_enabled": 30.5
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled='string');
+ERROR: invalid value for boolean option "autovacuum_enabled": string
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor='string');
+ERROR: invalid value for floating point option "autovacuum_analyze_scale_factor": string
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor=true);
+ERROR: invalid value for floating point option "autovacuum_analyze_scale_factor": true
+-- Specifing name only should fail as if there was =ture after it
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor);
+ERROR: invalid value for integer option "fillfactor": true
+-- Simple ALTER TABLE
+ALTER TABLE reloptions_test SET (fillfactor=31, autovacuum_analyze_scale_factor = 0.3);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+ reloptions
+------------------------------------------------------------------------------
+ {autovacuum_enabled=false,fillfactor=31,autovacuum_analyze_scale_factor=0.3}
+(1 row)
+
+-- Check that we cat set boolean option to true just by mentioning it
+ALTER TABLE reloptions_test SET (autovacuum_enabled, fillfactor=32);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+ reloptions
+-----------------------------------------------------------------------------
+ {autovacuum_analyze_scale_factor=0.3,autovacuum_enabled=true,fillfactor=32}
+(1 row)
+
+-- Check that RESET works well
+ALTER TABLE reloptions_test RESET (fillfactor);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+ reloptions
+---------------------------------------------------------------
+ {autovacuum_analyze_scale_factor=0.3,autovacuum_enabled=true}
+(1 row)
+
+-- Check that RESETting all values make NULL reloptions record in pg_class
+ALTER TABLE reloptions_test RESET (autovacuum_enabled, autovacuum_analyze_scale_factor);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass AND reloptions IS NULL;
+ reloptions
+------------
+
+(1 row)
+
+-- Check RESET fails on att=value
+ALTER TABLE reloptions_test RESET (fillfactor=12);
+ERROR: RESET must not include values for parameters
+-- Check oids options is ignored
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test(i INT) WITH (fillfactor=20, oids=true);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+ reloptions
+-----------------
+ {fillfactor=20}
+(1 row)
+
+-- Now testing toast.* options
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test (s VARCHAR) WITH (toast.autovacuum_vacuum_cost_delay = 23 );
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+ reloptions
+-----------------------------------
+ {autovacuum_vacuum_cost_delay=23}
+(1 row)
+
+ALTER TABLE reloptions_test SET (toast.autovacuum_vacuum_cost_delay = 24);
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+ reloptions
+-----------------------------------
+ {autovacuum_vacuum_cost_delay=24}
+(1 row)
+
+ALTER TABLE reloptions_test RESET (toast.autovacuum_vacuum_cost_delay);
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+ reloptions
+------------
+
+(1 row)
+
+-- Fail on unexisting options in toast namespace
+CREATE TABLE reloptions_test2 (i int) WITH (toast.not_existing_option = 42 );
+ERROR: unrecognized parameter "toast.not_existing_option"
+-- Fail on setting reloption to a table that does not have a TOAST relation
+CREATE TABLE reloptions_test2 (i int) WITH (toast.autovacuum_vacuum_cost_delay = 23 );
+ERROR: no TOAST relation was created for a table. Can't set toast.* storage parameters
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test(i INT);
+ALTER TABLE reloptions_test SET (toast.autovacuum_vacuum_cost_delay = 23);
+ERROR: TOAST relation for this table does not exist. Can't change toast.* storage parameters
+ALTER TABLE reloptions_test RESET (toast.autovacuum_vacuum_cost_delay);
+ERROR: TOAST relation for this table does not exist. Can't change toast.* storage parameters
+-- autovacuum_analyze_scale_factor and autovacuum_analyze_threshold should be
+-- accepted by heap but rejected by toast (special case)
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test (s VARCHAR) WITH (autovacuum_analyze_scale_factor=1, autovacuum_analyze_threshold=1);
+CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analyze_scale_factor=1);
+ERROR: unrecognized parameter "toast.autovacuum_analyze_scale_factor"
+CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analyze_threshold=1);
+ERROR: unrecognized parameter "toast.autovacuum_analyze_threshold"
+-- And now mixed toast + heap
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test (s VARCHAR) WITH (toast.autovacuum_vacuum_cost_delay = 23, autovacuum_vacuum_cost_delay = 24, fillfactor = 40);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+ reloptions
+-------------------------------------------------
+ {autovacuum_vacuum_cost_delay=24,fillfactor=40}
+(1 row)
+
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+ reloptions
+-----------------------------------
+ {autovacuum_vacuum_cost_delay=23}
+(1 row)
+
+-- Same FOR CREATE and ALTER INDEX for btree indexes
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (fillfactor=30);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx'::regclass;
+ reloptions
+-----------------
+ {fillfactor=30}
+(1 row)
+
+-- Fail when option and namespase do not exist
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_existing_option=2);
+ERROR: unrecognized parameter "not_existing_option"
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_existing_option.fillfactor=2);
+ERROR: unrecognized parameter namespace "not_existing_option"
+-- Check ranges
+CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfactor=1);
+ERROR: value 1 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfactor=130);
+ERROR: value 130 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+-- Check alter
+ALTER INDEX reloptions_test_idx SET (fillfactor=40);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx'::regclass;
+ reloptions
+-----------------
+ {fillfactor=40}
+(1 row)
+
+-- Check alter on empty relop list
+CREATE INDEX reloptions_test_idx3 ON reloptions_test (s);
+ALTER INDEX reloptions_test_idx3 SET (fillfactor=40);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx3'::regclass;
+ reloptions
+-----------------
+ {fillfactor=40}
+(1 row)
+
diff --git a/src/test/regress/expected/spgist.out b/src/test/regress/expected/spgist.out
index 0691e91..bb58fb7 100644
--- a/src/test/regress/expected/spgist.out
+++ b/src/test/regress/expected/spgist.out
@@ -4,7 +4,7 @@
-- There are other tests to test different SP-GiST opclasses. This is for
-- testing SP-GiST code itself.
create table spgist_point_tbl(id int4, p point);
-create index spgist_point_idx on spgist_point_tbl using spgist(p);
+create index spgist_point_idx on spgist_point_tbl using spgist(p) with (fillfactor = 75);
-- Test vacuum-root operation. It gets invoked when the root is also a leaf,
-- i.e. the index is very small.
insert into spgist_point_tbl (id, p)
@@ -37,3 +37,12 @@ select g, 'baaaaaaaaaaaaaar' || g from generate_series(1, 1000) g;
-- tuple to be moved to another page.
insert into spgist_text_tbl (id, t)
select -g, 'f' || repeat('o', 100-g) || 'surprise' from generate_series(1, 100) g;
+-- Check reloptions min, max values
+create index spgist_point_idx2 on spgist_point_tbl using spgist(p) with (fillfactor = 9);
+ERROR: value 9 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+create index spgist_point_idx2 on spgist_point_tbl using spgist(p) with (fillfactor = 101);
+ERROR: value 101 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+-- ALTER reloption should work
+alter index spgist_point_idx set (fillfactor = 76);
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out
index aa06d1d..287dd84 100644
--- a/src/test/regress/expected/updatable_views.out
+++ b/src/test/regress/expected/updatable_views.out
@@ -1551,7 +1551,7 @@ SELECT * FROM base_tbl;
ALTER VIEW rw_view1 SET (check_option=here); -- invalid
ERROR: invalid value for "check_option" option
-DETAIL: Valid values are "local" and "cascaded".
+DETAIL: Valid values are "local", "cascaded".
ALTER VIEW rw_view1 SET (check_option=local);
INSERT INTO rw_view2 VALUES (-20); -- should fail
ERROR: new row violates check option for view "rw_view1"
diff --git a/src/test/regress/input/tablespace.source b/src/test/regress/input/tablespace.source
index 03a62bd..7f7934b 100644
--- a/src/test/regress/input/tablespace.source
+++ b/src/test/regress/input/tablespace.source
@@ -12,10 +12,10 @@ DROP TABLESPACE regress_tblspacewith;
CREATE TABLESPACE regress_tblspace LOCATION '@testtablespace@';
-- try setting and resetting some properties for the new tablespace
-ALTER TABLESPACE regress_tblspace SET (random_page_cost = 1.0);
+ALTER TABLESPACE regress_tblspace SET (random_page_cost = 1.0, seq_page_cost = 1.1);
ALTER TABLESPACE regress_tblspace SET (some_nonexistent_parameter = true); -- fail
ALTER TABLESPACE regress_tblspace RESET (random_page_cost = 2.0); -- fail
-ALTER TABLESPACE regress_tblspace RESET (random_page_cost, seq_page_cost); -- ok
+ALTER TABLESPACE regress_tblspace RESET (random_page_cost, effective_io_concurrency); -- ok
-- create a schema we can use
CREATE SCHEMA testschema;
diff --git a/src/test/regress/output/tablespace.source b/src/test/regress/output/tablespace.source
index aaedf5f..2443511 100644
--- a/src/test/regress/output/tablespace.source
+++ b/src/test/regress/output/tablespace.source
@@ -14,12 +14,12 @@ DROP TABLESPACE regress_tblspacewith;
-- create a tablespace we can use
CREATE TABLESPACE regress_tblspace LOCATION '@testtablespace@';
-- try setting and resetting some properties for the new tablespace
-ALTER TABLESPACE regress_tblspace SET (random_page_cost = 1.0);
+ALTER TABLESPACE regress_tblspace SET (random_page_cost = 1.0, seq_page_cost = 1.1);
ALTER TABLESPACE regress_tblspace SET (some_nonexistent_parameter = true); -- fail
ERROR: unrecognized parameter "some_nonexistent_parameter"
ALTER TABLESPACE regress_tblspace RESET (random_page_cost = 2.0); -- fail
ERROR: RESET must not include values for parameters
-ALTER TABLESPACE regress_tblspace RESET (random_page_cost, seq_page_cost); -- ok
+ALTER TABLESPACE regress_tblspace RESET (random_page_cost, effective_io_concurrency); -- ok
-- create a schema we can use
CREATE SCHEMA testschema;
-- try a table
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 1f8f098..e9b9b33 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -109,7 +109,7 @@ test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combo
# NB: temp.sql does a reconnect which transiently uses 2 connections,
# so keep this parallel group to at most 19 tests
# ----------
-test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml
+test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml reloptions
# ----------
# Another group of parallel tests
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 04206c3..f9c08b7 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -177,5 +177,6 @@ test: returning
test: largeobject
test: with
test: xml
+test: reloptions
test: event_trigger
test: stats
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index eb1b4b5..33a8741 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -2235,3 +2235,10 @@ create table parted_validate_test_1 partition of parted_validate_test for values
alter table parted_validate_test add constraint parted_validate_test_chka check (a > 0) not valid;
alter table parted_validate_test validate constraint parted_validate_test_chka;
drop table parted_validate_test;
+-- test alter column options
+CREATE TABLE tmp(i integer);
+INSERT INTO tmp VALUES (1);
+ALTER TABLE tmp ALTER COLUMN i SET (n_distinct = 1, n_distinct_inherited = 2);
+ALTER TABLE tmp ALTER COLUMN i RESET (n_distinct_inherited);
+ANALYZE tmp;
+DROP TABLE tmp;
diff --git a/src/test/regress/sql/brin.sql b/src/test/regress/sql/brin.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/create_index.sql b/src/test/regress/sql/create_index.sql
index 1648072..1f8d101 100644
--- a/src/test/regress/sql/create_index.sql
+++ b/src/test/regress/sql/create_index.sql
@@ -682,7 +682,7 @@ CREATE INDEX hash_name_index ON hash_name_heap USING hash (random name_ops);
CREATE INDEX hash_txt_index ON hash_txt_heap USING hash (random text_ops);
-CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_ops);
+CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=60);
CREATE UNLOGGED TABLE unlogged_hash_table (id int4);
CREATE INDEX unlogged_hash_index ON unlogged_hash_table USING hash (id int4_ops);
diff --git a/src/test/regress/sql/gist.sql b/src/test/regress/sql/gist.sql
index 9d4ff1e..ce1ac1a 100644
--- a/src/test/regress/sql/gist.sql
+++ b/src/test/regress/sql/gist.sql
@@ -17,6 +17,17 @@ select g+100000, point(g*10+1, g*10+1) from generate_series(1, 10000) g;
-- To test vacuum, delete some entries from all over the index.
delete from gist_point_tbl where id % 2 = 1;
+-- Test buffering and fillfactor reloption takes valid values
+create index gist_pointidx2 on gist_point_tbl using gist(p) with (buffering = on, fillfactor=50);
+create index gist_pointidx3 on gist_point_tbl using gist(p) with (buffering = off);
+create index gist_pointidx4 on gist_point_tbl using gist(p) with (buffering = auto);
+--Test buffering and fillfactor for refising invalid values
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (buffering = invalid_value);
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfactor=9);
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfactor=101);
+
+
+
-- And also delete some concentration of values. (GiST doesn't currently
-- attempt to delete pages even when they become empty, but if it did, this
-- would exercise it)
diff --git a/src/test/regress/sql/hash_index.sql b/src/test/regress/sql/hash_index.sql
index 9af03d2..f245525 100644
--- a/src/test/regress/sql/hash_index.sql
+++ b/src/test/regress/sql/hash_index.sql
@@ -192,3 +192,8 @@ CREATE TABLE hash_heap_float4 (x float4, y int);
INSERT INTO hash_heap_float4 VALUES (1.1,1);
CREATE INDEX hash_idx ON hash_heap_float4 USING hash (x);
DROP TABLE hash_heap_float4 CASCADE;
+
+-- Check reloptions min max values and that it is not allowed to ALTER
+CREATE INDEX hash_f8_index2 ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=9);
+CREATE INDEX hash_f8_index2 ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=101);
+ALTER INDEX hash_f8_index SET (fillfactor=99);
\ No newline at end of file
diff --git a/src/test/regress/sql/reloptions.sql b/src/test/regress/sql/reloptions.sql
new file mode 100644
index 0000000..0bfec28
--- /dev/null
+++ b/src/test/regress/sql/reloptions.sql
@@ -0,0 +1,153 @@
+
+-- Simple create
+CREATE TABLE reloptions_test(i INT) WITH (fillfactor=30,autovacuum_enabled = false, autovacuum_analyze_scale_factor = 0.2);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+
+-- Test сase insensitive
+CREATE TABLE reloptions_test3(i INT) WITH (FiLlFaCtoR=40);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test3'::regclass;
+
+-- Fail on min/max values check
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=2);
+
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=110);
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor = -10.0);
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor = 110.0);
+
+-- Fail when option and namespase do not exist
+
+CREATE TABLE reloptions_test2(i INT) WITH (not_existing_option=2);
+
+CREATE TABLE reloptions_test2(i INT) WITH (not_existing_namespace.fillfactor=2);
+
+-- Fail while setting unproper value
+
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=30.5);
+
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor='string');
+
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=true);
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled=12);
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled=30.5);
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled='string');
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor='string');
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor=true);
+
+-- Specifing name only should fail as if there was =ture after it
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor);
+
+-- Simple ALTER TABLE
+
+ALTER TABLE reloptions_test SET (fillfactor=31, autovacuum_analyze_scale_factor = 0.3);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+
+-- Check that we cat set boolean option to true just by mentioning it
+
+ALTER TABLE reloptions_test SET (autovacuum_enabled, fillfactor=32);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+
+-- Check that RESET works well
+
+ALTER TABLE reloptions_test RESET (fillfactor);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+
+-- Check that RESETting all values make NULL reloptions record in pg_class
+ALTER TABLE reloptions_test RESET (autovacuum_enabled, autovacuum_analyze_scale_factor);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass AND reloptions IS NULL;
+
+-- Check RESET fails on att=value
+
+ALTER TABLE reloptions_test RESET (fillfactor=12);
+
+-- Check oids options is ignored
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test(i INT) WITH (fillfactor=20, oids=true);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+
+-- Now testing toast.* options
+DROP TABLE reloptions_test;
+
+CREATE TABLE reloptions_test (s VARCHAR) WITH (toast.autovacuum_vacuum_cost_delay = 23 );
+
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+
+ALTER TABLE reloptions_test SET (toast.autovacuum_vacuum_cost_delay = 24);
+
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+
+ALTER TABLE reloptions_test RESET (toast.autovacuum_vacuum_cost_delay);
+
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+
+-- Fail on unexisting options in toast namespace
+CREATE TABLE reloptions_test2 (i int) WITH (toast.not_existing_option = 42 );
+
+-- Fail on setting reloption to a table that does not have a TOAST relation
+CREATE TABLE reloptions_test2 (i int) WITH (toast.autovacuum_vacuum_cost_delay = 23 );
+DROP TABLE reloptions_test;
+
+CREATE TABLE reloptions_test(i INT);
+ALTER TABLE reloptions_test SET (toast.autovacuum_vacuum_cost_delay = 23);
+ALTER TABLE reloptions_test RESET (toast.autovacuum_vacuum_cost_delay);
+
+-- autovacuum_analyze_scale_factor and autovacuum_analyze_threshold should be
+-- accepted by heap but rejected by toast (special case)
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test (s VARCHAR) WITH (autovacuum_analyze_scale_factor=1, autovacuum_analyze_threshold=1);
+
+CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analyze_scale_factor=1);
+CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analyze_threshold=1);
+
+-- And now mixed toast + heap
+DROP TABLE reloptions_test;
+
+CREATE TABLE reloptions_test (s VARCHAR) WITH (toast.autovacuum_vacuum_cost_delay = 23, autovacuum_vacuum_cost_delay = 24, fillfactor = 40);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+
+-- Same FOR CREATE and ALTER INDEX for btree indexes
+
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (fillfactor=30);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx'::regclass;
+
+-- Fail when option and namespase do not exist
+
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_existing_option=2);
+
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_existing_option.fillfactor=2);
+
+-- Check ranges
+
+CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfactor=1);
+
+CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfactor=130);
+
+-- Check alter
+
+ALTER INDEX reloptions_test_idx SET (fillfactor=40);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx'::regclass;
+
+-- Check alter on empty relop list
+
+CREATE INDEX reloptions_test_idx3 ON reloptions_test (s);
+
+ALTER INDEX reloptions_test_idx3 SET (fillfactor=40);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx3'::regclass;
+
+
diff --git a/src/test/regress/sql/spgist.sql b/src/test/regress/sql/spgist.sql
index 5896b50..8d26e49 100644
--- a/src/test/regress/sql/spgist.sql
+++ b/src/test/regress/sql/spgist.sql
@@ -5,7 +5,7 @@
-- testing SP-GiST code itself.
create table spgist_point_tbl(id int4, p point);
-create index spgist_point_idx on spgist_point_tbl using spgist(p);
+create index spgist_point_idx on spgist_point_tbl using spgist(p) with (fillfactor = 75);
-- Test vacuum-root operation. It gets invoked when the root is also a leaf,
-- i.e. the index is very small.
@@ -48,3 +48,10 @@ select g, 'baaaaaaaaaaaaaar' || g from generate_series(1, 1000) g;
-- tuple to be moved to another page.
insert into spgist_text_tbl (id, t)
select -g, 'f' || repeat('o', 100-g) || 'surprise' from generate_series(1, 100) g;
+
+-- Check reloptions min, max values
+create index spgist_point_idx2 on spgist_point_tbl using spgist(p) with (fillfactor = 9);
+create index spgist_point_idx2 on spgist_point_tbl using spgist(p) with (fillfactor = 101);
+
+-- ALTER reloption should work
+alter index spgist_point_idx set (fillfactor = 76);
В письме от 19 апреля 2017 21:31:42 пользователь Nikolay Shaplov написал:
I rebased the patch again, after code reindent.
Still waiting for review.
--
Do code for fun. Can do it for money (Perl & C/C++ ~10h/week)
Attachments:
reloptions7c.difftext/x-patch; charset=UTF-8; name=reloptions7c.diffDownload
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 cbc50f7..7e3bbc0 100644
--- a/contrib/bloom/expected/bloom.out
+++ b/contrib/bloom/expected/bloom.out
@@ -210,3 +210,37 @@ ORDER BY 1;
text_ops | t
(2 rows)
+-- reloptions test
+DROP INDEX bloomidx;
+CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (length=7, col1 = 4);
+SELECT reloptions FROM pg_class WHERE oid = 'bloomidx'::regclass;
+ reloptions
+-------------------
+ {length=7,col1=4}
+(1 row)
+
+-- check for min and max values
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=0);
+ERROR: value 0 out of bounds for option "length"
+DETAIL: Valid values are between "1" and "4096".
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=4097);
+ERROR: value 4097 out of bounds for option "length"
+DETAIL: Valid values are between "1" and "4096".
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=0);
+ERROR: value 0 out of bounds for option "col1"
+DETAIL: Valid values are between "1" and "4095".
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=4096);
+ERROR: value 4096 out of bounds for option "col1"
+DETAIL: Valid values are between "1" and "4095".
+-- check post_validate for colN<lengh
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=10,col1=11);
+ERROR: col0 should not be grater than length
+-- check that ALTERing of reloptions is forbidden
+ALTER INDEX bloomidx SET (length=4);
+ERROR: changing parameter "length" is not allowed
+ALTER INDEX bloomidx SET (col1=4);
+ERROR: changing parameter "col1" is not allowed
+ALTER INDEX bloomidx RESET (length);
+ERROR: changing parameter "length" is not allowed
+ALTER INDEX bloomidx RESET (col1);
+ERROR: changing parameter "col1" is not allowed
diff --git a/contrib/bloom/sql/bloom.sql b/contrib/bloom/sql/bloom.sql
index 2227460..dbac21c 100644
--- a/contrib/bloom/sql/bloom.sql
+++ b/contrib/bloom/sql/bloom.sql
@@ -81,3 +81,23 @@ SELECT opcname, amvalidate(opc.oid)
FROM pg_opclass opc JOIN pg_am am ON am.oid = opcmethod
WHERE amname = 'bloom'
ORDER BY 1;
+
+-- reloptions test
+
+DROP INDEX bloomidx;
+CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (length=7, col1 = 4);
+SELECT reloptions FROM pg_class WHERE oid = 'bloomidx'::regclass;
+-- check for min and max values
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=0);
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=4097);
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=0);
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=4096);
+-- check post_validate for colN<lengh
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=10,col1=11);
+
+-- check that ALTERing of reloptions is forbidden
+
+ALTER INDEX bloomidx SET (length=4);
+ALTER INDEX bloomidx SET (col1=4);
+ALTER INDEX bloomidx RESET (length);
+ALTER INDEX bloomidx RESET (col1);
diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
index 81136b1..2c37165 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 2396bd4..07fa996 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 efebeb0..c0b9744 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;
@@ -798,37 +799,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.
*/
@@ -1426,3 +1396,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 fb27944..c9fb063 100644
--- a/src/backend/access/common/Makefile
+++ b/src/backend/access/common/Makefile
@@ -12,7 +12,7 @@ subdir = src/backend/access/common
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
-OBJS = bufmask.o heaptuple.o indextuple.o printsimple.o printtup.o \
+OBJS = bufmask.o heaptuple.o indextuple.o options.o printsimple.o printtup.o \
reloptions.o scankey.o tupconvert.o tupdesc.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/common/options.c b/src/backend/access/common/options.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 91e4a8c..0f2af51 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -16,7 +16,7 @@
#include "access/gin_private.h"
#include "access/ginxlog.h"
-#include "access/reloptions.h"
+#include "access/options.h"
#include "access/xloginsert.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_type.h"
@@ -26,7 +26,8 @@
#include "utils/builtins.h"
#include "utils/index_selfuncs.h"
#include "utils/typcache.h"
-
+#include "utils/memutils.h"
+#include "utils/guc.h"
/*
* GIN handler function: return IndexAmRoutine with access method parameters
@@ -60,7 +61,6 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amvacuumcleanup = ginvacuumcleanup;
amroutine->amcanreturn = NULL;
amroutine->amcostestimate = gincostestimate;
- amroutine->amoptions = ginoptions;
amroutine->amproperty = NULL;
amroutine->amvalidate = ginvalidate;
amroutine->ambeginscan = ginbeginscan;
@@ -70,6 +70,7 @@ ginhandler(PG_FUNCTION_ARGS)
amroutine->amendscan = ginendscan;
amroutine->ammarkpos = NULL;
amroutine->amrestrpos = NULL;
+ amroutine->amrelopt_catalog = gingetreloptcatalog;
amroutine->amestimateparallelscan = NULL;
amroutine->aminitparallelscan = NULL;
amroutine->amparallelrescan = NULL;
@@ -593,35 +594,6 @@ ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
return entries;
}
-bytea *
-ginoptions(Datum reloptions, bool validate)
-{
- relopt_value *options;
- GinOptions *rdopts;
- int numoptions;
- static const relopt_parse_elt tab[] = {
- {"fastupdate", RELOPT_TYPE_BOOL, offsetof(GinOptions, useFastUpdate)},
- {"gin_pending_list_limit", RELOPT_TYPE_INT, offsetof(GinOptions,
- pendingListCleanupSize)}
- };
-
- options = parseRelOptions(reloptions, validate, RELOPT_KIND_GIN,
- &numoptions);
-
- /* if none set, we're done */
- if (numoptions == 0)
- return NULL;
-
- rdopts = allocateReloptStruct(sizeof(GinOptions), options, numoptions);
-
- fillRelOptions((void *) rdopts, sizeof(GinOptions), options, numoptions,
- validate, tab, lengthof(tab));
-
- pfree(options);
-
- return (bytea *) rdopts;
-}
-
/*
* Fetch index's statistical data into *stats
*
@@ -698,3 +670,32 @@ ginUpdateStats(Relation index, const GinStatsData *stats)
END_CRIT_SECTION();
}
+
+
+static options_catalog *gin_relopt_catalog = NULL;
+
+void *
+gingetreloptcatalog(void)
+{
+ if (!gin_relopt_catalog)
+ {
+ gin_relopt_catalog = allocateOptionsCatalog(NULL,
+ sizeof(GinOptions), 2);
+
+ optionsCatalogAddItemBool(gin_relopt_catalog, "fastupdate",
+ "Enables \"fast update\" feature for this GIN index",
+ AccessExclusiveLock,
+ 0,
+ offsetof(GinOptions, useFastUpdate),
+ GIN_DEFAULT_USE_FASTUPDATE);
+
+ optionsCatalogAddItemInt(gin_relopt_catalog, "gin_pending_list_limit",
+ "Maximum size of the pending list for this GIN index, in kilobytes",
+ AccessExclusiveLock,
+ 0,
+ offsetof(GinOptions, pendingListCleanupSize),
+ -1, 64, MAX_KILOBYTES);
+
+ }
+ return gin_relopt_catalog;
+}
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index 565525b..d44916f 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 4756a70..8d5c839 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 b6ccc1a..5f46058 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -18,14 +18,13 @@
#include "access/gist_private.h"
#include "access/htup_details.h"
-#include "access/reloptions.h"
+#include "access/options.h"
#include "catalog/pg_opclass.h"
#include "storage/indexfsm.h"
#include "storage/lmgr.h"
#include "utils/builtins.h"
#include "utils/syscache.h"
-
/*
* Write itup vector to page, has no control of free space.
*/
@@ -812,34 +811,6 @@ gistNewBuffer(Relation r)
return buffer;
}
-bytea *
-gistoptions(Datum reloptions, bool validate)
-{
- relopt_value *options;
- GiSTOptions *rdopts;
- int numoptions;
- static const relopt_parse_elt tab[] = {
- {"fillfactor", RELOPT_TYPE_INT, offsetof(GiSTOptions, fillfactor)},
- {"buffering", RELOPT_TYPE_STRING, offsetof(GiSTOptions, bufferingModeOffset)}
- };
-
- options = parseRelOptions(reloptions, validate, RELOPT_KIND_GIST,
- &numoptions);
-
- /* if none set, we're done */
- if (numoptions == 0)
- return NULL;
-
- rdopts = allocateReloptStruct(sizeof(GiSTOptions), options, numoptions);
-
- fillRelOptions((void *) rdopts, sizeof(GiSTOptions), options, numoptions,
- validate, tab, lengthof(tab));
-
- pfree(options);
-
- return (bytea *) rdopts;
-}
-
/*
* gistproperty() -- Check boolean properties of indexes.
*
@@ -965,3 +936,33 @@ gistGetFakeLSN(Relation rel)
return GetFakeLSNForUnloggedRel();
}
}
+
+static options_catalog *gist_relopt_catalog = NULL;
+
+void *
+gistgetreloptcatalog(void)
+{
+ static const char *enum_names[] = GIST_OPTION_BUFFERING_VALUE_NAMES;
+
+ if (!gist_relopt_catalog)
+ {
+ gist_relopt_catalog = allocateOptionsCatalog(NULL,
+ sizeof(GiSTOptions), 2);
+
+ optionsCatalogAddItemInt(gist_relopt_catalog, "fillfactor",
+ "Packs gist index pages only to this percentage",
+ NoLock, /* No ALTER, no lock */
+ OPTION_DEFINITION_FLAG_FORBID_ALTER,
+ offsetof(GiSTOptions, fillfactor),
+ GIST_DEFAULT_FILLFACTOR,
+ GIST_MIN_FILLFACTOR, 100);
+ optionsCatalogAddItemEnum(gist_relopt_catalog, "buffering",
+ "Enables buffering build for this GiST index",
+ NoLock, /* No ALTER, no lock */
+ OPTION_DEFINITION_FLAG_FORBID_ALTER,
+ offsetof(GiSTOptions, buffering_mode),
+ enum_names,
+ GIST_OPTION_BUFFERING_AUTO);
+ }
+ return gist_relopt_catalog;
+}
diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c
index d89c192..6ef32cf 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 1cb18a7..8330044 100644
--- a/src/backend/access/hash/hashpage.c
+++ b/src/backend/access/hash/hashpage.c
@@ -360,7 +360,7 @@ _hash_init(Relation rel, double num_tuples, ForkNumber forkNum)
data_width = sizeof(uint32);
item_width = MAXALIGN(sizeof(IndexTupleData)) + MAXALIGN(data_width) +
sizeof(ItemIdData); /* include the line pointer */
- ffactor = RelationGetTargetPageUsage(rel, HASH_DEFAULT_FILLFACTOR) / item_width;
+ ffactor = (BLCKSZ * HashGetFillFactor(rel) / 100) / item_width;
/* keep to a sane range */
if (ffactor < 10)
ffactor = 10;
diff --git a/src/backend/access/hash/hashutil.c b/src/backend/access/hash/hashutil.c
index 9b803af..c5309b0 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
*/
@@ -581,3 +575,28 @@ _hash_kill_items(IndexScanDesc scan)
MarkBufferDirtyHint(so->hashso_curbuf, true);
}
}
+
+static options_catalog *hash_relopt_catalog = NULL;
+
+void *
+hashgetreloptcatalog(void)
+{
+ if (!hash_relopt_catalog)
+ {
+ hash_relopt_catalog = allocateOptionsCatalog(NULL,
+ sizeof(HashRelOptions), 1);
+
+ optionsCatalogAddItemInt(hash_relopt_catalog, "fillfactor",
+ "Packs hash index pages only to this percentage",
+ NoLock, /* No ALTER -- no lock */
+ OPTION_DEFINITION_FLAG_FORBID_ALTER, /* fillfactor is actualy
+ * stored in metapage
+ * and should not be
+ * changed once index is
+ * created */
+ offsetof(HashRelOptions, fillfactor),
+ HASH_DEFAULT_FILLFACTOR,
+ HASH_MIN_FILLFACTOR, 100);
+ }
+ return hash_relopt_catalog;
+}
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 9766723..6780ef8 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -2670,7 +2670,10 @@ heap_multi_insert(Relation relation, HeapTuple *tuples, int ntuples,
bool need_cids = RelationIsAccessibleInLogicalDecoding(relation);
needwal = !(options & HEAP_INSERT_SKIP_WAL) && RelationNeedsWAL(relation);
- saveFreeSpace = RelationGetTargetPageFreeSpace(relation,
+ if (IsToastRelation(relation))
+ saveFreeSpace = ToastGetTargetPageFreeSpace();
+ else
+ saveFreeSpace = HeapGetTargetPageFreeSpace(relation,
HEAP_DEFAULT_FILLFACTOR);
/* Toast and set header data in all the tuples */
diff --git a/src/backend/access/heap/hio.c b/src/backend/access/heap/hio.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 52231ac..0686cf0 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 bd560e4..b40abc9 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 4aca7e4..f37054a 100644
--- a/src/backend/access/nbtree/nbtinsert.c
+++ b/src/backend/access/nbtree/nbtinsert.c
@@ -1433,8 +1433,7 @@ _bt_findsplitloc(Relation rel,
state.is_rightmost = P_RIGHTMOST(opaque);
state.have_split = false;
if (state.is_leaf)
- state.fillfactor = RelationGetFillFactor(rel,
- BTREE_DEFAULT_FILLFACTOR);
+ state.fillfactor = BTGetFillFactor(rel);
else
state.fillfactor = BTREE_NONLEAF_FILLFACTOR;
state.newitemonleft = false; /* these just to keep compiler quiet */
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.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 8656af4..022e893 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 29756eb..aaa9270 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -36,7 +36,7 @@
/* Potentially set by pg_upgrade_support functions */
Oid binary_upgrade_next_toast_pg_type_oid = InvalidOid;
-static void CheckAndCreateToastTable(Oid relOid, Datum reloptions,
+static bool CheckAndCreateToastTable(Oid relOid, Datum reloptions,
LOCKMODE lockmode, bool check);
static bool create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
Datum reloptions, LOCKMODE lockmode, bool check);
@@ -67,23 +67,26 @@ NewHeapCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmode)
CheckAndCreateToastTable(relOid, reloptions, lockmode, false);
}
-void
+bool
NewRelationCreateToastTable(Oid relOid, Datum reloptions)
{
- CheckAndCreateToastTable(relOid, reloptions, AccessExclusiveLock, false);
+ return CheckAndCreateToastTable(relOid, reloptions,
+ AccessExclusiveLock, false);
}
-static void
+static bool
CheckAndCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmode, bool check)
{
Relation rel;
+ bool success;
rel = heap_open(relOid, lockmode);
/* create_toast_table does all the work */
- (void) create_toast_table(rel, InvalidOid, InvalidOid, reloptions, lockmode, check);
+ success = create_toast_table(rel, InvalidOid, InvalidOid, reloptions, lockmode, check);
heap_close(rel, NoLock);
+ return success;
}
/*
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 97f9c55..79c5147 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 620704e..c3c8b21 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;
@@ -536,7 +536,7 @@ DefineIndex(Oid relationId,
accessMethodName)));
amcanorder = amRoutine->amcanorder;
- amoptions = amRoutine->amoptions;
+ amoption_catalog = amRoutine->amrelopt_catalog;
pfree(amRoutine);
ReleaseSysCache(tuple);
@@ -550,10 +550,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 7d9c769..0847809 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -513,7 +513,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
Datum reloptions;
ListCell *listptr;
AttrNumber attnum;
- static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
Oid ofTypeId;
ObjectAddress address;
@@ -599,13 +598,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)
{
@@ -3113,7 +3122,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);
@@ -3155,13 +3165,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)
{
@@ -3372,7 +3384,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:
@@ -4147,7 +4162,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",
@@ -6228,11 +6243,11 @@ ATExecSetOptions(Relation rel, const char *colName, Node *options,
/* Generate new proposed attoptions (text array) */
datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
&isnull);
- newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
- castNode(List, options), NULL, NULL,
- false, isReset);
- /* Validate new options */
- (void) attribute_reloptions(newOptions, true);
+
+ newOptions = transformOptions(get_attribute_options_catalog(),
+ isnull ? (Datum) 0 : datum,
+ castNode(List, options), OPTIONS_PARSE_MODE_FOR_ALTER |
+ (isReset ? OPTIONS_PARSE_MODE_FOR_RESET : 0));
/* Build new tuple. */
memset(repl_null, false, sizeof(repl_null));
@@ -10256,7 +10271,7 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
Datum repl_val[Natts_pg_class];
bool repl_null[Natts_pg_class];
bool repl_repl[Natts_pg_class];
- static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
+ options_parse_mode parse_mode;
if (defList == NIL && operation != AT_ReplaceRelOptions)
return; /* nothing to do */
@@ -10285,25 +10300,52 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
&isnull);
}
- /* Generate new proposed reloptions (text array) */
- newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
- defList, NULL, validnsps, false,
- operation == AT_ResetRelOptions);
-
/* Validate */
+ parse_mode = OPTIONS_PARSE_MODE_FOR_ALTER;
+ if (operation == AT_ResetRelOptions)
+ parse_mode |= OPTIONS_PARSE_MODE_FOR_RESET;
+
switch (rel->rd_rel->relkind)
{
case RELKIND_RELATION:
case RELKIND_TOASTVALUE:
case RELKIND_MATVIEW:
case RELKIND_PARTITIONED_TABLE:
- (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
+ {
+ char *namespaces[] = HEAP_RELOPT_NAMESPACES;
+ List *heapDefList;
+
+ optionsDefListValdateNamespaces(defList, namespaces);
+ heapDefList = optionsDefListFilterNamespaces(
+ defList, NULL);
+ newOptions = transformOptions(get_heap_relopt_catalog(),
+ isnull ? (Datum) 0 : datum,
+ heapDefList, parse_mode);
+ }
break;
case RELKIND_VIEW:
- (void) view_reloptions(newOptions, true);
+ {
+ newOptions = transformOptions(
+ get_view_relopt_catalog(),
+ isnull ? (Datum) 0 : datum,
+ defList, parse_mode);
+ }
break;
case RELKIND_INDEX:
- (void) index_reloptions(rel->rd_amroutine->amoptions, newOptions, true);
+ if (rel->rd_amroutine->amrelopt_catalog)
+ {
+ newOptions = transformOptions(
+ rel->rd_amroutine->amrelopt_catalog(),
+ isnull ? (Datum) 0 : datum,
+ defList, parse_mode);
+ }
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("index %s does not support options",
+ RelationGetRelationName(rel))));
+ }
break;
default:
ereport(ERROR,
@@ -10317,7 +10359,7 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
if (rel->rd_rel->relkind == RELKIND_VIEW)
{
Query *view_query = get_view_query(rel);
- List *view_options = untransformRelOptions(newOptions);
+ List *view_options = optionsTextArrayToDefList(newOptions);
ListCell *cell;
bool check_option = false;
@@ -10377,6 +10419,8 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
{
Relation toastrel;
Oid toastid = rel->rd_rel->reltoastrelid;
+ List *toastDefList;
+ options_parse_mode parse_mode;
toastrel = heap_open(toastid, lockmode);
@@ -10400,12 +10444,15 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
&isnull);
}
+ parse_mode = OPTIONS_PARSE_MODE_FOR_ALTER;
+ if (operation == AT_ResetRelOptions)
+ parse_mode |= OPTIONS_PARSE_MODE_FOR_RESET;
- newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
- defList, "toast", validnsps, false,
- operation == AT_ResetRelOptions);
+ toastDefList = optionsDefListFilterNamespaces(defList, "toast");
- (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
+ newOptions = transformOptions(get_toast_relopt_catalog(),
+ isnull ? (Datum) 0 : datum,
+ toastDefList, parse_mode);
memset(repl_val, 0, sizeof(repl_val));
memset(repl_null, false, sizeof(repl_null));
@@ -10433,6 +10480,18 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
heap_close(toastrel, NoLock);
}
+ else
+ {
+ List *toastDefList;
+
+ toastDefList = optionsDefListFilterNamespaces(defList, "toast");
+ if (toastDefList)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("TOAST relation for this table does not exist. Can't change toast.* storage parameters")));
+ }
+ }
heap_close(pgclass, RowExclusiveLock);
}
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.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 dc0b0b0..0bf8492 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -140,7 +140,7 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
&rel->pages, &rel->tuples, &rel->allvisfrac);
/* Retrieve the parallel_workers reloption, or -1 if not set. */
- rel->rel_parallel_workers = RelationGetParallelWorkers(relation, -1);
+ rel->rel_parallel_workers = HeapGetParallelWorkers(relation, -1);
/*
* Make list of indexes. Ignore indexes on system catalogs if told to.
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 9ff80b8..35e8008 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -3148,7 +3148,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 ee5f3a3..b528068 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1520,7 +1520,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 00b1e82..506748d 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -2736,6 +2736,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 ||
@@ -2745,8 +2746,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 6225216..f822968 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -1531,7 +1531,7 @@ ApplyRetrieveRule(Query *parsetree,
rte->rtekind = RTE_SUBQUERY;
rte->relid = InvalidOid;
- rte->security_barrier = RelationIsSecurityView(relation);
+ rte->security_barrier = ViewIsSecurityView(relation);
rte->subquery = rule_action;
rte->inh = false; /* must not be set for a subquery */
@@ -3035,7 +3035,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
@@ -3071,8 +3071,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 ddacac8..19dcbb1 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 43238dd..50a2566 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 7fa868b..4c3cc38 100644
--- a/src/include/access/hash.h
+++ b/src/include/access/hash.h
@@ -224,6 +224,19 @@ typedef struct HashMetaPageData
typedef HashMetaPageData *HashMetaPage;
+
+typedef struct HashRelOptions
+{
+ int32 varlena_header_; /* varlena header (do not touch directly!) */
+ int fillfactor; /* page fill factor in percent (0..100) */
+} HashRelOptions;
+
+
+#define HashGetFillFactor(relation) \
+ ((relation)->rd_options ? \
+ ((HashRelOptions *) (relation)->rd_options)->fillfactor : \
+ HASH_DEFAULT_FILLFACTOR)
+
/*
* Maximum size of a hash index item (it's okay to have only one per page)
*/
@@ -319,7 +332,6 @@ extern IndexBulkDeleteResult *hashbulkdelete(IndexVacuumInfo *info,
void *callback_state);
extern IndexBulkDeleteResult *hashvacuumcleanup(IndexVacuumInfo *info,
IndexBulkDeleteResult *stats);
-extern bytea *hashoptions(Datum reloptions, bool validate);
extern bool hashvalidate(Oid opclassoid);
extern Datum hash_any(register const unsigned char *k, register int keylen);
@@ -422,4 +434,6 @@ extern void hashbucketcleanup(Relation rel, Bucket cur_bucket,
bool bucket_has_garbage,
IndexBulkDeleteCallback callback, void *callback_state);
+extern void *hashgetreloptcatalog(void);
+
#endif /* HASH_H */
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index e6abbec..9bbf426 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 abd31b6..21e85ed 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -31,7 +31,7 @@ extern Oid AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode);
extern void AlterTable(Oid relid, LOCKMODE lockmode, AlterTableStmt *stmt);
-extern LOCKMODE AlterTableGetLockLevel(List *cmds);
+extern LOCKMODE AlterTableGetLockLevel(Oid relid, List *cmds);
extern void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.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 13d6a4b..c0bd149 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -2354,7 +2354,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;
@@ -3506,3 +3508,10 @@ create table parted_validate_test_1 partition of parted_validate_test for values
alter table parted_validate_test add constraint parted_validate_test_chka check (a > 0) not valid;
alter table parted_validate_test validate constraint parted_validate_test_chka;
drop table parted_validate_test;
+-- test alter column options
+CREATE TABLE tmp(i integer);
+INSERT INTO tmp VALUES (1);
+ALTER TABLE tmp ALTER COLUMN i SET (n_distinct = 1, n_distinct_inherited = 2);
+ALTER TABLE tmp ALTER COLUMN i RESET (n_distinct_inherited);
+ANALYZE tmp;
+DROP TABLE tmp;
diff --git a/src/test/regress/expected/brin.out b/src/test/regress/expected/brin.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/create_index.out b/src/test/regress/expected/create_index.out
index 26cd059..7b8f805 100644
--- a/src/test/regress/expected/create_index.out
+++ b/src/test/regress/expected/create_index.out
@@ -2337,7 +2337,7 @@ Options: fastupdate=on, gin_pending_list_limit=128
CREATE INDEX hash_i4_index ON hash_i4_heap USING hash (random int4_ops);
CREATE INDEX hash_name_index ON hash_name_heap USING hash (random name_ops);
CREATE INDEX hash_txt_index ON hash_txt_heap USING hash (random text_ops);
-CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_ops);
+CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=60);
CREATE UNLOGGED TABLE unlogged_hash_table (id int4);
CREATE INDEX unlogged_hash_index ON unlogged_hash_table USING hash (id int4_ops);
DROP TABLE unlogged_hash_table;
diff --git a/src/test/regress/expected/gist.out b/src/test/regress/expected/gist.out
index 91f9998..df52e46 100644
--- a/src/test/regress/expected/gist.out
+++ b/src/test/regress/expected/gist.out
@@ -12,6 +12,20 @@ insert into gist_point_tbl (id, p)
select g+100000, point(g*10+1, g*10+1) from generate_series(1, 10000) g;
-- To test vacuum, delete some entries from all over the index.
delete from gist_point_tbl where id % 2 = 1;
+-- Test buffering and fillfactor reloption takes valid values
+create index gist_pointidx2 on gist_point_tbl using gist(p) with (buffering = on, fillfactor=50);
+create index gist_pointidx3 on gist_point_tbl using gist(p) with (buffering = off);
+create index gist_pointidx4 on gist_point_tbl using gist(p) with (buffering = auto);
+--Test buffering and fillfactor for refising invalid values
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (buffering = invalid_value);
+ERROR: invalid value for "buffering" option
+DETAIL: Valid values are "on", "off", "auto".
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfactor=9);
+ERROR: value 9 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfactor=101);
+ERROR: value 101 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
-- And also delete some concentration of values. (GiST doesn't currently
-- attempt to delete pages even when they become empty, but if it did, this
-- would exercise it)
diff --git a/src/test/regress/expected/hash_index.out b/src/test/regress/expected/hash_index.out
index 0bbaa2a..6d5cf0d 100644
--- a/src/test/regress/expected/hash_index.out
+++ b/src/test/regress/expected/hash_index.out
@@ -229,3 +229,12 @@ CREATE TABLE hash_heap_float4 (x float4, y int);
INSERT INTO hash_heap_float4 VALUES (1.1,1);
CREATE INDEX hash_idx ON hash_heap_float4 USING hash (x);
DROP TABLE hash_heap_float4 CASCADE;
+-- Check reloptions min max values and that it is not allowed to ALTER
+CREATE INDEX hash_f8_index2 ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=9);
+ERROR: value 9 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+CREATE INDEX hash_f8_index2 ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=101);
+ERROR: value 101 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+ALTER INDEX hash_f8_index SET (fillfactor=99);
+ERROR: changing parameter "fillfactor" is not allowed
diff --git a/src/test/regress/expected/reloptions.out b/src/test/regress/expected/reloptions.out
new file mode 100644
index 0000000..835d72f
--- /dev/null
+++ b/src/test/regress/expected/reloptions.out
@@ -0,0 +1,193 @@
+-- Simple create
+CREATE TABLE reloptions_test(i INT) WITH (fillfactor=30,autovacuum_enabled = false, autovacuum_analyze_scale_factor = 0.2);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+ reloptions
+------------------------------------------------------------------------------
+ {fillfactor=30,autovacuum_enabled=false,autovacuum_analyze_scale_factor=0.2}
+(1 row)
+
+-- Test сase insensitive
+CREATE TABLE reloptions_test3(i INT) WITH (FiLlFaCtoR=40);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test3'::regclass;
+ reloptions
+-----------------
+ {fillfactor=40}
+(1 row)
+
+-- Fail on min/max values check
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=2);
+ERROR: value 2 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=110);
+ERROR: value 110 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor = -10.0);
+ERROR: value -10.0 out of bounds for option "autovacuum_analyze_scale_factor"
+DETAIL: Valid values are between "0.000000" and "100.000000".
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor = 110.0);
+ERROR: value 110.0 out of bounds for option "autovacuum_analyze_scale_factor"
+DETAIL: Valid values are between "0.000000" and "100.000000".
+-- Fail when option and namespase do not exist
+CREATE TABLE reloptions_test2(i INT) WITH (not_existing_option=2);
+ERROR: unrecognized parameter "not_existing_option"
+CREATE TABLE reloptions_test2(i INT) WITH (not_existing_namespace.fillfactor=2);
+ERROR: unrecognized parameter namespace "not_existing_namespace"
+-- Fail while setting unproper value
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=30.5);
+ERROR: invalid value for integer option "fillfactor": 30.5
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor='string');
+ERROR: invalid value for integer option "fillfactor": string
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=true);
+ERROR: invalid value for integer option "fillfactor": true
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled=12);
+ERROR: invalid value for boolean option "autovacuum_enabled": 12
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled=30.5);
+ERROR: invalid value for boolean option "autovacuum_enabled": 30.5
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled='string');
+ERROR: invalid value for boolean option "autovacuum_enabled": string
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor='string');
+ERROR: invalid value for floating point option "autovacuum_analyze_scale_factor": string
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor=true);
+ERROR: invalid value for floating point option "autovacuum_analyze_scale_factor": true
+-- Specifing name only should fail as if there was =ture after it
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor);
+ERROR: invalid value for integer option "fillfactor": true
+-- Simple ALTER TABLE
+ALTER TABLE reloptions_test SET (fillfactor=31, autovacuum_analyze_scale_factor = 0.3);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+ reloptions
+------------------------------------------------------------------------------
+ {autovacuum_enabled=false,fillfactor=31,autovacuum_analyze_scale_factor=0.3}
+(1 row)
+
+-- Check that we cat set boolean option to true just by mentioning it
+ALTER TABLE reloptions_test SET (autovacuum_enabled, fillfactor=32);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+ reloptions
+-----------------------------------------------------------------------------
+ {autovacuum_analyze_scale_factor=0.3,autovacuum_enabled=true,fillfactor=32}
+(1 row)
+
+-- Check that RESET works well
+ALTER TABLE reloptions_test RESET (fillfactor);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+ reloptions
+---------------------------------------------------------------
+ {autovacuum_analyze_scale_factor=0.3,autovacuum_enabled=true}
+(1 row)
+
+-- Check that RESETting all values make NULL reloptions record in pg_class
+ALTER TABLE reloptions_test RESET (autovacuum_enabled, autovacuum_analyze_scale_factor);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass AND reloptions IS NULL;
+ reloptions
+------------
+
+(1 row)
+
+-- Check RESET fails on att=value
+ALTER TABLE reloptions_test RESET (fillfactor=12);
+ERROR: RESET must not include values for parameters
+-- Check oids options is ignored
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test(i INT) WITH (fillfactor=20, oids=true);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+ reloptions
+-----------------
+ {fillfactor=20}
+(1 row)
+
+-- Now testing toast.* options
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test (s VARCHAR) WITH (toast.autovacuum_vacuum_cost_delay = 23 );
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+ reloptions
+-----------------------------------
+ {autovacuum_vacuum_cost_delay=23}
+(1 row)
+
+ALTER TABLE reloptions_test SET (toast.autovacuum_vacuum_cost_delay = 24);
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+ reloptions
+-----------------------------------
+ {autovacuum_vacuum_cost_delay=24}
+(1 row)
+
+ALTER TABLE reloptions_test RESET (toast.autovacuum_vacuum_cost_delay);
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+ reloptions
+------------
+
+(1 row)
+
+-- Fail on unexisting options in toast namespace
+CREATE TABLE reloptions_test2 (i int) WITH (toast.not_existing_option = 42 );
+ERROR: unrecognized parameter "toast.not_existing_option"
+-- Fail on setting reloption to a table that does not have a TOAST relation
+CREATE TABLE reloptions_test2 (i int) WITH (toast.autovacuum_vacuum_cost_delay = 23 );
+ERROR: no TOAST relation was created for a table. Can't set toast.* storage parameters
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test(i INT);
+ALTER TABLE reloptions_test SET (toast.autovacuum_vacuum_cost_delay = 23);
+ERROR: TOAST relation for this table does not exist. Can't change toast.* storage parameters
+ALTER TABLE reloptions_test RESET (toast.autovacuum_vacuum_cost_delay);
+ERROR: TOAST relation for this table does not exist. Can't change toast.* storage parameters
+-- autovacuum_analyze_scale_factor and autovacuum_analyze_threshold should be
+-- accepted by heap but rejected by toast (special case)
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test (s VARCHAR) WITH (autovacuum_analyze_scale_factor=1, autovacuum_analyze_threshold=1);
+CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analyze_scale_factor=1);
+ERROR: unrecognized parameter "toast.autovacuum_analyze_scale_factor"
+CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analyze_threshold=1);
+ERROR: unrecognized parameter "toast.autovacuum_analyze_threshold"
+-- And now mixed toast + heap
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test (s VARCHAR) WITH (toast.autovacuum_vacuum_cost_delay = 23, autovacuum_vacuum_cost_delay = 24, fillfactor = 40);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+ reloptions
+-------------------------------------------------
+ {autovacuum_vacuum_cost_delay=24,fillfactor=40}
+(1 row)
+
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+ reloptions
+-----------------------------------
+ {autovacuum_vacuum_cost_delay=23}
+(1 row)
+
+-- Same FOR CREATE and ALTER INDEX for btree indexes
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (fillfactor=30);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx'::regclass;
+ reloptions
+-----------------
+ {fillfactor=30}
+(1 row)
+
+-- Fail when option and namespase do not exist
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_existing_option=2);
+ERROR: unrecognized parameter "not_existing_option"
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_existing_option.fillfactor=2);
+ERROR: unrecognized parameter namespace "not_existing_option"
+-- Check ranges
+CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfactor=1);
+ERROR: value 1 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfactor=130);
+ERROR: value 130 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+-- Check alter
+ALTER INDEX reloptions_test_idx SET (fillfactor=40);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx'::regclass;
+ reloptions
+-----------------
+ {fillfactor=40}
+(1 row)
+
+-- Check alter on empty relop list
+CREATE INDEX reloptions_test_idx3 ON reloptions_test (s);
+ALTER INDEX reloptions_test_idx3 SET (fillfactor=40);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx3'::regclass;
+ reloptions
+-----------------
+ {fillfactor=40}
+(1 row)
+
diff --git a/src/test/regress/expected/spgist.out b/src/test/regress/expected/spgist.out
index 0691e91..bb58fb7 100644
--- a/src/test/regress/expected/spgist.out
+++ b/src/test/regress/expected/spgist.out
@@ -4,7 +4,7 @@
-- There are other tests to test different SP-GiST opclasses. This is for
-- testing SP-GiST code itself.
create table spgist_point_tbl(id int4, p point);
-create index spgist_point_idx on spgist_point_tbl using spgist(p);
+create index spgist_point_idx on spgist_point_tbl using spgist(p) with (fillfactor = 75);
-- Test vacuum-root operation. It gets invoked when the root is also a leaf,
-- i.e. the index is very small.
insert into spgist_point_tbl (id, p)
@@ -37,3 +37,12 @@ select g, 'baaaaaaaaaaaaaar' || g from generate_series(1, 1000) g;
-- tuple to be moved to another page.
insert into spgist_text_tbl (id, t)
select -g, 'f' || repeat('o', 100-g) || 'surprise' from generate_series(1, 100) g;
+-- Check reloptions min, max values
+create index spgist_point_idx2 on spgist_point_tbl using spgist(p) with (fillfactor = 9);
+ERROR: value 9 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+create index spgist_point_idx2 on spgist_point_tbl using spgist(p) with (fillfactor = 101);
+ERROR: value 101 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+-- ALTER reloption should work
+alter index spgist_point_idx set (fillfactor = 76);
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.out
index 971dddd..85d1daf 100644
--- a/src/test/regress/expected/updatable_views.out
+++ b/src/test/regress/expected/updatable_views.out
@@ -1551,7 +1551,7 @@ SELECT * FROM base_tbl;
ALTER VIEW rw_view1 SET (check_option=here); -- invalid
ERROR: invalid value for "check_option" option
-DETAIL: Valid values are "local" and "cascaded".
+DETAIL: Valid values are "local", "cascaded".
ALTER VIEW rw_view1 SET (check_option=local);
INSERT INTO rw_view2 VALUES (-20); -- should fail
ERROR: new row violates check option for view "rw_view1"
diff --git a/src/test/regress/input/tablespace.source b/src/test/regress/input/tablespace.source
index 03a62bd..7f7934b 100644
--- a/src/test/regress/input/tablespace.source
+++ b/src/test/regress/input/tablespace.source
@@ -12,10 +12,10 @@ DROP TABLESPACE regress_tblspacewith;
CREATE TABLESPACE regress_tblspace LOCATION '@testtablespace@';
-- try setting and resetting some properties for the new tablespace
-ALTER TABLESPACE regress_tblspace SET (random_page_cost = 1.0);
+ALTER TABLESPACE regress_tblspace SET (random_page_cost = 1.0, seq_page_cost = 1.1);
ALTER TABLESPACE regress_tblspace SET (some_nonexistent_parameter = true); -- fail
ALTER TABLESPACE regress_tblspace RESET (random_page_cost = 2.0); -- fail
-ALTER TABLESPACE regress_tblspace RESET (random_page_cost, seq_page_cost); -- ok
+ALTER TABLESPACE regress_tblspace RESET (random_page_cost, effective_io_concurrency); -- ok
-- create a schema we can use
CREATE SCHEMA testschema;
diff --git a/src/test/regress/output/tablespace.source b/src/test/regress/output/tablespace.source
index aaedf5f..2443511 100644
--- a/src/test/regress/output/tablespace.source
+++ b/src/test/regress/output/tablespace.source
@@ -14,12 +14,12 @@ DROP TABLESPACE regress_tblspacewith;
-- create a tablespace we can use
CREATE TABLESPACE regress_tblspace LOCATION '@testtablespace@';
-- try setting and resetting some properties for the new tablespace
-ALTER TABLESPACE regress_tblspace SET (random_page_cost = 1.0);
+ALTER TABLESPACE regress_tblspace SET (random_page_cost = 1.0, seq_page_cost = 1.1);
ALTER TABLESPACE regress_tblspace SET (some_nonexistent_parameter = true); -- fail
ERROR: unrecognized parameter "some_nonexistent_parameter"
ALTER TABLESPACE regress_tblspace RESET (random_page_cost = 2.0); -- fail
ERROR: RESET must not include values for parameters
-ALTER TABLESPACE regress_tblspace RESET (random_page_cost, seq_page_cost); -- ok
+ALTER TABLESPACE regress_tblspace RESET (random_page_cost, effective_io_concurrency); -- ok
-- create a schema we can use
CREATE SCHEMA testschema;
-- try a table
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 2369261..970025b 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -109,7 +109,7 @@ test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combo
# NB: temp.sql does a reconnect which transiently uses 2 connections,
# so keep this parallel group to at most 19 tests
# ----------
-test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml
+test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml reloptions
# ----------
# Another group of parallel tests
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 5e8b7e9..55d5deb 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -178,5 +178,6 @@ test: returning
test: largeobject
test: with
test: xml
+test: reloptions
test: event_trigger
test: stats
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index 5dd1402..4e84fb8 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -2307,3 +2307,10 @@ create table parted_validate_test_1 partition of parted_validate_test for values
alter table parted_validate_test add constraint parted_validate_test_chka check (a > 0) not valid;
alter table parted_validate_test validate constraint parted_validate_test_chka;
drop table parted_validate_test;
+-- test alter column options
+CREATE TABLE tmp(i integer);
+INSERT INTO tmp VALUES (1);
+ALTER TABLE tmp ALTER COLUMN i SET (n_distinct = 1, n_distinct_inherited = 2);
+ALTER TABLE tmp ALTER COLUMN i RESET (n_distinct_inherited);
+ANALYZE tmp;
+DROP TABLE tmp;
diff --git a/src/test/regress/sql/brin.sql b/src/test/regress/sql/brin.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/create_index.sql b/src/test/regress/sql/create_index.sql
index 1648072..1f8d101 100644
--- a/src/test/regress/sql/create_index.sql
+++ b/src/test/regress/sql/create_index.sql
@@ -682,7 +682,7 @@ CREATE INDEX hash_name_index ON hash_name_heap USING hash (random name_ops);
CREATE INDEX hash_txt_index ON hash_txt_heap USING hash (random text_ops);
-CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_ops);
+CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=60);
CREATE UNLOGGED TABLE unlogged_hash_table (id int4);
CREATE INDEX unlogged_hash_index ON unlogged_hash_table USING hash (id int4_ops);
diff --git a/src/test/regress/sql/gist.sql b/src/test/regress/sql/gist.sql
index 49126fd..f65ce37 100644
--- a/src/test/regress/sql/gist.sql
+++ b/src/test/regress/sql/gist.sql
@@ -17,6 +17,17 @@ select g+100000, point(g*10+1, g*10+1) from generate_series(1, 10000) g;
-- To test vacuum, delete some entries from all over the index.
delete from gist_point_tbl where id % 2 = 1;
+-- Test buffering and fillfactor reloption takes valid values
+create index gist_pointidx2 on gist_point_tbl using gist(p) with (buffering = on, fillfactor=50);
+create index gist_pointidx3 on gist_point_tbl using gist(p) with (buffering = off);
+create index gist_pointidx4 on gist_point_tbl using gist(p) with (buffering = auto);
+--Test buffering and fillfactor for refising invalid values
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (buffering = invalid_value);
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfactor=9);
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfactor=101);
+
+
+
-- And also delete some concentration of values. (GiST doesn't currently
-- attempt to delete pages even when they become empty, but if it did, this
-- would exercise it)
diff --git a/src/test/regress/sql/hash_index.sql b/src/test/regress/sql/hash_index.sql
index 9af03d2..f245525 100644
--- a/src/test/regress/sql/hash_index.sql
+++ b/src/test/regress/sql/hash_index.sql
@@ -192,3 +192,8 @@ CREATE TABLE hash_heap_float4 (x float4, y int);
INSERT INTO hash_heap_float4 VALUES (1.1,1);
CREATE INDEX hash_idx ON hash_heap_float4 USING hash (x);
DROP TABLE hash_heap_float4 CASCADE;
+
+-- Check reloptions min max values and that it is not allowed to ALTER
+CREATE INDEX hash_f8_index2 ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=9);
+CREATE INDEX hash_f8_index2 ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=101);
+ALTER INDEX hash_f8_index SET (fillfactor=99);
\ No newline at end of file
diff --git a/src/test/regress/sql/reloptions.sql b/src/test/regress/sql/reloptions.sql
new file mode 100644
index 0000000..0bfec28
--- /dev/null
+++ b/src/test/regress/sql/reloptions.sql
@@ -0,0 +1,153 @@
+
+-- Simple create
+CREATE TABLE reloptions_test(i INT) WITH (fillfactor=30,autovacuum_enabled = false, autovacuum_analyze_scale_factor = 0.2);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+
+-- Test сase insensitive
+CREATE TABLE reloptions_test3(i INT) WITH (FiLlFaCtoR=40);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test3'::regclass;
+
+-- Fail on min/max values check
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=2);
+
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=110);
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor = -10.0);
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor = 110.0);
+
+-- Fail when option and namespase do not exist
+
+CREATE TABLE reloptions_test2(i INT) WITH (not_existing_option=2);
+
+CREATE TABLE reloptions_test2(i INT) WITH (not_existing_namespace.fillfactor=2);
+
+-- Fail while setting unproper value
+
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=30.5);
+
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor='string');
+
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=true);
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled=12);
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled=30.5);
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled='string');
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor='string');
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor=true);
+
+-- Specifing name only should fail as if there was =ture after it
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor);
+
+-- Simple ALTER TABLE
+
+ALTER TABLE reloptions_test SET (fillfactor=31, autovacuum_analyze_scale_factor = 0.3);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+
+-- Check that we cat set boolean option to true just by mentioning it
+
+ALTER TABLE reloptions_test SET (autovacuum_enabled, fillfactor=32);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+
+-- Check that RESET works well
+
+ALTER TABLE reloptions_test RESET (fillfactor);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+
+-- Check that RESETting all values make NULL reloptions record in pg_class
+ALTER TABLE reloptions_test RESET (autovacuum_enabled, autovacuum_analyze_scale_factor);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass AND reloptions IS NULL;
+
+-- Check RESET fails on att=value
+
+ALTER TABLE reloptions_test RESET (fillfactor=12);
+
+-- Check oids options is ignored
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test(i INT) WITH (fillfactor=20, oids=true);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+
+-- Now testing toast.* options
+DROP TABLE reloptions_test;
+
+CREATE TABLE reloptions_test (s VARCHAR) WITH (toast.autovacuum_vacuum_cost_delay = 23 );
+
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+
+ALTER TABLE reloptions_test SET (toast.autovacuum_vacuum_cost_delay = 24);
+
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+
+ALTER TABLE reloptions_test RESET (toast.autovacuum_vacuum_cost_delay);
+
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+
+-- Fail on unexisting options in toast namespace
+CREATE TABLE reloptions_test2 (i int) WITH (toast.not_existing_option = 42 );
+
+-- Fail on setting reloption to a table that does not have a TOAST relation
+CREATE TABLE reloptions_test2 (i int) WITH (toast.autovacuum_vacuum_cost_delay = 23 );
+DROP TABLE reloptions_test;
+
+CREATE TABLE reloptions_test(i INT);
+ALTER TABLE reloptions_test SET (toast.autovacuum_vacuum_cost_delay = 23);
+ALTER TABLE reloptions_test RESET (toast.autovacuum_vacuum_cost_delay);
+
+-- autovacuum_analyze_scale_factor and autovacuum_analyze_threshold should be
+-- accepted by heap but rejected by toast (special case)
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test (s VARCHAR) WITH (autovacuum_analyze_scale_factor=1, autovacuum_analyze_threshold=1);
+
+CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analyze_scale_factor=1);
+CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analyze_threshold=1);
+
+-- And now mixed toast + heap
+DROP TABLE reloptions_test;
+
+CREATE TABLE reloptions_test (s VARCHAR) WITH (toast.autovacuum_vacuum_cost_delay = 23, autovacuum_vacuum_cost_delay = 24, fillfactor = 40);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+
+-- Same FOR CREATE and ALTER INDEX for btree indexes
+
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (fillfactor=30);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx'::regclass;
+
+-- Fail when option and namespase do not exist
+
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_existing_option=2);
+
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_existing_option.fillfactor=2);
+
+-- Check ranges
+
+CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfactor=1);
+
+CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfactor=130);
+
+-- Check alter
+
+ALTER INDEX reloptions_test_idx SET (fillfactor=40);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx'::regclass;
+
+-- Check alter on empty relop list
+
+CREATE INDEX reloptions_test_idx3 ON reloptions_test (s);
+
+ALTER INDEX reloptions_test_idx3 SET (fillfactor=40);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx3'::regclass;
+
+
diff --git a/src/test/regress/sql/spgist.sql b/src/test/regress/sql/spgist.sql
index 5896b50..8d26e49 100644
--- a/src/test/regress/sql/spgist.sql
+++ b/src/test/regress/sql/spgist.sql
@@ -5,7 +5,7 @@
-- testing SP-GiST code itself.
create table spgist_point_tbl(id int4, p point);
-create index spgist_point_idx on spgist_point_tbl using spgist(p);
+create index spgist_point_idx on spgist_point_tbl using spgist(p) with (fillfactor = 75);
-- Test vacuum-root operation. It gets invoked when the root is also a leaf,
-- i.e. the index is very small.
@@ -48,3 +48,10 @@ select g, 'baaaaaaaaaaaaaar' || g from generate_series(1, 1000) g;
-- tuple to be moved to another page.
insert into spgist_text_tbl (id, t)
select -g, 'f' || repeat('o', 100-g) || 'surprise' from generate_series(1, 100) g;
+
+-- Check reloptions min, max values
+create index spgist_point_idx2 on spgist_point_tbl using spgist(p) with (fillfactor = 9);
+create index spgist_point_idx2 on spgist_point_tbl using spgist(p) with (fillfactor = 101);
+
+-- ALTER reloption should work
+alter index spgist_point_idx set (fillfactor = 76);
В письме от 25 июня 2017 21:05:49 пользователь Nikolay Shaplov написал:
I've just made sure that patch is still applyable to the current master.
And I am still waiting for reviews :-)
--
Do code for fun. Can do it for money (Perl & C/C++ ~10h/week)
Attachments:
reloptions7d.difftext/x-patch; charset=UTF-8; name=reloptions7d.diffDownload
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 cbc50f7..7e3bbc0 100644
--- a/contrib/bloom/expected/bloom.out
+++ b/contrib/bloom/expected/bloom.out
@@ -210,3 +210,37 @@ ORDER BY 1;
text_ops | t
(2 rows)
+-- reloptions test
+DROP INDEX bloomidx;
+CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (length=7, col1 = 4);
+SELECT reloptions FROM pg_class WHERE oid = 'bloomidx'::regclass;
+ reloptions
+-------------------
+ {length=7,col1=4}
+(1 row)
+
+-- check for min and max values
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=0);
+ERROR: value 0 out of bounds for option "length"
+DETAIL: Valid values are between "1" and "4096".
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=4097);
+ERROR: value 4097 out of bounds for option "length"
+DETAIL: Valid values are between "1" and "4096".
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=0);
+ERROR: value 0 out of bounds for option "col1"
+DETAIL: Valid values are between "1" and "4095".
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=4096);
+ERROR: value 4096 out of bounds for option "col1"
+DETAIL: Valid values are between "1" and "4095".
+-- check post_validate for colN<lengh
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=10,col1=11);
+ERROR: col0 should not be grater than length
+-- check that ALTERing of reloptions is forbidden
+ALTER INDEX bloomidx SET (length=4);
+ERROR: changing parameter "length" is not allowed
+ALTER INDEX bloomidx SET (col1=4);
+ERROR: changing parameter "col1" is not allowed
+ALTER INDEX bloomidx RESET (length);
+ERROR: changing parameter "length" is not allowed
+ALTER INDEX bloomidx RESET (col1);
+ERROR: changing parameter "col1" is not allowed
diff --git a/contrib/bloom/sql/bloom.sql b/contrib/bloom/sql/bloom.sql
index 2227460..dbac21c 100644
--- a/contrib/bloom/sql/bloom.sql
+++ b/contrib/bloom/sql/bloom.sql
@@ -81,3 +81,23 @@ SELECT opcname, amvalidate(opc.oid)
FROM pg_opclass opc JOIN pg_am am ON am.oid = opcmethod
WHERE amname = 'bloom'
ORDER BY 1;
+
+-- reloptions test
+
+DROP INDEX bloomidx;
+CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (length=7, col1 = 4);
+SELECT reloptions FROM pg_class WHERE oid = 'bloomidx'::regclass;
+-- check for min and max values
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=0);
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=4097);
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=0);
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=4096);
+-- check post_validate for colN<lengh
+CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=10,col1=11);
+
+-- check that ALTERing of reloptions is forbidden
+
+ALTER INDEX bloomidx SET (length=4);
+ALTER INDEX bloomidx SET (col1=4);
+ALTER INDEX bloomidx RESET (length);
+ALTER INDEX bloomidx RESET (col1);
diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
index 3113b07..345e86c 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 fb27944..c9fb063 100644
--- a/src/backend/access/common/Makefile
+++ b/src/backend/access/common/Makefile
@@ -12,7 +12,7 @@ subdir = src/backend/access/common
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
-OBJS = bufmask.o heaptuple.o indextuple.o printsimple.o printtup.o \
+OBJS = bufmask.o heaptuple.o indextuple.o options.o printsimple.o printtup.o \
reloptions.o scankey.o tupconvert.o tupdesc.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/common/options.c b/src/backend/access/common/options.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 565525b..d44916f 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 b6ccc1a..5f46058 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -18,14 +18,13 @@
#include "access/gist_private.h"
#include "access/htup_details.h"
-#include "access/reloptions.h"
+#include "access/options.h"
#include "catalog/pg_opclass.h"
#include "storage/indexfsm.h"
#include "storage/lmgr.h"
#include "utils/builtins.h"
#include "utils/syscache.h"
-
/*
* Write itup vector to page, has no control of free space.
*/
@@ -812,34 +811,6 @@ gistNewBuffer(Relation r)
return buffer;
}
-bytea *
-gistoptions(Datum reloptions, bool validate)
-{
- relopt_value *options;
- GiSTOptions *rdopts;
- int numoptions;
- static const relopt_parse_elt tab[] = {
- {"fillfactor", RELOPT_TYPE_INT, offsetof(GiSTOptions, fillfactor)},
- {"buffering", RELOPT_TYPE_STRING, offsetof(GiSTOptions, bufferingModeOffset)}
- };
-
- options = parseRelOptions(reloptions, validate, RELOPT_KIND_GIST,
- &numoptions);
-
- /* if none set, we're done */
- if (numoptions == 0)
- return NULL;
-
- rdopts = allocateReloptStruct(sizeof(GiSTOptions), options, numoptions);
-
- fillRelOptions((void *) rdopts, sizeof(GiSTOptions), options, numoptions,
- validate, tab, lengthof(tab));
-
- pfree(options);
-
- return (bytea *) rdopts;
-}
-
/*
* gistproperty() -- Check boolean properties of indexes.
*
@@ -965,3 +936,33 @@ gistGetFakeLSN(Relation rel)
return GetFakeLSNForUnloggedRel();
}
}
+
+static options_catalog *gist_relopt_catalog = NULL;
+
+void *
+gistgetreloptcatalog(void)
+{
+ static const char *enum_names[] = GIST_OPTION_BUFFERING_VALUE_NAMES;
+
+ if (!gist_relopt_catalog)
+ {
+ gist_relopt_catalog = allocateOptionsCatalog(NULL,
+ sizeof(GiSTOptions), 2);
+
+ optionsCatalogAddItemInt(gist_relopt_catalog, "fillfactor",
+ "Packs gist index pages only to this percentage",
+ NoLock, /* No ALTER, no lock */
+ OPTION_DEFINITION_FLAG_FORBID_ALTER,
+ offsetof(GiSTOptions, fillfactor),
+ GIST_DEFAULT_FILLFACTOR,
+ GIST_MIN_FILLFACTOR, 100);
+ optionsCatalogAddItemEnum(gist_relopt_catalog, "buffering",
+ "Enables buffering build for this GiST index",
+ NoLock, /* No ALTER, no lock */
+ OPTION_DEFINITION_FLAG_FORBID_ALTER,
+ offsetof(GiSTOptions, buffering_mode),
+ enum_names,
+ GIST_OPTION_BUFFERING_AUTO);
+ }
+ return gist_relopt_catalog;
+}
diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/hash.c
index d89c192..6ef32cf 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 0579841..b6ca7a4 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 869cbc1..f593d33 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
*/
@@ -581,3 +575,28 @@ _hash_kill_items(IndexScanDesc scan)
MarkBufferDirtyHint(so->hashso_curbuf, true);
}
}
+
+static options_catalog *hash_relopt_catalog = NULL;
+
+void *
+hashgetreloptcatalog(void)
+{
+ if (!hash_relopt_catalog)
+ {
+ hash_relopt_catalog = allocateOptionsCatalog(NULL,
+ sizeof(HashRelOptions), 1);
+
+ optionsCatalogAddItemInt(hash_relopt_catalog, "fillfactor",
+ "Packs hash index pages only to this percentage",
+ NoLock, /* No ALTER -- no lock */
+ OPTION_DEFINITION_FLAG_FORBID_ALTER, /* fillfactor is actualy
+ * stored in metapage
+ * and should not be
+ * changed once index is
+ * created */
+ offsetof(HashRelOptions, fillfactor),
+ HASH_DEFAULT_FILLFACTOR,
+ HASH_MIN_FILLFACTOR, 100);
+ }
+ return hash_relopt_catalog;
+}
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index e29c5ad..2267953 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -2684,7 +2684,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 52231ac..0686cf0 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 bd560e4..b40abc9 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 e60210c..7b6c4ca 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 b61aaac..66b1318 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;
@@ -536,7 +536,7 @@ DefineIndex(Oid relationId,
accessMethodName)));
amcanorder = amRoutine->amcanorder;
- amoptions = amRoutine->amoptions;
+ amoption_catalog = amRoutine->amrelopt_catalog;
pfree(amRoutine);
ReleaseSysCache(tuple);
@@ -550,10 +550,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 0f08245..ab2722f 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -518,7 +518,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
Datum reloptions;
ListCell *listptr;
AttrNumber attnum;
- static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
Oid ofTypeId;
ObjectAddress address;
@@ -604,13 +603,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)
{
@@ -3121,7 +3130,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);
@@ -3163,13 +3173,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)
{
@@ -3380,7 +3392,10 @@ AlterTableGetLockLevel(List *cmds)
* getTables() */
case AT_ResetRelOptions: /* Uses MVCC in getIndexes() and
* getTables() */
- cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
+ rel = relation_open(relid, NoLock);
+ cmd_lockmode = AlterTableGetRelOptionsLockLevel(rel,
+ castNode(List, cmd->def));
+ relation_close(rel,NoLock);
break;
case AT_AttachPartition:
@@ -4155,7 +4170,7 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode)
errmsg("cannot rewrite system relation \"%s\"",
RelationGetRelationName(OldHeap))));
- if (RelationIsUsedAsCatalogTable(OldHeap))
+ if (HeapIsUsedAsCatalogTable(OldHeap))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot rewrite table \"%s\" used as a catalog table",
@@ -6254,11 +6269,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));
@@ -10286,7 +10301,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 */
@@ -10315,25 +10330,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,
@@ -10347,7 +10389,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;
@@ -10407,6 +10449,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);
@@ -10430,12 +10474,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));
@@ -10463,6 +10510,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 a1ebd4a..8140cda 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -140,7 +140,7 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
&rel->pages, &rel->tuples, &rel->allvisfrac);
/* Retrieve the parallel_workers reloption, or -1 if not set. */
- rel->rel_parallel_workers = RelationGetParallelWorkers(relation, -1);
+ rel->rel_parallel_workers = HeapGetParallelWorkers(relation, -1);
/*
* Make list of indexes. Ignore indexes on system catalogs if told to.
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 9ff80b8..35e8008 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -3148,7 +3148,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 2058679..32a3669 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1523,7 +1523,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 776b1c0..c0a868c 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -2691,6 +2691,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 ||
@@ -2700,8 +2701,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 ef52dd5..9afa4b4 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 775477c..6815937 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 c06dcb2..ea86270 100644
--- a/src/include/access/hash.h
+++ b/src/include/access/hash.h
@@ -234,6 +234,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)
*/
@@ -337,7 +350,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);
@@ -443,4 +455,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 e6abbec..9bbf426 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 abd31b6..21e85ed 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -31,7 +31,7 @@ extern Oid AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode);
extern void AlterTable(Oid relid, LOCKMODE lockmode, AlterTableStmt *stmt);
-extern LOCKMODE AlterTableGetLockLevel(List *cmds);
+extern LOCKMODE AlterTableGetLockLevel(Oid relid, List *cmds);
extern void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.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 ed03cb9..aaa1d5b 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -2354,7 +2354,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;
@@ -3551,3 +3553,10 @@ create table parted_validate_test_1 partition of parted_validate_test for values
alter table parted_validate_test add constraint parted_validate_test_chka check (a > 0) not valid;
alter table parted_validate_test validate constraint parted_validate_test_chka;
drop table parted_validate_test;
+-- test alter column options
+CREATE TABLE tmp(i integer);
+INSERT INTO tmp VALUES (1);
+ALTER TABLE tmp ALTER COLUMN i SET (n_distinct = 1, n_distinct_inherited = 2);
+ALTER TABLE tmp ALTER COLUMN i RESET (n_distinct_inherited);
+ANALYZE tmp;
+DROP TABLE tmp;
diff --git a/src/test/regress/expected/brin.out b/src/test/regress/expected/brin.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/create_index.out b/src/test/regress/expected/create_index.out
index 064adb4..16a3b8b 100644
--- a/src/test/regress/expected/create_index.out
+++ b/src/test/regress/expected/create_index.out
@@ -2337,7 +2337,7 @@ Options: fastupdate=on, gin_pending_list_limit=128
CREATE INDEX hash_i4_index ON hash_i4_heap USING hash (random int4_ops);
CREATE INDEX hash_name_index ON hash_name_heap USING hash (random name_ops);
CREATE INDEX hash_txt_index ON hash_txt_heap USING hash (random text_ops);
-CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_ops);
+CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=60);
CREATE UNLOGGED TABLE unlogged_hash_table (id int4);
CREATE INDEX unlogged_hash_index ON unlogged_hash_table USING hash (id int4_ops);
DROP TABLE unlogged_hash_table;
diff --git a/src/test/regress/expected/gist.out b/src/test/regress/expected/gist.out
index 91f9998..df52e46 100644
--- a/src/test/regress/expected/gist.out
+++ b/src/test/regress/expected/gist.out
@@ -12,6 +12,20 @@ insert into gist_point_tbl (id, p)
select g+100000, point(g*10+1, g*10+1) from generate_series(1, 10000) g;
-- To test vacuum, delete some entries from all over the index.
delete from gist_point_tbl where id % 2 = 1;
+-- Test buffering and fillfactor reloption takes valid values
+create index gist_pointidx2 on gist_point_tbl using gist(p) with (buffering = on, fillfactor=50);
+create index gist_pointidx3 on gist_point_tbl using gist(p) with (buffering = off);
+create index gist_pointidx4 on gist_point_tbl using gist(p) with (buffering = auto);
+--Test buffering and fillfactor for refising invalid values
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (buffering = invalid_value);
+ERROR: invalid value for "buffering" option
+DETAIL: Valid values are "on", "off", "auto".
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfactor=9);
+ERROR: value 9 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfactor=101);
+ERROR: value 101 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
-- And also delete some concentration of values. (GiST doesn't currently
-- attempt to delete pages even when they become empty, but if it did, this
-- would exercise it)
diff --git a/src/test/regress/expected/hash_index.out b/src/test/regress/expected/hash_index.out
index 0bbaa2a..6d5cf0d 100644
--- a/src/test/regress/expected/hash_index.out
+++ b/src/test/regress/expected/hash_index.out
@@ -229,3 +229,12 @@ CREATE TABLE hash_heap_float4 (x float4, y int);
INSERT INTO hash_heap_float4 VALUES (1.1,1);
CREATE INDEX hash_idx ON hash_heap_float4 USING hash (x);
DROP TABLE hash_heap_float4 CASCADE;
+-- Check reloptions min max values and that it is not allowed to ALTER
+CREATE INDEX hash_f8_index2 ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=9);
+ERROR: value 9 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+CREATE INDEX hash_f8_index2 ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=101);
+ERROR: value 101 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+ALTER INDEX hash_f8_index SET (fillfactor=99);
+ERROR: changing parameter "fillfactor" is not allowed
diff --git a/src/test/regress/expected/reloptions.out b/src/test/regress/expected/reloptions.out
new file mode 100644
index 0000000..835d72f
--- /dev/null
+++ b/src/test/regress/expected/reloptions.out
@@ -0,0 +1,193 @@
+-- Simple create
+CREATE TABLE reloptions_test(i INT) WITH (fillfactor=30,autovacuum_enabled = false, autovacuum_analyze_scale_factor = 0.2);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+ reloptions
+------------------------------------------------------------------------------
+ {fillfactor=30,autovacuum_enabled=false,autovacuum_analyze_scale_factor=0.2}
+(1 row)
+
+-- Test сase insensitive
+CREATE TABLE reloptions_test3(i INT) WITH (FiLlFaCtoR=40);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test3'::regclass;
+ reloptions
+-----------------
+ {fillfactor=40}
+(1 row)
+
+-- Fail on min/max values check
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=2);
+ERROR: value 2 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=110);
+ERROR: value 110 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor = -10.0);
+ERROR: value -10.0 out of bounds for option "autovacuum_analyze_scale_factor"
+DETAIL: Valid values are between "0.000000" and "100.000000".
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor = 110.0);
+ERROR: value 110.0 out of bounds for option "autovacuum_analyze_scale_factor"
+DETAIL: Valid values are between "0.000000" and "100.000000".
+-- Fail when option and namespase do not exist
+CREATE TABLE reloptions_test2(i INT) WITH (not_existing_option=2);
+ERROR: unrecognized parameter "not_existing_option"
+CREATE TABLE reloptions_test2(i INT) WITH (not_existing_namespace.fillfactor=2);
+ERROR: unrecognized parameter namespace "not_existing_namespace"
+-- Fail while setting unproper value
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=30.5);
+ERROR: invalid value for integer option "fillfactor": 30.5
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor='string');
+ERROR: invalid value for integer option "fillfactor": string
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=true);
+ERROR: invalid value for integer option "fillfactor": true
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled=12);
+ERROR: invalid value for boolean option "autovacuum_enabled": 12
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled=30.5);
+ERROR: invalid value for boolean option "autovacuum_enabled": 30.5
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled='string');
+ERROR: invalid value for boolean option "autovacuum_enabled": string
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor='string');
+ERROR: invalid value for floating point option "autovacuum_analyze_scale_factor": string
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor=true);
+ERROR: invalid value for floating point option "autovacuum_analyze_scale_factor": true
+-- Specifing name only should fail as if there was =ture after it
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor);
+ERROR: invalid value for integer option "fillfactor": true
+-- Simple ALTER TABLE
+ALTER TABLE reloptions_test SET (fillfactor=31, autovacuum_analyze_scale_factor = 0.3);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+ reloptions
+------------------------------------------------------------------------------
+ {autovacuum_enabled=false,fillfactor=31,autovacuum_analyze_scale_factor=0.3}
+(1 row)
+
+-- Check that we cat set boolean option to true just by mentioning it
+ALTER TABLE reloptions_test SET (autovacuum_enabled, fillfactor=32);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+ reloptions
+-----------------------------------------------------------------------------
+ {autovacuum_analyze_scale_factor=0.3,autovacuum_enabled=true,fillfactor=32}
+(1 row)
+
+-- Check that RESET works well
+ALTER TABLE reloptions_test RESET (fillfactor);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+ reloptions
+---------------------------------------------------------------
+ {autovacuum_analyze_scale_factor=0.3,autovacuum_enabled=true}
+(1 row)
+
+-- Check that RESETting all values make NULL reloptions record in pg_class
+ALTER TABLE reloptions_test RESET (autovacuum_enabled, autovacuum_analyze_scale_factor);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass AND reloptions IS NULL;
+ reloptions
+------------
+
+(1 row)
+
+-- Check RESET fails on att=value
+ALTER TABLE reloptions_test RESET (fillfactor=12);
+ERROR: RESET must not include values for parameters
+-- Check oids options is ignored
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test(i INT) WITH (fillfactor=20, oids=true);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+ reloptions
+-----------------
+ {fillfactor=20}
+(1 row)
+
+-- Now testing toast.* options
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test (s VARCHAR) WITH (toast.autovacuum_vacuum_cost_delay = 23 );
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+ reloptions
+-----------------------------------
+ {autovacuum_vacuum_cost_delay=23}
+(1 row)
+
+ALTER TABLE reloptions_test SET (toast.autovacuum_vacuum_cost_delay = 24);
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+ reloptions
+-----------------------------------
+ {autovacuum_vacuum_cost_delay=24}
+(1 row)
+
+ALTER TABLE reloptions_test RESET (toast.autovacuum_vacuum_cost_delay);
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+ reloptions
+------------
+
+(1 row)
+
+-- Fail on unexisting options in toast namespace
+CREATE TABLE reloptions_test2 (i int) WITH (toast.not_existing_option = 42 );
+ERROR: unrecognized parameter "toast.not_existing_option"
+-- Fail on setting reloption to a table that does not have a TOAST relation
+CREATE TABLE reloptions_test2 (i int) WITH (toast.autovacuum_vacuum_cost_delay = 23 );
+ERROR: no TOAST relation was created for a table. Can't set toast.* storage parameters
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test(i INT);
+ALTER TABLE reloptions_test SET (toast.autovacuum_vacuum_cost_delay = 23);
+ERROR: TOAST relation for this table does not exist. Can't change toast.* storage parameters
+ALTER TABLE reloptions_test RESET (toast.autovacuum_vacuum_cost_delay);
+ERROR: TOAST relation for this table does not exist. Can't change toast.* storage parameters
+-- autovacuum_analyze_scale_factor and autovacuum_analyze_threshold should be
+-- accepted by heap but rejected by toast (special case)
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test (s VARCHAR) WITH (autovacuum_analyze_scale_factor=1, autovacuum_analyze_threshold=1);
+CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analyze_scale_factor=1);
+ERROR: unrecognized parameter "toast.autovacuum_analyze_scale_factor"
+CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analyze_threshold=1);
+ERROR: unrecognized parameter "toast.autovacuum_analyze_threshold"
+-- And now mixed toast + heap
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test (s VARCHAR) WITH (toast.autovacuum_vacuum_cost_delay = 23, autovacuum_vacuum_cost_delay = 24, fillfactor = 40);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+ reloptions
+-------------------------------------------------
+ {autovacuum_vacuum_cost_delay=24,fillfactor=40}
+(1 row)
+
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+ reloptions
+-----------------------------------
+ {autovacuum_vacuum_cost_delay=23}
+(1 row)
+
+-- Same FOR CREATE and ALTER INDEX for btree indexes
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (fillfactor=30);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx'::regclass;
+ reloptions
+-----------------
+ {fillfactor=30}
+(1 row)
+
+-- Fail when option and namespase do not exist
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_existing_option=2);
+ERROR: unrecognized parameter "not_existing_option"
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_existing_option.fillfactor=2);
+ERROR: unrecognized parameter namespace "not_existing_option"
+-- Check ranges
+CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfactor=1);
+ERROR: value 1 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfactor=130);
+ERROR: value 130 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+-- Check alter
+ALTER INDEX reloptions_test_idx SET (fillfactor=40);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx'::regclass;
+ reloptions
+-----------------
+ {fillfactor=40}
+(1 row)
+
+-- Check alter on empty relop list
+CREATE INDEX reloptions_test_idx3 ON reloptions_test (s);
+ALTER INDEX reloptions_test_idx3 SET (fillfactor=40);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx3'::regclass;
+ reloptions
+-----------------
+ {fillfactor=40}
+(1 row)
+
diff --git a/src/test/regress/expected/spgist.out b/src/test/regress/expected/spgist.out
index 0691e91..bb58fb7 100644
--- a/src/test/regress/expected/spgist.out
+++ b/src/test/regress/expected/spgist.out
@@ -4,7 +4,7 @@
-- There are other tests to test different SP-GiST opclasses. This is for
-- testing SP-GiST code itself.
create table spgist_point_tbl(id int4, p point);
-create index spgist_point_idx on spgist_point_tbl using spgist(p);
+create index spgist_point_idx on spgist_point_tbl using spgist(p) with (fillfactor = 75);
-- Test vacuum-root operation. It gets invoked when the root is also a leaf,
-- i.e. the index is very small.
insert into spgist_point_tbl (id, p)
@@ -37,3 +37,12 @@ select g, 'baaaaaaaaaaaaaar' || g from generate_series(1, 1000) g;
-- tuple to be moved to another page.
insert into spgist_text_tbl (id, t)
select -g, 'f' || repeat('o', 100-g) || 'surprise' from generate_series(1, 100) g;
+-- Check reloptions min, max values
+create index spgist_point_idx2 on spgist_point_tbl using spgist(p) with (fillfactor = 9);
+ERROR: value 9 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+create index spgist_point_idx2 on spgist_point_tbl using spgist(p) with (fillfactor = 101);
+ERROR: value 101 out of bounds for option "fillfactor"
+DETAIL: Valid values are between "10" and "100".
+-- ALTER reloption should work
+alter index spgist_point_idx set (fillfactor = 76);
diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regress/expected/updatable_views.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/input/tablespace.source b/src/test/regress/input/tablespace.source
index 03a62bd..7f7934b 100644
--- a/src/test/regress/input/tablespace.source
+++ b/src/test/regress/input/tablespace.source
@@ -12,10 +12,10 @@ DROP TABLESPACE regress_tblspacewith;
CREATE TABLESPACE regress_tblspace LOCATION '@testtablespace@';
-- try setting and resetting some properties for the new tablespace
-ALTER TABLESPACE regress_tblspace SET (random_page_cost = 1.0);
+ALTER TABLESPACE regress_tblspace SET (random_page_cost = 1.0, seq_page_cost = 1.1);
ALTER TABLESPACE regress_tblspace SET (some_nonexistent_parameter = true); -- fail
ALTER TABLESPACE regress_tblspace RESET (random_page_cost = 2.0); -- fail
-ALTER TABLESPACE regress_tblspace RESET (random_page_cost, seq_page_cost); -- ok
+ALTER TABLESPACE regress_tblspace RESET (random_page_cost, effective_io_concurrency); -- ok
-- create a schema we can use
CREATE SCHEMA testschema;
diff --git a/src/test/regress/output/tablespace.source b/src/test/regress/output/tablespace.source
index aaedf5f..2443511 100644
--- a/src/test/regress/output/tablespace.source
+++ b/src/test/regress/output/tablespace.source
@@ -14,12 +14,12 @@ DROP TABLESPACE regress_tblspacewith;
-- create a tablespace we can use
CREATE TABLESPACE regress_tblspace LOCATION '@testtablespace@';
-- try setting and resetting some properties for the new tablespace
-ALTER TABLESPACE regress_tblspace SET (random_page_cost = 1.0);
+ALTER TABLESPACE regress_tblspace SET (random_page_cost = 1.0, seq_page_cost = 1.1);
ALTER TABLESPACE regress_tblspace SET (some_nonexistent_parameter = true); -- fail
ERROR: unrecognized parameter "some_nonexistent_parameter"
ALTER TABLESPACE regress_tblspace RESET (random_page_cost = 2.0); -- fail
ERROR: RESET must not include values for parameters
-ALTER TABLESPACE regress_tblspace RESET (random_page_cost, seq_page_cost); -- ok
+ALTER TABLESPACE regress_tblspace RESET (random_page_cost, effective_io_concurrency); -- ok
-- create a schema we can use
CREATE SCHEMA testschema;
-- try a table
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 2fd3f2b..7d369ba 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -109,7 +109,7 @@ test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combo
# NB: temp.sql does a reconnect which transiently uses 2 connections,
# so keep this parallel group to at most 19 tests
# ----------
-test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml
+test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without_oid conversion truncate alter_table sequence polymorphism rowtypes returning largeobject with xml reloptions
# ----------
# Another group of parallel tests
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 76b0de3..277ed7d 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -177,5 +177,6 @@ test: returning
test: largeobject
test: with
test: xml
+test: reloptions
test: event_trigger
test: stats
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index 9a20dd1..3f5af8f 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -2345,3 +2345,10 @@ create table parted_validate_test_1 partition of parted_validate_test for values
alter table parted_validate_test add constraint parted_validate_test_chka check (a > 0) not valid;
alter table parted_validate_test validate constraint parted_validate_test_chka;
drop table parted_validate_test;
+-- test alter column options
+CREATE TABLE tmp(i integer);
+INSERT INTO tmp VALUES (1);
+ALTER TABLE tmp ALTER COLUMN i SET (n_distinct = 1, n_distinct_inherited = 2);
+ALTER TABLE tmp ALTER COLUMN i RESET (n_distinct_inherited);
+ANALYZE tmp;
+DROP TABLE tmp;
diff --git a/src/test/regress/sql/brin.sql b/src/test/regress/sql/brin.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/create_index.sql b/src/test/regress/sql/create_index.sql
index 67470db..a45e8eb 100644
--- a/src/test/regress/sql/create_index.sql
+++ b/src/test/regress/sql/create_index.sql
@@ -682,7 +682,7 @@ CREATE INDEX hash_name_index ON hash_name_heap USING hash (random name_ops);
CREATE INDEX hash_txt_index ON hash_txt_heap USING hash (random text_ops);
-CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_ops);
+CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=60);
CREATE UNLOGGED TABLE unlogged_hash_table (id int4);
CREATE INDEX unlogged_hash_index ON unlogged_hash_table USING hash (id int4_ops);
diff --git a/src/test/regress/sql/gist.sql b/src/test/regress/sql/gist.sql
index 49126fd..f65ce37 100644
--- a/src/test/regress/sql/gist.sql
+++ b/src/test/regress/sql/gist.sql
@@ -17,6 +17,17 @@ select g+100000, point(g*10+1, g*10+1) from generate_series(1, 10000) g;
-- To test vacuum, delete some entries from all over the index.
delete from gist_point_tbl where id % 2 = 1;
+-- Test buffering and fillfactor reloption takes valid values
+create index gist_pointidx2 on gist_point_tbl using gist(p) with (buffering = on, fillfactor=50);
+create index gist_pointidx3 on gist_point_tbl using gist(p) with (buffering = off);
+create index gist_pointidx4 on gist_point_tbl using gist(p) with (buffering = auto);
+--Test buffering and fillfactor for refising invalid values
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (buffering = invalid_value);
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfactor=9);
+create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfactor=101);
+
+
+
-- And also delete some concentration of values. (GiST doesn't currently
-- attempt to delete pages even when they become empty, but if it did, this
-- would exercise it)
diff --git a/src/test/regress/sql/hash_index.sql b/src/test/regress/sql/hash_index.sql
index 9af03d2..f245525 100644
--- a/src/test/regress/sql/hash_index.sql
+++ b/src/test/regress/sql/hash_index.sql
@@ -192,3 +192,8 @@ CREATE TABLE hash_heap_float4 (x float4, y int);
INSERT INTO hash_heap_float4 VALUES (1.1,1);
CREATE INDEX hash_idx ON hash_heap_float4 USING hash (x);
DROP TABLE hash_heap_float4 CASCADE;
+
+-- Check reloptions min max values and that it is not allowed to ALTER
+CREATE INDEX hash_f8_index2 ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=9);
+CREATE INDEX hash_f8_index2 ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=101);
+ALTER INDEX hash_f8_index SET (fillfactor=99);
\ No newline at end of file
diff --git a/src/test/regress/sql/reloptions.sql b/src/test/regress/sql/reloptions.sql
new file mode 100644
index 0000000..0bfec28
--- /dev/null
+++ b/src/test/regress/sql/reloptions.sql
@@ -0,0 +1,153 @@
+
+-- Simple create
+CREATE TABLE reloptions_test(i INT) WITH (fillfactor=30,autovacuum_enabled = false, autovacuum_analyze_scale_factor = 0.2);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+
+-- Test сase insensitive
+CREATE TABLE reloptions_test3(i INT) WITH (FiLlFaCtoR=40);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test3'::regclass;
+
+-- Fail on min/max values check
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=2);
+
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=110);
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor = -10.0);
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor = 110.0);
+
+-- Fail when option and namespase do not exist
+
+CREATE TABLE reloptions_test2(i INT) WITH (not_existing_option=2);
+
+CREATE TABLE reloptions_test2(i INT) WITH (not_existing_namespace.fillfactor=2);
+
+-- Fail while setting unproper value
+
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=30.5);
+
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor='string');
+
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=true);
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled=12);
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled=30.5);
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled='string');
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor='string');
+
+CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor=true);
+
+-- Specifing name only should fail as if there was =ture after it
+CREATE TABLE reloptions_test2(i INT) WITH (fillfactor);
+
+-- Simple ALTER TABLE
+
+ALTER TABLE reloptions_test SET (fillfactor=31, autovacuum_analyze_scale_factor = 0.3);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+
+-- Check that we cat set boolean option to true just by mentioning it
+
+ALTER TABLE reloptions_test SET (autovacuum_enabled, fillfactor=32);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+
+-- Check that RESET works well
+
+ALTER TABLE reloptions_test RESET (fillfactor);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+
+-- Check that RESETting all values make NULL reloptions record in pg_class
+ALTER TABLE reloptions_test RESET (autovacuum_enabled, autovacuum_analyze_scale_factor);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass AND reloptions IS NULL;
+
+-- Check RESET fails on att=value
+
+ALTER TABLE reloptions_test RESET (fillfactor=12);
+
+-- Check oids options is ignored
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test(i INT) WITH (fillfactor=20, oids=true);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+
+-- Now testing toast.* options
+DROP TABLE reloptions_test;
+
+CREATE TABLE reloptions_test (s VARCHAR) WITH (toast.autovacuum_vacuum_cost_delay = 23 );
+
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+
+ALTER TABLE reloptions_test SET (toast.autovacuum_vacuum_cost_delay = 24);
+
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+
+ALTER TABLE reloptions_test RESET (toast.autovacuum_vacuum_cost_delay);
+
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+
+-- Fail on unexisting options in toast namespace
+CREATE TABLE reloptions_test2 (i int) WITH (toast.not_existing_option = 42 );
+
+-- Fail on setting reloption to a table that does not have a TOAST relation
+CREATE TABLE reloptions_test2 (i int) WITH (toast.autovacuum_vacuum_cost_delay = 23 );
+DROP TABLE reloptions_test;
+
+CREATE TABLE reloptions_test(i INT);
+ALTER TABLE reloptions_test SET (toast.autovacuum_vacuum_cost_delay = 23);
+ALTER TABLE reloptions_test RESET (toast.autovacuum_vacuum_cost_delay);
+
+-- autovacuum_analyze_scale_factor and autovacuum_analyze_threshold should be
+-- accepted by heap but rejected by toast (special case)
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test (s VARCHAR) WITH (autovacuum_analyze_scale_factor=1, autovacuum_analyze_threshold=1);
+
+CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analyze_scale_factor=1);
+CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analyze_threshold=1);
+
+-- And now mixed toast + heap
+DROP TABLE reloptions_test;
+
+CREATE TABLE reloptions_test (s VARCHAR) WITH (toast.autovacuum_vacuum_cost_delay = 23, autovacuum_vacuum_cost_delay = 24, fillfactor = 40);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+SELECT reloptions FROM pg_class WHERE oid = (SELECT reltoastrelid FROM pg_class WHERE oid = 'reloptions_test'::regclass);
+
+-- Same FOR CREATE and ALTER INDEX for btree indexes
+
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (fillfactor=30);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx'::regclass;
+
+-- Fail when option and namespase do not exist
+
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_existing_option=2);
+
+CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_existing_option.fillfactor=2);
+
+-- Check ranges
+
+CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfactor=1);
+
+CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfactor=130);
+
+-- Check alter
+
+ALTER INDEX reloptions_test_idx SET (fillfactor=40);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx'::regclass;
+
+-- Check alter on empty relop list
+
+CREATE INDEX reloptions_test_idx3 ON reloptions_test (s);
+
+ALTER INDEX reloptions_test_idx3 SET (fillfactor=40);
+
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test_idx3'::regclass;
+
+
diff --git a/src/test/regress/sql/spgist.sql b/src/test/regress/sql/spgist.sql
index 5896b50..8d26e49 100644
--- a/src/test/regress/sql/spgist.sql
+++ b/src/test/regress/sql/spgist.sql
@@ -5,7 +5,7 @@
-- testing SP-GiST code itself.
create table spgist_point_tbl(id int4, p point);
-create index spgist_point_idx on spgist_point_tbl using spgist(p);
+create index spgist_point_idx on spgist_point_tbl using spgist(p) with (fillfactor = 75);
-- Test vacuum-root operation. It gets invoked when the root is also a leaf,
-- i.e. the index is very small.
@@ -48,3 +48,10 @@ select g, 'baaaaaaaaaaaaaar' || g from generate_series(1, 1000) g;
-- tuple to be moved to another page.
insert into spgist_text_tbl (id, t)
select -g, 'f' || repeat('o', 100-g) || 'surprise' from generate_series(1, 100) g;
+
+-- Check reloptions min, max values
+create index spgist_point_idx2 on spgist_point_tbl using spgist(p) with (fillfactor = 9);
+create index spgist_point_idx2 on spgist_point_tbl using spgist(p) with (fillfactor = 101);
+
+-- ALTER reloption should work
+alter index spgist_point_idx set (fillfactor = 76);
Nikolay Shaplov wrote:
В письме от 25 июня 2017 21:05:49 пользователь Nikolay Shaplov написал:
I've just made sure that patch is still applyable to the current master.
And I am still waiting for reviews :-)
I see that this patch adds a few tests for reloptions, for instance in
bloom. I think we should split this in at least two commits, one that
adds those tests before the functionality change, so that they can be
committed in advance, verify that the buildfarm likes it with the
current code, and verify the coverage.
I also noticed that your patch adds an error message that didn't exist
previously,
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("col%i should not be grater than length", i)));
this seems a bit troublesome, because of the "col%i" thing and also
because of the typo. I wonder if this means you've changed the way
sanity checks work here.
The new checks around toast table creation look like they belong to a
completely independent patch also ... the error message there goes
against message style guidelines also.
--
Álvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
В письме от 3 сентября 2017 11:45:43 пользователь Alvaro Herrera написал:
I've just made sure that patch is still applyable to the current master.
And I am still waiting for reviews :-)
I see that this patch adds a few tests for reloptions, for instance in
bloom. I think we should split this in at least two commits, one that
adds those tests before the functionality change, so that they can be
committed in advance, verify that the buildfarm likes it with the
current code, and verify the coverage.
This sounds as a good idea. I created such patch and started a new thread for
it /messages/by-id/2615372.orqtEn8VGB@x200m (I will
add that thread to commitfest as soon as I understand what test should be left
there)
I also noticed that your patch adds an error message that didn't exist
previously,+ ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("col%i should not be grater than length", i)));this seems a bit troublesome, because of the "col%i" thing
What the problem with col%i ?
and also because of the typo.
Oh... I always had bad marks for all natural languages in school :-( ;-)
I wonder if this means you've changed the way sanity checks work here.
This should not be like this (I hope). I will attentively look at this part of
code again, and explain what exactly I've done. (I did it a year ago and now
need some time to recall)
The new checks around toast table creation look like they belong to a
completely independent patch also ...
This sounds reasonable too. Will do it, it is possible, as far as I remember.
the error message there goes against message style guidelines also.
Oh... these style guidelines... will pay attention to it too...
--
Do code for fun. Can do it for money (Perl & C/C++ ~10h/week)
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
В письме от 3 сентября 2017 11:45:43 пользователь Alvaro Herrera написал:
Yet another git rebase. This patch can be applied to master branch again.
For this patch no review needed now, as I will offer part of it (one that
refuses to set toast reloptions when there is no TOAST table) as a separate
patch.
--
Do code for fun. Can do it for money (Perl & C/C++ ~10h/week)
Attachments:
reloptions8.difftext/x-patch; charset=UTF-8; name=reloptions8.diffDownload
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;
On Mon, Oct 30, 2017 at 9:49 AM, Nikolay Shaplov <dhyan@nataraj.su> wrote:
В письме от 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.
This patch needs a rebase, there are conflicts with HEAD.
-\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".
The basic set of relopt tests have been committed 4b95cc1. Why
insisting on them?
+ if (validate)
+ for (i = 0; i < INDEX_MAX_KEYS; i++)
+ {
The lack of brackets here is not project-like. You should use some for clarity.
There is still no API equivalent for add_int_reloption(),
add_real_reloption(), which is a problem as extension should be
allowed to still use that. Except if I am missing something you could
just have a compatibility API as for example optionCatalogAddItem()
and add_reloption() share really a lot.
}
-
return lockmode;
[...]
#include "utils/typcache.h"
-
+#include "utils/memutils.h"
+#include "utils/guc.h"
Much noise diffs.
LOCKMODE
-AlterTableGetRelOptionsLockLevel(List *defList)
+AlterTableGetRelOptionsLockLevel(Relation rel, List *defList)
{
This could just be static within tablecmds.c.
-#define RelationGetTargetPageFreeSpace(relation, defaultff) \
- (BLCKSZ * (100 - RelationGetFillFactor(relation, defaultff)) / 100)
+#define HeapGetTargetPageFreeSpace(relation, defaultff) \
+ (BLCKSZ * (100 - HeapGetFillFactor(relation, defaultff)) / 100)
-1 for this kind of renames. OK, fillfactor cannot be set for toast
tables but that's not a reason to break current APIs and give more
noise to extension authors.
+/* optionsDefListToRawValues
+ * Converts option values that came from syntax analyzer (DefList) into
+ * Values List.
Should be careful about comment block format.
+ case RELKIND_PARTITIONED_TABLE:
+ catalog = NULL; /* No options for parted table for now */
+ break;
s/parted/partitioned/.
+ amrelopt_catalog_function amrelopt_catalog; /* can be NULL */
Or amoptionlist? Catalog is confusing and refers to system catalogs for example.
- int bufferingModeOffset; /* use buffering build? */
+ int buffering_mode; /* use buffering build? */
I would suggest avoiding unnecessary renames to keep the patch simple.
--
Michael
On Tue, Nov 14, 2017 at 4:44 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:
On Mon, Oct 30, 2017 at 9:49 AM, Nikolay Shaplov <dhyan@nataraj.su> wrote:
В письме от 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.This patch needs a rebase, there are conflicts with HEAD.
Bumping the patch to next CF as no updates have happened for the last two weeks.
There is still no API equivalent for add_int_reloption(),
add_real_reloption(), which is a problem as extension should be
allowed to still use that. Except if I am missing something you could
just have a compatibility API as for example optionCatalogAddItem()
and add_reloption() share really a lot.
This worries me.
--
Michael
On Tue, Nov 28, 2017 at 11:22 AM, Michael Paquier
<michael.paquier@gmail.com> wrote:
On Tue, Nov 14, 2017 at 4:44 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:On Mon, Oct 30, 2017 at 9:49 AM, Nikolay Shaplov <dhyan@nataraj.su> wrote:
В письме от 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.This patch needs a rebase, there are conflicts with HEAD.
Bumping the patch to next CF as no updates have happened for the last two weeks.
Er, returned with feedback.. This will be my last patch for the day.
--
Michael
В письме от 28 ноября 2017 11:23:38 пользователь Michael Paquier написал:
On Tue, Nov 28, 2017 at 11:22 AM, Michael Paquier
<michael.paquier@gmail.com> wrote:
On Tue, Nov 14, 2017 at 4:44 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:
On Mon, Oct 30, 2017 at 9:49 AM, Nikolay Shaplov <dhyan@nataraj.su>
wrote:
В письме от 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.This patch needs a rebase, there are conflicts with HEAD.
Bumping the patch to next CF as no updates have happened for the last two
weeks.Er, returned with feedback.. This will be my last patch for the day.
May be you are right. This patch should be split into smaller patches. I will
do it bit by bit whet I have time. But I see no reason for the big one to
remain on commitfest, as it is not for direct commit.
PS. I will do the rebase soon, this patch overlaps with two recent commits. So
this require very careful rebasing....
--
Do code for fun. Can do it for money (Perl & C/C++ ~10h/week)
В письме от 28 ноября 2017 11:23:38 пользователь Michael Paquier написал:
This patch needs a rebase, there are conflicts with HEAD.
Bumping the patch to next CF as no updates have happened for the last two
weeks.Er, returned with feedback.. This will be my last patch for the day.
Just to have it up to date. This is a rebased version of the patch.
Will cut chunks from it and add them to commitfest, soon.
--
Do code for fun. Can do it for money (Perl & C/C++ ~10h/week)
Attachments:
reloptions8a.difftext/x-patch; charset=UTF-8; name=reloptions8a.diffDownload
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 a7e22b3..3cbef64 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 370cc36..8efbcad 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 cafc8fe..55b81be 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.
*/
@@ -1463,3 +1433,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..6003e76
--- /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, const char *name,
+ const char *desc, LOCKMODE lockmode,
+ option_definition_flags flags, int struct_offset);
+
+static void parse_one_option(option_value * option, const 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(const 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, const char *name, const 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, const char *name, const 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, const char *name,
+ const 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, const char *name, const 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, const char *name, const 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, const char *name, const char *desc,
+ LOCKMODE lockmode, option_definition_flags flags, int struct_offset,
+ const char *default_val, validate_string_option 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, const 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, const 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 aa9c0f1..67a6257 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,938 +20,17 @@
#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 "access/tuptoaster.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
- },
- {
- {
- "toast_tuple_target",
- "Sets the target tuple length at which external columns will be toasted",
- RELOPT_KIND_HEAP,
- ShareUpdateExclusiveLock
- },
- TOAST_TUPLE_TARGET, 128, TOAST_TUPLE_TARGET_MAIN
- },
- {
- {
- "pages_per_range",
- "Number of pages that each page range covers in a BRIN index",
- RELOPT_KIND_BRIN,
- AccessExclusiveLock
- }, 128, 1, 131072
- },
- {
- {
- "gin_pending_list_limit",
- "Maximum size of the pending list for this GIN index, in kilobytes.",
- RELOPT_KIND_GIN,
- AccessExclusiveLock
- },
- -1, 64, MAX_KILOBYTES
- },
- {
- {
- "effective_io_concurrency",
- "Number of simultaneous requests that can be handled efficiently by the disk subsystem.",
- RELOPT_KIND_TABLESPACE,
- ShareUpdateExclusiveLock
- },
-#ifdef USE_PREFETCH
- -1, 0, MAX_IO_CONCURRENCY
-#else
- 0, 0, 0
-#endif
- },
- {
- {
- "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, const char *name, const 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, const char *name, const 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, const char *name, const 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, const char *name, const 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, const char *name, const char *desc, const 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, const 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.
@@ -964,11 +46,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,
@@ -984,550 +67,327 @@ 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)},
- {"toast_tuple_target", RELOPT_TYPE_INT,
- offsetof(StdRdOptions, toast_tuple_target)},
- {"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 + 5); /* 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);
+
+ optionsCatalogAddItemInt(heap_relopt_catalog, "toast_tuple_target",
+ "Sets the target tuple length at which external columns will be toasted",
+ ShareUpdateExclusiveLock,
+ 0, offsetof(HeapOptions, toast_tuple_target),
+ TOAST_TUPLE_TARGET, 128, TOAST_TUPLE_TARGET_MAIN);
+
+ 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;
}
/*
@@ -1537,33 +397,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 d9c6483..8de47ed 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;
@@ -603,35 +604,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
*
@@ -718,3 +690,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 cf4b319..262f4f2 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 2415f00..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(const 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 d8d1c0a..154f36f 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 a50e35d..dc001b2 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 3acef27..5e31684 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -2689,7 +2689,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 9f33e0c..e9fa5a5 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/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c
index c74945a..18ee03e 100644
--- a/src/backend/access/heap/tuptoaster.c
+++ b/src/backend/access/heap/tuptoaster.c
@@ -727,7 +727,7 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
hoff += sizeof(Oid);
hoff = MAXALIGN(hoff);
/* now convert to a limit on the tuple data size */
- maxDataLen = RelationGetToastTupleTarget(rel, TOAST_TUPLE_TARGET) - hoff;
+ maxDataLen = HeapGetToastTupleTarget(rel, TOAST_TUPLE_TARGET) - hoff;
/*
* Look for attributes with attstorage 'x' to compress. Also find large
diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access/nbtree/nbtinsert.c
index 310589d..164fe0a 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 399e6a1..45e6327 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 9b53aa3..7a3b060 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 bd5301f..e6a6e68 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;
@@ -384,8 +384,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 */
@@ -557,15 +557,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
@@ -931,3 +922,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 539ca79..dfb2fd4 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 97091dd..ee94aec 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;
@@ -537,7 +537,7 @@ DefineIndex(Oid relationId,
accessMethodName)));
amcanorder = amRoutine->amcanorder;
- amoptions = amRoutine->amoptions;
+ amoption_catalog = amRoutine->amrelopt_catalog;
pfree(amRoutine);
ReleaseSysCache(tuple);
@@ -551,10 +551,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 d19846d..65c33e9 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -520,7 +520,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
Datum reloptions;
ListCell *listptr;
AttrNumber attnum;
- static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
Oid ofTypeId;
ObjectAddress address;
@@ -606,13 +605,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)
{
@@ -3166,7 +3175,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);
@@ -3208,13 +3218,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)
{
@@ -3426,7 +3438,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:
@@ -4210,7 +4225,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",
@@ -6349,11 +6364,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));
@@ -10439,7 +10454,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 */
@@ -10468,25 +10483,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,
@@ -10500,7 +10542,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;
@@ -10560,6 +10602,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);
@@ -10583,12 +10627,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));
@@ -10616,6 +10663,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 d574e4d..19f4cbf 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 45fca52..05f0e2d 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 2828bbf..1be609c 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 8461da4..82ca4bb 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 48765bb..f9e03a3 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 c2bc3ad..8ced76e 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 1908420..a722f45 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -456,7 +456,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 dc49b6f..4d91551 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 eb1c672..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(const 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..239d192
--- /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_option) (const 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_option 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(const char *namespace, int size_of_bytea,
+ int num_items_expected);
+extern void optionsCatalogAddItemBool(options_catalog * catalog, const char *name,
+ const char *desc, LOCKMODE lockmode, option_definition_flags flags,
+ int struct_offset, bool default_val);
+extern void optionsCatalogAddItemInt(options_catalog * catalog, const char *name,
+ const 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, const char *name,
+ const 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,
+ const char *name, const char *desc, LOCKMODE lockmode, option_definition_flags flags,
+ int struct_offset, const char **allowed_values, int default_val);
+extern void optionsCatalogAddItemString(options_catalog * catalog, const char *name,
+ const char *desc, LOCKMODE lockmode, option_definition_flags flags,
+int struct_offset, const char *default_val, validate_string_option 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, const 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 cd43e3a..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) (const 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, const char *name, const char *desc,
- bool default_val);
-extern void add_int_reloption(bits32 kinds, const char *name, const char *desc,
- int default_val, int min_val, int max_val);
-extern void add_real_reloption(bits32 kinds, const char *name, const char *desc,
- double default_val, double min_val, double max_val);
-extern void add_string_reloption(bits32 kinds, const char *name, const char *desc,
- const char *default_val, validate_string_relopt validator);
-
-extern Datum transformRelOptions(Datum oldOptions, List *defList,
- const 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 68fd6fb..ff4251f 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -273,7 +273,7 @@ typedef struct AutoVacOpts
float8 analyze_scale_factor;
} AutoVacOpts;
-typedef struct StdRdOptions
+typedef struct HeapOptions
{
int32 vl_len_; /* varlena header (do not touch directly!) */
int fillfactor; /* page fill factor in percent (0..100) */
@@ -281,115 +281,124 @@ typedef struct StdRdOptions
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 */
+
/*
- * RelationGetToastTupleTarget
+ * HeapGetToastTupleTarget
* Returns the relation's toast_tuple_target. Note multiple eval of argument!
*/
-#define RelationGetToastTupleTarget(relation, defaulttarg) \
+#define HeapGetToastTupleTarget(relation, defaulttarg) \
((relation)->rd_options ? \
- ((StdRdOptions *) (relation)->rd_options)->toast_tuple_target : (defaulttarg))
+ ((HeapOptions *) (relation)->rd_options)->toast_tuple_target : (defaulttarg))
/*
- * 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
@@ -568,7 +577,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 11f0baa..9a6e6d7 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -2463,7 +2463,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;
В письме от 3 сентября 2017 11:45:43 пользователь Alvaro Herrera написал:
The new checks around toast table creation look like they belong to a
completely independent patch also ... the error message there goes
against message style guidelines also.
Offered toast relation checks as a separate patch:
/messages/by-id/3413542.LWpeoxCNq5@x200m
--
Do code for fun.
В письме от 3 сентября 2017 11:45:43 пользователь Alvaro Herrera написал:
I think we should split this in at least two commits,
Added another part for commit:
/messages/by-id/43332102.S2V5pIjXRx@x200m
This one adds an enum relation option type.
--
Do code for fun.
В письме от 3 сентября 2017 11:45:43 пользователь Alvaro Herrera написал:
I think we should split this in at least two commits,
I've added a third part of this patch to commitfest:
/messages/by-id/2083183.Rn7qOxG4Ov@x200m
To finally commit the rest of the path (the main part of it at least) I need
this, and enum-options patches to be commited. Hope it will be done in march
and I will offer the last part in next commitfest.
--
Do code for fun.