Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtp (Exim 4.84_2) (envelope-from ) id 1cop5T-0002bN-C8 for pgsql-hackers@arkaria.postgresql.org; Fri, 17 Mar 2017 10:26:23 +0000 Received: from localhost ([127.0.0.1] helo=postgresql.org) by malur.postgresql.org with smtp (Exim 4.84_2) (envelope-from ) id 1cop5S-0007Zf-U5 for pgsql-hackers@arkaria.postgresql.org; Fri, 17 Mar 2017 10:26:23 +0000 Received: from makus.postgresql.org ([2001:4800:1501:1::229]) by malur.postgresql.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_CBC_SHA384:256) (Exim 4.84_2) (envelope-from ) id 1cop2R-0004G6-KO for pgsql-hackers@postgresql.org; Fri, 17 Mar 2017 10:23:16 +0000 Received: from enot.nataraj.su ([195.54.209.170]) by makus.postgresql.org with esmtp (Exim 4.84_2) (envelope-from ) id 1cop2G-0001LN-Gm for pgsql-hackers@postgresql.org; Fri, 17 Mar 2017 10:23:14 +0000 Received: from x200m.localnet (unknown [193.107.1.21]) by enot.nataraj.su (Postfix) with ESMTPSA id 9111D511E3C5; Fri, 17 Mar 2017 06:22:30 -0400 (EDT) From: Nikolay Shaplov To: pgsql-hackers@postgresql.org Cc: Alvaro Herrera Subject: Re: [PATCH] Move all am-related reloption code into src/backend/access/[am-name] and get rid of relopt_kind for custom AM Date: Fri, 17 Mar 2017 13:22:25 +0300 Message-ID: <3123308.RiobK7ytJP@x200m> User-Agent: KMail/4.14.1 (Linux/3.16.0-4-amd64; KDE/4.14.2; x86_64; ; ) In-Reply-To: <12845883.2S9x5RULA8@x200m> References: <2146419.veIEZdk4E4@x200m> <1703774.tbaA9tY9qs@x200m> <12845883.2S9x5RULA8@x200m> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="nextPart1556889.GIN69zsn4T" Content-Transfer-Encoding: 7Bit X-Pg-Spam-Score: -1.9 (-) List-Archive: List-Help: List-ID: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: X-Mailing-List: pgsql-hackers Precedence: bulk Sender: pgsql-hackers-owner@postgresql.org This is a multi-part message in MIME format. --nextPart1556889.GIN69zsn4T Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" =D0=92 =D0=BF=D0=B8=D1=81=D1=8C=D0=BC=D0=B5 =D0=BE=D1=82 12 =D0=BC=D0=B0=D1= =80=D1=82=D0=B0 2017 22:29:30 =D0=BF=D0=BE=D0=BB=D1=8C=D0=B7=D0=BE=D0=B2=D0= =B0=D1=82=D0=B5=D0=BB=D1=8C Nikolay Shaplov =D0=BD=D0=B0=D0=BF=D0=B8=D1=81= =D0=B0=D0=BB: Another rebase. > I've rebased the patch again. >=20 > Alvaro, I am waiting for you! ;-) >=20 > > I've rebased the patch, so it can be applied to current master. See > > attachment. > >=20 > > Alvaro in private letter told that he will review the patch some time > > later. So I am waiting. > >=20 > > Also added this patch to commitfest: > > https://commitfest.postgresql.org/13/992/ > >=20 > > > (Please see Attribution notice at the bottom of the letter) > > >=20 > > > 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 > > > https://www.postgresql.org/message-id/4636505.Bu9AKW1Kzc%40nataraj-am= d64 > > > thread. > > >=20 > > > Now I have a patch that is ready > > >=20 > > > 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. > > >=20 > > > Reloptions values in postgres actually exists in four representations: > > > DefList*, TextArray[], Bytea and so called Values. > > >=20 > > > - DefList representation is a list of DefElem, it is the representati= on > > > in > > > which reloptions comes from syntax analyzer, when relation is created= or > > > altered, and also this representation that is needed for pg_dump to d= ump > > > a > > > create statement that creates this relation. > > >=20 > > > - TextArray[] representation is a way, how reloptions are stored in > > > pg_catalog. It is TEXT[] attribute in the pg_catalog table and from t= he > > > poinf o view of postgres code it is Datum array of TEXT Datums, when > > > read > > > into the memory > > >=20 > > > - Bytea (or I will also call it Binary) representation, is a > > > representation with which relation code really works. It has C-struct= ure > > > 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. > > >=20 > > > - Values (or in some cases RawValues) -- internal representation of > > > option > > > parser. Technically it is List of option_value structures (old name f= or > > > it > > > is relopt_value). This representation is used for parsing and validat= ing > > > 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. > > >=20 > > >=20 > > > DefList and TextArray representations are converted in both ways(alwa= ys > > > through Values representation): we need DefList -> TextArray to create > > > or > > > change entry in the pg_catalog, and need TextArray -> DefList for > > > pg_dump > > >=20 > > > Bytea representation is converted from TextArray representation (also > > > through Values representation). When relation code, tries to get cach= ed > > > Bytea representation for certain relation, and none is actually cache= d, > > > then cache code calls extractRelOptions function that fetches entry f= rom > > > pg_catalog for that relation, gets reloptions TextArray[], converts = it > > > into Bytea and cache it. > > >=20 > > >=20 > > > Each reloption has it's own definition. This definitions tells how th= is > > > 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) > > >=20 > > > The set of options definition I would call a options catalog. Before = my > > > patch there was one global options catalog (actually it was divided i= nto > > > parts by data types and has another part for dynamically added option= s, > > > 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 sti= ll > > > kept in reloption.c > > >=20 > > >=20 > > >=20 > > > Now I am ready to tell what actually have been done in this patch: > > >=20 > > > 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 > > >=20 > > > 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 al= so > > > 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) > > >=20 > > > 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 so= me > > > 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 featu= re > > > is > > > used in bloom index. > > >=20 > > > 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. > > >=20 > > > 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 relation= s, > > > 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. > > >=20 > > > 6. I've added some more featues: > > >=20 > > > - Now you can set OPTION_DEFINITION_FLAG_FORBID_ALTER flag in definit= ion > > > 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. > > >=20 > > > - After applying my patch if you try to set toast. option for table t= hat > > > does not have toast, you will get an error. Before postgres will acce= pt > > > 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. > > >=20 > > > - 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 byt= es > > > 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. > > >=20 > > > - Added some more tests that will trigger more options code. Though I= am > > > dreaming about more advanced test system, that will allow to check th= at > > > certain value is received somewhere deep in the code. > > >=20 > > > 7. And now the most controversial change. I've got rid of StdRdOptio= ns > > > 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 ve= ry > > > 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. > > >=20 > > > 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. > > >=20 > > > 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 :-) > > >=20 > > > So, before adding this to commitfest I want somebody to give a general > > > look > > > at it, if the code is OK. > > >=20 > > > Alvaro Herrera, you once said that you can review the patch... > > >=20 > > > The patch is available in the attachment. It can be applied to curre= nt > > > master > > >=20 > > > You can also see latest version on my github > > > https://github.com/dhyannataraj/postgres/tree/reloption_fix > > > at the reloption_fix branch. > > >=20 > > >=20 > > > 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 man= age > > > 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. --=20 Nikolay Shaplov, independent Perl & C/C++ developer. Available for hire.= --nextPart1556889.GIN69zsn4T Content-Disposition: attachment; filename="reloptions6c.diff" Content-Transfer-Encoding: quoted-printable Content-Type: text/x-patch; charset="UTF-8"; name="reloptions6c.diff" 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(IndexVacuumI= nfo *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 @@ =20 PG_FUNCTION_INFO_V1(blhandler); =20 -/* 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; =20 static int32 myRand(void); static void mySrand(uint32 seed); - -/* - * Module initialize function: initialize info about Bloom relation option= s. - * - * Note: keep this in sync with makeDefaultBloomOptions(). - */ -void -_PG_init(void) -{ - int i; - char buf[16]; - - bl_relopt_kind =3D 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 =3D "length"; - bl_relopt_tab[0].opttype =3D RELOPT_TYPE_INT; - bl_relopt_tab[0].offset =3D offsetof(BloomOptions, bloomLength); - - /* Number of bits for each possible index column: col1, col2, ... */ - for (i =3D 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 =3D MemoryContextStrdup(TopMemoryContext, - buf); - bl_relopt_tab[i + 1].opttype =3D RELOPT_TYPE_INT; - bl_relopt_tab[i + 1].offset =3D offsetof(BloomOptions, bitSize[0]) + siz= eof(int) * i; - } -} +static void *blrelopt_catalog(void); +static void blReloptionPostprocess(void *, bool validate); =20 /* * Construct a default set of Bloom options. @@ -129,7 +93,7 @@ blhandler(PG_FUNCTION_ARGS) amroutine->amvacuumcleanup =3D blvacuumcleanup; amroutine->amcanreturn =3D NULL; amroutine->amcostestimate =3D blcostestimate; - amroutine->amoptions =3D bloptions; + amroutine->amrelopt_catalog =3D blrelopt_catalog; amroutine->amproperty =3D NULL; amroutine->amvalidate =3D blvalidate; amroutine->ambeginscan =3D blbeginscan; @@ -146,6 +110,27 @@ blhandler(PG_FUNCTION_ARGS) PG_RETURN_POINTER(amroutine); } =20 +void blReloptionPostprocess(void *data, bool validate) +{ + BloomOptions *opts =3D (BloomOptions*) data; + int i; + + if (validate) + for (i =3D 0; i < INDEX_MAX_KEYS; i++) + { + if (opts->bitSize[i] >=3D 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 =3D (opts->bloomLength + SIGNWORDBITS - 1) / SIGNWORDBI= TS; +} + + /* * Fill BloomState structure for particular index. */ @@ -466,24 +451,38 @@ BloomInitMetapage(Relation index) UnlockReleaseBuffer(metaBuffer); } =20 -/* - * 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) + { =20 - /* Parse the user-given reloptions */ - options =3D parseRelOptions(reloptions, validate, bl_relopt_kind, &numopt= ions); - rdopts =3D 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]; =20 - /* Convert signature length from # of bits to # to words, rounding up */ - rdopts->bloomLength =3D (rdopts->bloomLength + SIGNWORDBITS - 1) / SIGNWO= RDBITS; + bl_relopt_catalog =3D allocateOptionsCatalog(NULL); + bl_relopt_catalog->struct_size =3D sizeof(BloomOptions); + bl_relopt_catalog->postprocess_fun =3D 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),=20 + DEFAULT_BLOOM_LENGTH, 1, MAX_BLOOM_LENGTH); =20 - return (bytea *) rdopts; + /* Number of bits for each possible index column: col1, col2, ... */ + for (i =3D 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/bloo= m.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) =20 +-- reloptions test +DROP INDEX bloomidx; +CREATE INDEX bloomidx ON tst USING bloom (i, t) WITH (length=3D7, col1 =3D= 4); +SELECT reloptions FROM pg_class WHERE oid =3D 'bloomidx'::regclass; + reloptions=20=20=20=20=20 +------------------- + {length=3D7,col1=3D4} +(1 row) + +-- check for min and max values +CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=3D0); +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=3D4097); +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=3D0); +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=3D4096); +ERROR: value 4096 out of bounds for option "col1" +DETAIL: Valid values are between "1" and "4095". +-- check post_validate for colNamvacuumcleanup =3D brinvacuumcleanup; amroutine->amcanreturn =3D NULL; amroutine->amcostestimate =3D brincostestimate; - amroutine->amoptions =3D brinoptions; amroutine->amproperty =3D NULL; amroutine->amvalidate =3D brinvalidate; amroutine->ambeginscan =3D brinbeginscan; @@ -113,6 +113,7 @@ brinhandler(PG_FUNCTION_ARGS) amroutine->amendscan =3D brinendscan; amroutine->ammarkpos =3D NULL; amroutine->amrestrpos =3D NULL; + amroutine->amrelopt_catalog =3D bringetreloptcatalog; amroutine->amestimateparallelscan =3D NULL; amroutine->aminitparallelscan =3D NULL; amroutine->amparallelrescan =3D NULL; @@ -756,36 +757,6 @@ brinvacuumcleanup(IndexVacuumInfo *info, IndexBulkDele= teResult *stats) } =20 /* - * reloptions processor for BRIN indexes - */ -bytea * -brinoptions(Datum reloptions, bool validate) -{ - relopt_value *options; - BrinOptions *rdopts; - int numoptions; - static const relopt_parse_elt tab[] =3D { - {"pages_per_range", RELOPT_TYPE_INT, offsetof(BrinOptions, pagesPerRange= )} - }; - - options =3D parseRelOptions(reloptions, validate, RELOPT_KIND_BRIN, - &numoptions); - - /* if none set, we're done */ - if (numoptions =3D=3D 0) - return NULL; - - rdopts =3D 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, BufferAccessStrate= gy strategy) if (vacuum_fsm) FreeSpaceMapVacuum(idxrel); } + +static options_catalog * brin_relopt_catalog =3D NULL; + +void * +bringetreloptcatalog (void) +{ + if (! brin_relopt_catalog) + { + brin_relopt_catalog =3D allocateOptionsCatalog(NULL); + brin_relopt_catalog->struct_size =3D 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 =3D src/backend/access/common top_builddir =3D ../../../.. include $(top_builddir)/src/Makefile.global =20 -OBJS =3D bufmask.o heaptuple.o indextuple.o printsimple.o printtup.o \ +OBJS =3D bufmask.o heaptuple.o indextuple.o options.o printsimple.o printt= up.o \ reloptions.o scankey.o tupconvert.o tupdesc.o =20 include $(top_srcdir)/src/backend/common.mk diff --git a/src/backend/access/common/options.c b/src/backend/access/commo= n/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 option= s. + * 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=20 + * pg_catalog. + * + * Now options are mainly used for all kind or relations (pg_class.relopti= on) + * 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 optio= ns + * for an object. All parsing/validation/transformation functions should n= ot + * 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. Opt= ions + * 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 (o= r 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 i= t. + * 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 mu= st 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 mad= e 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 lo= ck. + * + * Planner-related parameters can be set with ShareUpdateExclusiveLock bec= ause + * they only affect planning and not the correctness of the execution. Pla= ns + * cannot be changed in mid-flight, so changes here could not easily resul= t 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=3Dvalue). For now postgres uses only toa= st. + * namespace for tables. + */ + +options_catalog * +allocateOptionsCatalog(char *namespace) +{ + MemoryContext oldcxt; + options_catalog *catalog; + + oldcxt =3D MemoryContextSwitchTo(TopMemoryContext); + catalog =3D palloc(sizeof(options_catalog)); + if (namespace) + { + catalog->namespace=3Dpalloc(strlen(namespace) + 1); + strcpy(catalog->namespace, namespace); + } else + catalog->namespace =3D NULL; + + catalog->definitions =3D NULL; + catalog->num =3D 0; + catalog->num_allocated =3D 0; + catalog->struct_size =3D -1; /* Will Assert if not properly changed */ + catalog->postprocess_fun =3D 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 >=3D catalog->num_allocated) + { + MemoryContext oldcxt; + + oldcxt =3D MemoryContextSwitchTo(TopMemoryContext); + + if (catalog->num_allocated =3D=3D 0) + { + catalog->num_allocated =3D 8; + catalog->definitions =3D palloc( + catalog->num_allocated * sizeof(option_definition_basic *)); + } + else + { + catalog->num_allocated *=3D 2; + catalog->definitions =3D repalloc(catalog->definitions, + catalog->num_allocated * sizeof(option_definition_basic *)); + } + MemoryContextSwitchTo(oldcxt); + } + catalog->definitions[catalog->num] =3D newoption; + catalog->definitions[catalog->num + 1] =3D 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 lockmo= de, + option_definition_flags flags, int struct_offset) +{ + MemoryContext oldcxt; + size_t size; + option_definition_basic *newoption; + + oldcxt =3D MemoryContextSwitchTo(TopMemoryContext); + + switch (type) + { + case OPTION_TYPE_BOOL: + size =3D sizeof(option_definition_bool); + break; + case OPTION_TYPE_INT: + size =3D sizeof(option_definition_int); + break; + case OPTION_TYPE_REAL: + size =3D sizeof(option_definition_real); + break; + case OPTION_TYPE_ENUM: + size =3D sizeof(option_definition_enum); + break; + case OPTION_TYPE_STRING: + size =3D sizeof(option_definition_string); + break; + default: + elog(ERROR, "unsupported reloption type %d", type); + return NULL; /* keep compiler quiet */ + } + + newoption =3D palloc(size); + + newoption->name =3D pstrdup(name); + if (desc) + newoption->desc =3D pstrdup(desc); + else + newoption->desc =3D NULL; + newoption->type =3D type; + newoption->lockmode =3D lockmode; + newoption->flags =3D flags; + newoption->struct_offset =3D struct_offset; + + MemoryContextSwitchTo(oldcxt); + + return newoption; +} + +/* + * optionsCatalogAddItemBool + * Add a new boolean option definition to the catalog + */ +void +optionsCatalogAddItemBool(options_catalog * catalog, char *name, char *des= c, + LOCKMODE lockmode, option_definition_flags flags, + int struct_offset, bool default_val) +{ + option_definition_bool *catalog_item; + + catalog_item =3D (option_definition_bool *) + allocateOptionDefinition(OPTION_TYPE_BOOL, name, desc, lockmode, + flags, struct_offset); + + catalog_item->default_val =3D 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 =3D (option_definition_int *) + allocateOptionDefinition(OPTION_TYPE_INT, name, desc, lockmode, + flags, struct_offset); + + catalog_item->default_val =3D default_val; + catalog_item->min =3D min_val; + catalog_item->max =3D 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 *des= c, + 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 =3D (option_definition_real *) + allocateOptionDefinition(OPTION_TYPE_REAL, name, desc, lockmode, + flags, struct_offset); + + catalog_item->default_val =3D default_val; + catalog_item->min =3D min_val; + catalog_item->max =3D 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 it= em of + * an array should be null). This array contains a list of acceptable valu= es + * 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 *des= c, + LOCKMODE lockmode, option_definition_flags flags, int struct_offset, + const char **allowed_values, int default_val) +{ + option_definition_enum *catalog_item; + + catalog_item =3D (option_definition_enum *) + allocateOptionDefinition(OPTION_TYPE_ENUM, name, desc, lockmode, + flags, struct_offset); + + catalog_item->default_val =3D default_val; + catalog_item->allowed_values =3D 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 *d= esc, + 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 =3D (option_definition_string *) + allocateOptionDefinition(OPTION_TYPE_STRING, name, desc, lockmode, + flags, struct_offset); + catalog_item->validate_cb =3D validator; + + if (default_val) + catalog_item->default_val =3D MemoryContextStrdup(TopMemoryContext, + default_val); + else + catalog_item->default_val =3D NULL; + optionCatalogAddItem((option_definition_basic *) catalog_item, catalog); +} + + +/* + * Options transform functions + */ + +/* + * Option values exists in five representations: DefList, TextArray, Value= s and + * Bytea: + * + * DefList: Is a List of DefElem structures, that comes from syntax analyz= er. + * It can be transformed to Values representation for further parsing and + * validating + * + * Values: A List of option_value structures. Is divided into two subclass= es: + * RawValues, when values are already transformed from DefList or TextArra= y, + * 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 catal= og + * and after parsing of the raw value. For ParsedValues content is stored = in + * values structure member, and name can be taken from option definition i= n gen + * structure member. Actually Value list can have both Raw and Parsed val= ues, + * as we do not validate options that came from database, and db option th= at + * does not exist in catalog is just ignored, and kept as RawValues + * + * TextArray: The representation in which options for existing object com= es + * and goes from/to database; for example from pg_class.reloptions. It is a + * plain TEXT[] db object with name=3Dvalue text inside. This representati= on can + * be transformed into Values for further processing, using options catalo= g. + * + * 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 opti= ons + * data from values representation into Bytea, using catalog data, and the= n 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 th= at + * 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 =3D NIL; + + foreach(cell, defList) + { + option_value *option_dst; + DefElem *def =3D (DefElem *) lfirst(cell); + char *value; + + option_dst =3D palloc(sizeof(option_value)); + + if (def->defnamespace) + { + option_dst->namespace =3D palloc(strlen(def->defnamespace) + 1); + strcpy(option_dst->namespace,def->defnamespace); + } else + { + option_dst->namespace =3D NULL; + } + option_dst->raw_name =3D 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=3Dvalue data, as syntax analyzer do not + * prevent it + */ + if (def->arg !=3D NULL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("RESET must not include values for parameters"))); + + option_dst->status =3D OPTION_VALUE_STATUS_FOR_RESET; + } + else + { + /* + * For SET statement we should treat (name) expression as if it + * is actually (name=3Dtrue) so do it here manually. In other cases + * just use value as we should use it + */ + option_dst->status =3D OPTION_VALUE_STATUS_RAW; + if (def->arg !=3D NULL) + value =3D defGetString(def); + else + value =3D "true"; + option_dst->raw_value =3D palloc(strlen(value) + 1); + strcpy(option_dst->raw_value,value); + } + + result =3D 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 =3D NULL; + ListCell *cell; + Datum result; + foreach(cell, options_values) + { + option_value *option =3D (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 =3D option->raw_value; + + Assert(option->status !=3D 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 =3D=3D OPTION_VALUE_STATUS_PARSED) + name =3D option->gen->name; + else + name =3D option->raw_name; + + /* + * Now build "name=3Dvalue" string and append it to the array + */ + len =3D VARHDRSZ + strlen(name) + strlen(value) + 1; + t =3D (text *) palloc(len + 1); + SET_VARSIZE(t, len); + sprintf(VARDATA(t), "%s=3D%s", name, value); + astate =3D accumArrayResult(astate, PointerGetDatum(t), false, + TEXTOID, CurrentMemoryContext); + } + if (astate) + result =3D makeArrayResult(astate, CurrentMemoryContext); + else + result =3D (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 =3D NIL; + + if (PointerIsValid(DatumGetPointer(array_datum))) + { + ArrayType *array =3D DatumGetArrayTypeP(array_datum); + Datum *options; + int noptions; + int i; + + deconstruct_array(array, TEXTOID, -1, false, 'i', + &options, NULL, &noptions); + + for (i =3D 0; i < noptions; i++) + { + option_value * option_dst; + char *text_str =3D VARDATA(options[i]); + int text_len =3D VARSIZE(options[i]) - VARHDRSZ; + int i; + int name_len =3D -1; + char *name; + int raw_value_len; + char *raw_value; + + /* + * Find position of '=3D' sign and treat id as a separator + * between name and value in "name=3Dvalue" item + */ + for(i =3D 0; i < text_len; i =3D i + pg_mblen(text_str)) + { + if (text_str[i] =3D=3D '=3D') + { + name_len =3D i; + break; + } + } + Assert(name_len >=3D 1); /* Just in case */ + + raw_value_len =3D text_len - name_len - 1; + + /* + * Copy name from src + */ + name =3D palloc(name_len + 1); + memcpy(name, text_str, name_len); + name[name_len] =3D '\0'; + + /* + * Copy value from src + */ + raw_value =3D palloc(raw_value_len + 1); + memcpy(raw_value, text_str + name_len + 1, raw_value_len); + raw_value[raw_value_len] =3D '\0'; + + /* + * Create new option_value item + */ + option_dst =3D palloc(sizeof(option_value)); + option_dst->status =3D OPTION_VALUE_STATUS_RAW; + option_dst->raw_name =3D name; + option_dst->raw_value =3D raw_value; + + result =3D 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 o= n. + * + * Both incoming lists will be destroyed while merging + */ +List * +optionsMergeOptionValues(List *old_options, List *new_options) +{ + List * result =3D NIL; + ListCell *old_cell; + ListCell *old_prev; + ListCell *old_next; + ListCell *new_cell; + ListCell *new_prev; + ListCell *new_next; + + old_prev =3D NULL; + + /* + * First try to remove from old options list all values + * that exists in a new options list + */ + for (old_cell =3D list_head(old_options); old_cell; old_cell =3D old_next) + { + bool found; + const char *old_name; + option_value *old_option; + + old_next =3D lnext(old_cell); + old_option =3D (option_value *) lfirst(old_cell); + if (old_option->status =3D=3D OPTION_VALUE_STATUS_PARSED) + old_name =3D old_option->gen->name; + else + old_name =3D old_option->raw_name; + + /* + * Try to find old_name option among new option + */ + found =3D false; + foreach(new_cell, new_options) + { + option_value *new_option; + const char *new_name; + new_option =3D (option_value *) lfirst(new_cell); + if (new_option->status =3D=3D OPTION_VALUE_STATUS_PARSED) + new_name =3D new_option->gen->name; + else + new_name =3D new_option->raw_name; + if (pg_strcasecmp(new_name, old_name) =3D=3D 0) + { + found =3D true; + break; + } + } + /* + * If found, delete old option from the list + */ + if (found) + { + old_options =3D list_delete_cell(old_options,old_cell,old_prev); + } + else + { + old_prev =3D 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 =3D NULL; + for (new_cell =3D list_head(new_options); new_cell; new_cell =3D new_next) + { + option_value *new_option =3D (option_value *) lfirst(new_cell); + new_next =3D lnext(new_cell); + + if (new_option->status =3D=3D OPTION_VALUE_STATUS_FOR_RESET) + new_options =3D list_delete_cell(new_options,new_cell,new_prev); + else + new_prev =3D new_cell; + } + + /* + * Now merge what remained of both lists + */ + result =3D 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 =3D (DefElem *) lfirst(cell); + /* + * Checking namespace only for options that have namespaces. + * Options with no namespaces are always accepted + */ + if (def->defnamespace) + { + bool found =3D false; + int i =3D 0; + while (allowed_namespaces[i]) + { + if (pg_strcasecmp(def->defnamespace, + allowed_namespaces[i]) =3D=3D 0) + { + found =3D 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 =3D NIL; + + foreach(cell, defList) + { + DefElem *def =3D (DefElem *) lfirst(cell); + if ((! namespace && ! def->defnamespace) || + (namespace && def->defnamespace && + pg_strcasecmp(namespace, def->defnamespace) =3D=3D 0)) + { + result =3D lappend(result, def); + } + } + return result; +} + +/* + * optionsTextArrayToDefList + * Convert the text-array format of reloptions into a List of DefElem. + */ +List * +optionsTextArrayToDefList(Datum options) +{ + List *result =3D NIL; + ArrayType *array; + Datum *optiondatums; + int noptions; + int i; + + /* Nothing to do if no options */ + if (!PointerIsValid(DatumGetPointer(options))) + return result; + + array =3D DatumGetArrayTypeP(options); + + deconstruct_array(array, TEXTOID, -1, false, 'i', + &optiondatums, NULL, &noptions); + + for (i =3D 0; i < noptions; i++) + { + char *s; + char *p; + Node *val =3D NULL; + + s =3D TextDatumGetCString(optiondatums[i]); + p =3D strchr(s, '=3D'); + if (p) + { + *p++ =3D '\0'; + val =3D (Node *) makeString(pstrdup(p)); + } + result =3D lappend(result, makeDefElem(pstrdup(s), val, -1)); + } + + return result; +} + +/* + * optionsParseRawValues + * Parses and vlaidates (if proper flag is set) option_values. As a resu= lt + * 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 opt= ion + * should be parsed. + * If validate mode is off, function found an option that do not have prop= er + * option_catalog entry, this option kept unparsed (if some garbage came f= rom + * 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 =3D NIL; + bool *is_set; + int i; + bool validate =3D mode & OPTIONS_PARSE_MODE_VALIDATE; + bool for_alter =3D mode & OPTIONS_PARSE_MODE_FOR_ALTER; + + + is_set =3D palloc0(sizeof(bool)*catalog->num); + foreach(cell, raw_values) + { + option_value *option =3D (option_value *) lfirst(cell); + bool found =3D false; + bool skip =3D false; + + + if (option->status =3D=3D 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 =3D lappend(result, option); + continue; + } + if (validate && option->namespace && (! catalog->namespace || + pg_strcasecmp(catalog->namespace, option->namespace) !=3D 0)) + { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("unrecognized parameter namespace \"%s\"", + option->namespace))); + } + + for (i=3D0;inum;i++) + { + option_definition_basic * definition =3D catalog->definitions[i]; + + if (pg_strcasecmp(option->raw_name, + definition->name) =3D=3D 0) + { + /* + * Skip option with "ignore" flag, as it is processed somewhere + * else. (WITH OIDS special case) + */ + if (definition->flags & OPTION_DEFINITION_FLAG_IGNORE) + { + found =3D true; + skip =3D 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 =3D 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 =3D=3D OPTION_VALUE_STATUS_FOR_RESET) + { + /* + * For RESET options do not need further processing + * so mark it found and stop searching + */ + found =3D true; + break; + } + pfree(option->raw_name); + option->raw_name =3D NULL; + option->gen =3D definition; + parse_one_option(option, NULL, -1, validate); + is_set[i] =3D true; + found =3D 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 =3D 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 =3D option->raw_value; + + switch (option->gen->type) + { + case OPTION_TYPE_BOOL: + { + parsed =3D 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 =3D + (option_definition_int *) option->gen; + + parsed =3D 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 =3D + (option_definition_real *) option->gen; + + parsed =3D 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 =3D + (option_definition_enum *) option->gen; + int i =3D 0; + parsed =3D false; + while (opt_enum->allowed_values[i]) + { + if (pg_strcasecmp(value, opt_enum->allowed_values[i]) =3D=3D 0) + { + option->values.enum_val =3D i; + parsed =3D true; + break; + } + i++; + } + if (! parsed) + { + int length =3D 0; + char *str; + char *ptr; + /* Generating list of allowed values: + * "value1", "value2", ... "valueN" */ + i =3D 0; + while (opt_enum->allowed_values[i]) + { + length +=3D 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 =3D palloc((length - 1 ) * sizeof(char)); + i =3D 0; + ptr =3D str; + while (opt_enum->allowed_values[i]) + { + if (i !=3D 0) + { + ptr[0] =3D ','; + ptr[1] =3D ' '; + ptr +=3D 2; + } + ptr[0]=3D'"'; + ptr++; + sprintf(ptr,"%s",opt_enum->allowed_values[i]); + ptr +=3D strlen(ptr); + ptr[0] =3D '"'; + ptr++; + i++; + } + *ptr=3D'\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 =3D + (option_definition_string *) option->gen; + + option->values.string_val =3D value; + if (validate && optstring->validate_cb) + (optstring->validate_cb) (value); + parsed =3D true; + } + break; + default: + elog(ERROR, "unsupported reloption type %d", option->gen->type); + parsed =3D true; /* quiet compiler */ + break; + } + + if (parsed) + option->status =3D OPTION_VALUE_STATUS_PARSED; + +} + +/* + * optionsAllocateBytea + * Allocates memory for bytea options representation + * + * Function allocates memory for byrea structure of an option, plus adds s= pace + * 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 intern= al + * structure, so it would not be able to copy string values if they are ou= tside + * 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 =3D catalog->struct_size; + + /* Calculate size needed to store all string values for this option */ + for (i=3D0;inum;i++) + { + option_definition_basic *definition =3D catalog->definitions[i]; + bool found =3D false; + option_value *option; + + /* Not interested in non-string options, skipping*/ + if (definition->type !=3D OPTION_TYPE_STRING) + continue; + + /* + * Trying to find option_value that references definition + * catalog entry + */ + foreach(cell, options) + { + option =3D (option_value *) lfirst(cell); + if (option->status =3D=3D OPTION_VALUE_STATUS_PARSED && + pg_strcasecmp(option->gen->name, definition->name) =3D=3D0) + { + found =3D true; + break; + } + } + if (found) + /* If found, it'value will be stored */ + length =3D 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 =3D strlen( + ((option_definition_string*)definition)->default_val) + 1; + else + length =3D 0; + /* Add total length of all string values to basic size */ + size +=3D length; + } + + res =3D 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 alloc= ated + * 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 =3D optionsAllocateBytea(catalog, options); + + /* place for string data starts right after original structure */ + string_values_buffer =3D data + catalog->struct_size; + + for (i =3D 0; i < catalog->num; i++) + { + option_value *found =3D NULL; + ListCell *cell; + char *item_pos; + option_definition_basic *definition =3D catalog->definitions[i]; + + if (definition->flags & OPTION_DEFINITION_FLAG_IGNORE) + continue; + + /* Calculate the position of the item inside the structure */ + item_pos =3D data + definition->struct_offset; + + /* Looking for the corresponding option from options list*/ + foreach(cell, options) + { + option_value *option =3D (option_value *) lfirst(cell); + + if (option->status =3D=3D OPTION_VALUE_STATUS_RAW) + continue; /* raw can come from db. Just ignore them then*/ + Assert(option->status !=3D OPTION_VALUE_STATUS_EMPTY); + + if (pg_strcasecmp(definition->name,option->gen->name) =3D=3D 0) + { + found =3D option; + break; + } + } + /* writing to the proper position either option value or default val */ + switch (definition->type) + { + case OPTION_TYPE_BOOL: + *(bool *) item_pos =3D found ? + found->values.bool_val : + ((option_definition_bool *) definition)->default_val; + break; + case OPTION_TYPE_INT: + *(int *) item_pos =3D found ? + found->values.int_val : + ((option_definition_int *) definition)->default_val; + break; + case OPTION_TYPE_REAL: + *(double *) item_pos =3D found ? + found->values.real_val : + ((option_definition_real *) definition)->default_val; + break; + case OPTION_TYPE_ENUM: + *(int *) item_pos =3D 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 =3D NULL; + if (found) + value =3D found->values.string_val; + else + value =3D ((option_definition_string *) definition) + ->default_val; + *(int *) item_pos =3D value ? + string_values_buffer - data : + OPTION_STRING_VALUE_NOT_SET_OFFSET; + if (value) + { + strcpy(string_values_buffer,value); + string_values_buffer +=3D 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 =3D optionsDefListToRawValues(defList, parse_mode); + new_values =3D 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 =3D optionsTextArrayToRawValues(oldOptions); + merged_values =3D 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 =3D optionsParseRawValues(merged_values, catalog, 0); + } else + { + merged_values =3D new_values; + } + + /* + * If we have postprocess_fun function defined in catalog, then there mig= ht + * 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 =3D optionsValuesToBytea(merged_values, catalog); + catalog->postprocess_fun(data, true); + pfree(data); + } + /* + * Convert options to TextArray format so caller can store them into + * database + */ + result =3D optionsValuesToTextArray(merged_values); + return result; +} + + +/* + * optionsTextArrayToBytea + * A meta-function that transforms options stored as TextArray into bina= ry + * (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 ca= ched + */ +bytea * +optionsTextArrayToBytea(options_catalog *catalog, Datum data) +{ + List *values; + bytea *options; + values =3D optionsTextArrayToRawValues(data); + values =3D optionsParseRawValues(values, catalog, 0); + options =3D 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/co= mmon/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 @@ =20 #include =20 -#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" =20 -/* - * 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 validat= ion - * routine. - * (ii) add a record below (or use add__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 i= t. - * 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 mu= st 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 mad= e 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 lo= ck. - * - * Planner-related parameters can be set with ShareUpdateExclusiveLock bec= ause - * they only affect planning and not the correctness of the execution. Pla= ns - * cannot be changed in mid-flight, so changes here could not easily resul= t 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[] =3D -{ - { - { - "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 o= f 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[] =3D -{ - { - { - "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 wraparoun= d", - 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 wrap= around", - 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 ve= rsions", - 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 th= is relation.", - RELOPT_KIND_HEAP, - ShareUpdateExclusiveLock - }, - -1, 0, 1024 - }, - - /* list terminator */ - {{NULL}} -}; - -static relopt_real realRelOpts[] =3D -{ - { - { - "autovacuum_vacuum_scale_factor", - "Number of tuple updates or deletes prior to vacuum as a fraction of re= ltuples", - 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 frac= tion 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 d= isk 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[] =3D -{ - { - { - "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 =3D NULL; -static bits32 last_assigned_kind =3D RELOPT_KIND_LAST_DEFAULT; - -static int num_custom_options =3D 0; -static relopt_gen **custom_options =3D NULL; -static bool need_initialization =3D 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 len= gth. - */ -static void -initialize_reloptions(void) -{ - int i; - int j; - - j =3D 0; - for (i =3D 0; boolRelOpts[i].gen.name; i++) - { - Assert(DoLockModesConflict(boolRelOpts[i].gen.lockmode, - boolRelOpts[i].gen.lockmode)); - j++; - } - for (i =3D 0; intRelOpts[i].gen.name; i++) - { - Assert(DoLockModesConflict(intRelOpts[i].gen.lockmode, - intRelOpts[i].gen.lockmode)); - j++; - } - for (i =3D 0; realRelOpts[i].gen.name; i++) - { - Assert(DoLockModesConflict(realRelOpts[i].gen.lockmode, - realRelOpts[i].gen.lockmode)); - j++; - } - for (i =3D 0; stringRelOpts[i].gen.name; i++) - { - Assert(DoLockModesConflict(stringRelOpts[i].gen.lockmode, - stringRelOpts[i].gen.lockmode)); - j++; - } - j +=3D num_custom_options; - - if (relOpts) - pfree(relOpts); - relOpts =3D MemoryContextAlloc(TopMemoryContext, - (j + 1) * sizeof(relopt_gen *)); - - j =3D 0; - for (i =3D 0; boolRelOpts[i].gen.name; i++) - { - relOpts[j] =3D &boolRelOpts[i].gen; - relOpts[j]->type =3D RELOPT_TYPE_BOOL; - relOpts[j]->namelen =3D strlen(relOpts[j]->name); - j++; - } - - for (i =3D 0; intRelOpts[i].gen.name; i++) - { - relOpts[j] =3D &intRelOpts[i].gen; - relOpts[j]->type =3D RELOPT_TYPE_INT; - relOpts[j]->namelen =3D strlen(relOpts[j]->name); - j++; - } - - for (i =3D 0; realRelOpts[i].gen.name; i++) - { - relOpts[j] =3D &realRelOpts[i].gen; - relOpts[j]->type =3D RELOPT_TYPE_REAL; - relOpts[j]->namelen =3D strlen(relOpts[j]->name); - j++; - } - - for (i =3D 0; stringRelOpts[i].gen.name; i++) - { - relOpts[j] =3D &stringRelOpts[i].gen; - relOpts[j]->type =3D RELOPT_TYPE_STRING; - relOpts[j]->namelen =3D strlen(relOpts[j]->name); - j++; - } - - for (i =3D 0; i < num_custom_options; i++) - { - relOpts[j] =3D custom_options[i]; - j++; - } - - /* add a list terminator */ - relOpts[j] =3D NULL; - - /* flag the work is complete */ - need_initialization =3D 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 >=3D RELOPT_KIND_MAX) - ereport(ERROR, - (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), - errmsg("user-defined relation parameter types limit exceeded"))); - last_assigned_kind <<=3D 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 =3D 0; - - if (num_custom_options >=3D max_custom_options) - { - MemoryContext oldcxt; - - oldcxt =3D MemoryContextSwitchTo(TopMemoryContext); - - if (max_custom_options =3D=3D 0) - { - max_custom_options =3D 8; - custom_options =3D palloc(max_custom_options * sizeof(relopt_gen *)); - } - else - { - max_custom_options *=3D 2; - custom_options =3D repalloc(custom_options, - max_custom_options * sizeof(relopt_gen *)); - } - MemoryContextSwitchTo(oldcxt); - } - custom_options[num_custom_options++] =3D newoption; - - need_initialization =3D 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 =3D MemoryContextSwitchTo(TopMemoryContext); - - switch (type) - { - case RELOPT_TYPE_BOOL: - size =3D sizeof(relopt_bool); - break; - case RELOPT_TYPE_INT: - size =3D sizeof(relopt_int); - break; - case RELOPT_TYPE_REAL: - size =3D sizeof(relopt_real); - break; - case RELOPT_TYPE_STRING: - size =3D sizeof(relopt_string); - break; - default: - elog(ERROR, "unsupported reloption type %d", type); - return NULL; /* keep compiler quiet */ - } - - newoption =3D palloc(size); - - newoption->name =3D pstrdup(name); - if (desc) - newoption->desc =3D pstrdup(desc); - else - newoption->desc =3D NULL; - newoption->kinds =3D kinds; - newoption->namelen =3D strlen(name); - newoption->type =3D 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 =3D (relopt_bool *) allocate_reloption(kinds, RELOPT_TYPE_BOOL, - name, desc); - newoption->default_val =3D 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 =3D (relopt_int *) allocate_reloption(kinds, RELOPT_TYPE_INT, - name, desc); - newoption->default_val =3D default_val; - newoption->min =3D min_val; - newoption->max =3D 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_va= l, - double min_val, double max_val) -{ - relopt_real *newoption; - - newoption =3D (relopt_real *) allocate_reloption(kinds, RELOPT_TYPE_REAL, - name, desc); - newoption->default_val =3D default_val; - newoption->min =3D min_val; - newoption->max =3D 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_v= al, - validate_string_relopt validator) -{ - relopt_string *newoption; - - /* make sure the validator/default combination is sane */ - if (validator) - (validator) (default_val); - - newoption =3D (relopt_string *) allocate_reloption(kinds, RELOPT_TYPE_STR= ING, - name, desc); - newoption->validate_cb =3D validator; - if (default_val) - { - newoption->default_val =3D MemoryContextStrdup(TopMemoryContext, - default_val); - newoption->default_len =3D strlen(default_val); - newoption->default_isnull =3D false; - } - else - { - newoption->default_val =3D ""; - newoption->default_len =3D 0; - newoption->default_isnull =3D 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 =3D=3D NIL) - return oldOptions; - - /* We build new array using accumArrayResult */ - astate =3D NULL; - - /* Copy any oldOptions that aren't to be replaced */ - if (PointerIsValid(DatumGetPointer(oldOptions))) - { - ArrayType *array =3D DatumGetArrayTypeP(oldOptions); - Datum *oldoptions; - int noldoptions; - int i; - - deconstruct_array(array, TEXTOID, -1, false, 'i', - &oldoptions, NULL, &noldoptions); - - for (i =3D 0; i < noldoptions; i++) - { - char *text_str =3D VARDATA(oldoptions[i]); - int text_len =3D VARSIZE(oldoptions[i]) - VARHDRSZ; - - /* Search for a match in defList */ - foreach(cell, defList) - { - DefElem *def =3D (DefElem *) lfirst(cell); - int kw_len; - - /* ignore if not in the same namespace */ - if (namspace =3D=3D NULL) - { - if (def->defnamespace !=3D NULL) - continue; - } - else if (def->defnamespace =3D=3D NULL) - continue; - else if (pg_strcasecmp(def->defnamespace, namspace) !=3D 0) - continue; - - kw_len =3D strlen(def->defname); - if (text_len > kw_len && text_str[kw_len] =3D=3D '=3D' && - pg_strncasecmp(text_str, def->defname, kw_len) =3D=3D 0) - break; - } - if (!cell) - { - /* No match, so keep old option */ - astate =3D 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=3Dval). (Must do this because the gramm= ar - * doesn't enforce it.) - */ - foreach(cell, defList) - { - DefElem *def =3D (DefElem *) lfirst(cell); - - if (isReset) - { - if (def->arg !=3D 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 !=3D NULL) - { - bool valid =3D false; - int i; - - if (validnsps) - { - for (i =3D 0; validnsps[i]; i++) - { - if (pg_strcasecmp(def->defnamespace, - validnsps[i]) =3D=3D 0) - { - valid =3D 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") =3D=3D 0) - continue; - - /* ignore if not in the same namespace */ - if (namspace =3D=3D NULL) - { - if (def->defnamespace !=3D NULL) - continue; - } - else if (def->defnamespace =3D=3D NULL) - continue; - else if (pg_strcasecmp(def->defnamespace, namspace) !=3D 0) - continue; - - /* - * Flatten the DefElem into a text string like "name=3Darg". If we - * have just "name", assume "name=3Dtrue" is meant. Note: the - * namespace is not output. - */ - if (def->arg !=3D NULL) - value =3D defGetString(def); - else - value =3D "true"; - len =3D VARHDRSZ + strlen(def->defname) + 1 + strlen(value); - /* +1 leaves room for sprintf's trailing null */ - t =3D (text *) palloc(len + 1); - SET_VARSIZE(t, len); - sprintf(VARDATA(t), "%s=3D%s", def->defname, value); - - astate =3D accumArrayResult(astate, PointerGetDatum(t), - false, TEXTOID, - CurrentMemoryContext); - } - } - - if (astate) - result =3D makeArrayResult(astate, CurrentMemoryContext); - else - result =3D (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 =3D NIL; - ArrayType *array; - Datum *optiondatums; - int noptions; - int i; - - /* Nothing to do if no options */ - if (!PointerIsValid(DatumGetPointer(options))) - return result; - - array =3D DatumGetArrayTypeP(options); - - deconstruct_array(array, TEXTOID, -1, false, 'i', - &optiondatums, NULL, &noptions); - - for (i =3D 0; i < noptions; i++) - { - char *s; - char *p; - Node *val =3D NULL; - - s =3D TextDatumGetCString(optiondatums[i]); - p =3D strchr(s, '=3D'); - if (p) - { - *p++ =3D '\0'; - val =3D (Node *) makeString(pstrdup(p)); - } - result =3D lappend(result, makeDefElem(pstrdup(s), val, -1)); - } - - return result; -} +void add_autovacuum_options(options_catalog *catalog, int base_offset, + bool for_toast); =20 /* * 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 =3D NULL; bool isnull; Datum datum; + options_catalog *catalog =3D NULL; Form_pg_class classForm; =20 datum =3D 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 =3D heap_reloptions(classForm->relkind, datum, false); + catalog =3D get_heap_relopt_catalog(); + break; + case RELKIND_TOASTVALUE: + catalog =3D get_toast_relopt_catalog(); break; case RELKIND_VIEW: - options =3D view_reloptions(datum, false); + catalog =3D get_view_relopt_catalog(); break; case RELKIND_INDEX: - options =3D index_reloptions(amoptions, datum, false); + if (catalog_fun) + catalog =3D catalog_fun(); break; case RELKIND_FOREIGN_TABLE: options =3D NULL; @@ -985,528 +89,293 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, break; } =20 + if (catalog) + options =3D optionsTextArrayToBytea(catalog, datum); return options; } =20 + /* - * 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=3Dtrue. The length of t= his - * 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. Catalo= gs + * 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 opt= ions. + * 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 N= ULL - * is returned. + * Function add_autovacuum_options adds to the "catalog" catalog autovacuum + * options definition. In binary mode this options are saved into AutoVacO= pts + * structure, which is located in result bytea chunk with base_offset offs= et. * - * 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 toa= st + * case this options are added to catalog with OPTION_DEFINITION_FLAG_REJE= CT + * flag set, so postgres will reject this toast options from the CREATE or + * ALTER command, but still will save default value in binary representati= on */ -relopt_value * -parseRelOptions(Datum options, bool validate, relopt_kind kind, - int *numrelopts) -{ - relopt_value *reloptions; - int numoptions =3D 0; - int i; - int j; - - if (need_initialization) - initialize_reloptions(); - - /* Build a list of expected options, based on kind */ - - for (i =3D 0; relOpts[i]; i++) - if (relOpts[i]->kinds & kind) - numoptions++; =20 - if (numoptions =3D=3D 0) - { - *numrelopts =3D 0; - return NULL; - } - - reloptions =3D palloc(numoptions * sizeof(relopt_value)); - - for (i =3D 0, j =3D 0; relOpts[i]; i++) - { - if (relOpts[i]->kinds & kind) - { - reloptions[j].gen =3D relOpts[i]; - reloptions[j].isset =3D false; - j++; - } - } - - /* Done if no options */ - if (PointerIsValid(DatumGetPointer(options))) - { - ArrayType *array =3D DatumGetArrayTypeP(options); - Datum *optiondatums; - int noptions; - - deconstruct_array(array, TEXTOID, -1, false, 'i', - &optiondatums, NULL, &noptions); - - for (i =3D 0; i < noptions; i++) - { - char *text_str =3D VARDATA(optiondatums[i]); - int text_len =3D VARSIZE(optiondatums[i]) - VARHDRSZ; - int j; - - /* Search for a match in reloptions */ - for (j =3D 0; j < numoptions; j++) - { - int kw_len =3D reloptions[j].gen->namelen; - - if (text_len > kw_len && text_str[kw_len] =3D=3D '=3D' && - pg_strncasecmp(text_str, reloptions[j].gen->name, - kw_len) =3D=3D 0) - { - parse_one_reloption(&reloptions[j], text_str, text_len, - validate); - break; - } - } - - if (j >=3D numoptions && validate) - { - char *s; - char *p; - - s =3D TextDatumGetCString(optiondatums[i]); - p =3D strchr(s, '=3D'); - if (p) - *p =3D '\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) !=3D DatumGetPointer(options)) - pfree(array); - } - - *numrelopts =3D 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 ver= sions", + 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 wrapa= round", + 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 f= reeze 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 rel= tuples", + 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 fract= ion of reltuples", + ShareUpdateExclusiveLock, + for_toast ? OPTION_DEFINITION_FLAG_REJECT : 0, + base_offset + offsetof(AutoVacOpts, analyze_scale_factor), + -1, 0.0, 100.0); } =20 /* - * 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 =3D false; +static options_catalog * heap_relopt_catalog =3D NULL; =20 - if (option->isset && validate) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("parameter \"%s\" specified more than once", - option->gen->name))); - - value_len =3D text_len - option->gen->namelen - 1; - value =3D (char *) palloc(value_len + 1); - memcpy(value, text_str + option->gen->namelen + 1, value_len); - value[value_len] =3D '\0'; - - switch (option->gen->type) +options_catalog * +get_heap_relopt_catalog(void) +{ + if (! heap_relopt_catalog) { - case RELOPT_TYPE_BOOL: - { - parsed =3D 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 =3D (relopt_int *) option->gen; + heap_relopt_catalog =3D allocateOptionsCatalog(NULL); + heap_relopt_catalog->struct_size =3D sizeof(HeapOptions); =20 - parsed =3D 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 =3D (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); =20 - parsed =3D 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 =3D (relopt_string *) option->gen; + add_autovacuum_options(heap_relopt_catalog, + offsetof(HeapOptions, autovacuum), false); =20 - option->values.string_val =3D value; - nofree =3D true; - if (validate && optstring->validate_cb) - (optstring->validate_cb) (value); - parsed =3D true; - } - break; - default: - elog(ERROR, "unsupported reloption type %d", option->gen->type); - parsed =3D true; /* quiet compiler */ - break; + optionsCatalogAddItemBool(heap_relopt_catalog, "user_catalog_table", + "Declare a table as an additional catalog table, e.g. for the purpose o= f 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 th= is 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 =3D true; - if (!nofree) - pfree(value); + return heap_relopt_catalog; } =20 /* - * Given the result from parseRelOptions, allocate a struct that's of the - * specified base size plus any extra space that's needed for string varia= bles. - * - * "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 =3D base; - int i; =20 - for (i =3D 0; i < numoptions; i++) - if (options[i].gen->type =3D=3D RELOPT_TYPE_STRING) - size +=3D GET_STRING_RELOPTION_LEN(options[i]) + 1; +static options_catalog * toast_relopt_catalog =3D NULL; =20 - 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 =3D basesize; - - for (i =3D 0; i < numoptions; i++) + if (! toast_relopt_catalog) { - int j; - bool found =3D false; - - for (j =3D 0; j < numelems; j++) - { - if (pg_strcasecmp(options[i].gen->name, elems[j].optname) =3D=3D 0) - { - relopt_string *optstring; - char *itempos =3D ((char *) rdopts) + elems[j].offset; - char *string_val; - - switch (options[i].gen->type) - { - case RELOPT_TYPE_BOOL: - *(bool *) itempos =3D options[i].isset ? - options[i].values.bool_val : - ((relopt_bool *) options[i].gen)->default_val; - break; - case RELOPT_TYPE_INT: - *(int *) itempos =3D options[i].isset ? - options[i].values.int_val : - ((relopt_int *) options[i].gen)->default_val; - break; - case RELOPT_TYPE_REAL: - *(double *) itempos =3D options[i].isset ? - options[i].values.real_val : - ((relopt_real *) options[i].gen)->default_val; - break; - case RELOPT_TYPE_STRING: - optstring =3D (relopt_string *) options[i].gen; - if (options[i].isset) - string_val =3D options[i].values.string_val; - else if (!optstring->default_isnull) - string_val =3D optstring->default_val; - else - string_val =3D NULL; + toast_relopt_catalog =3D allocateOptionsCatalog("toast"); + toast_relopt_catalog->struct_size =3D sizeof(ToastOptions); =20 - if (string_val =3D=3D NULL) - *(int *) itempos =3D 0; - else - { - strcpy((char *) rdopts + offset, string_val); - *(int *) itempos =3D offset; - offset +=3D strlen(string_val) + 1; - } - break; - default: - elog(ERROR, "unsupported reloption type %d", - options[i].gen->type); - break; - } - found =3D 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; } =20 - /* - * 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[] =3D { - {"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_thresho= ld)}, - {"autovacuum_analyze_threshold", RELOPT_TYPE_INT, - offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, analyze_thresh= old)}, - {"autovacuum_vacuum_cost_delay", RELOPT_TYPE_INT, - offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, vacuum_cost_de= lay)}, - {"autovacuum_vacuum_cost_limit", RELOPT_TYPE_INT, - offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, vacuum_cost_li= mit)}, - {"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_a= ge)}, - {"autovacuum_multixact_freeze_min_age", RELOPT_TYPE_INT, - offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, multixact_free= ze_min_age)}, - {"autovacuum_multixact_freeze_max_age", RELOPT_TYPE_INT, - offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, multixact_free= ze_max_age)}, - {"autovacuum_multixact_freeze_table_age", RELOPT_TYPE_INT, - offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, multixact_free= ze_table_age)}, - {"log_autovacuum_min_duration", RELOPT_TYPE_INT, - offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, log_min_durati= on)}, - {"autovacuum_vacuum_scale_factor", RELOPT_TYPE_REAL, - offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, vacuum_scale_f= actor)}, - {"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 =3D parseRelOptions(reloptions, validate, kind, &numoptions); - - /* if none set, we're done */ - if (numoptions =3D=3D 0) - return NULL; - - rdopts =3D allocateReloptStruct(sizeof(StdRdOptions), options, numoptions= ); - - fillRelOptions((void *) rdopts, sizeof(StdRdOptions), options, numoptions, - validate, tab, lengthof(tab)); +static options_catalog * view_relopt_catalog =3D NULL; =20 - 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[] =3D { - {"security_barrier", RELOPT_TYPE_BOOL, - offsetof(ViewOptions, security_barrier)}, - {"check_option", RELOPT_TYPE_STRING, - offsetof(ViewOptions, check_option_offset)} - }; - - options =3D parseRelOptions(reloptions, validate, RELOPT_KIND_VIEW, &numo= ptions); + static const char *enum_names[] =3D VIEW_OPTION_CHECK_OPTION_VALUE_NAMES; =20 - /* if none set, we're done */ - if (numoptions =3D=3D 0) - return NULL; - - vopts =3D 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 =3D allocateOptionsCatalog(NULL); + view_relopt_catalog->struct_size =3D sizeof(ViewOptions); =20 -/* - * 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); =20 - switch (relkind) - { - case RELKIND_TOASTVALUE: - rdopts =3D (StdRdOptions *) - default_reloptions(reloptions, validate, RELOPT_KIND_TOAST); - if (rdopts !=3D NULL) - { - /* adjust default-only parameters for TOAST relations */ - rdopts->fillfactor =3D 100; - rdopts->autovacuum.analyze_threshold =3D -1; - rdopts->autovacuum.analyze_scale_factor =3D -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; } =20 - /* - * 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 vali= date) -{ - Assert(amoptions !=3D NULL); - - /* Assume function is strict */ - if (!PointerIsValid(DatumGetPointer(reloptions))) - return NULL; - - return amoptions(reloptions, validate); -} +static options_catalog * attribute_options_catalog =3D NULL; =20 -/* - * 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[] =3D { - {"n_distinct", RELOPT_TYPE_REAL, offsetof(AttributeOpts, n_distinct)}, - {"n_distinct_inherited", RELOPT_TYPE_REAL, offsetof(AttributeOpts, n_dis= tinct_inherited)} - }; - - options =3D parseRelOptions(reloptions, validate, RELOPT_KIND_ATTRIBUTE, - &numoptions); - - /* if none set, we're done */ - if (numoptions =3D=3D 0) - return NULL; - - aopts =3D allocateReloptStruct(sizeof(AttributeOpts), options, numoptions= ); - - fillRelOptions((void *) aopts, sizeof(AttributeOpts), options, numoptions, - validate, tab, lengthof(tab)); + if (! attribute_options_catalog) + { + attribute_options_catalog =3D allocateOptionsCatalog(NULL); + attribute_options_catalog->struct_size =3D sizeof(AttributeOpts); =20 - 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); =20 - 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; } =20 /* - * 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[] =3D { - {"random_page_cost", RELOPT_TYPE_REAL, offsetof(TableSpaceOpts, random_p= age_cost)}, - {"seq_page_cost", RELOPT_TYPE_REAL, offsetof(TableSpaceOpts, seq_page_co= st)}, - {"effective_io_concurrency", RELOPT_TYPE_INT, offsetof(TableSpaceOpts, e= ffective_io_concurrency)} - }; - - options =3D parseRelOptions(reloptions, validate, RELOPT_KIND_TABLESPACE, - &numoptions); - - /* if none set, we're done */ - if (numoptions =3D=3D 0) - return NULL; +static options_catalog * tablespace_options_catalog =3D NULL; =20 - tsopts =3D allocateReloptStruct(sizeof(TableSpaceOpts), options, numoptio= ns); +options_catalog * +get_tablespace_options_catalog(void) +{ + if (! tablespace_options_catalog) + { + tablespace_options_catalog =3D allocateOptionsCatalog(NULL); + tablespace_options_catalog->struct_size =3D sizeof(TableSpaceOpts); =20 - fillRelOptions((void *) tsopts, sizeof(TableSpaceOpts), options, numoptio= ns, - validate, tab, lengthof(tab)); + optionsCatalogAddItemReal(tablespace_options_catalog, + "random_page_cost", + "Sets the planner's estimate of the cost of a nonsequentially fetched d= isk page", + ShareUpdateExclusiveLock, + 0, offsetof(TableSpaceOpts, random_page_cost), -1, 0.0, DBL_MAX); =20 - 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); =20 - 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; } =20 /* @@ -1516,33 +385,51 @@ tablespace_reloptions(Datum reloptions, bool validat= e) * for a longer explanation of how this works. */ LOCKMODE -AlterTableGetRelOptionsLockLevel(List *defList) +AlterTableGetRelOptionsLockLevel(Relation rel, List *defList) { - LOCKMODE lockmode =3D NoLock; - ListCell *cell; + LOCKMODE lockmode =3D NoLock; + ListCell *cell; + options_catalog *catalog =3D NULL; =20 if (defList =3D=3D NIL) return AccessExclusiveLock; =20 - if (need_initialization) - initialize_reloptions(); + switch (rel->rd_rel->relkind) + { + case RELKIND_TOASTVALUE: + catalog =3D get_toast_relopt_catalog(); + break; + case RELKIND_RELATION: + case RELKIND_MATVIEW: + catalog =3D get_heap_relopt_catalog(); + break; + case RELKIND_INDEX: + catalog =3D rel->rd_amroutine->amrelopt_catalog(); + break; + case RELKIND_VIEW: + catalog =3D get_view_relopt_catalog(); + break; + default: + Assert(false); /* can't get here */ + break; + } + + Assert(catalog);/* No catalog - no reloption change. Should not get here = */ =20 foreach(cell, defList) { - DefElem *def =3D (DefElem *) lfirst(cell); - int i; + DefElem *def =3D (DefElem *) lfirst(cell); =20 - for (i =3D 0; relOpts[i]; i++) + int i; + for (i=3D0; i< catalog->num; i++) { - if (pg_strncasecmp(relOpts[i]->name, - def->defname, - relOpts[i]->namelen + 1) =3D=3D 0) - { - if (lockmode < relOpts[i]->lockmode) - lockmode =3D relOpts[i]->lockmode; - } + option_definition_basic *gen =3D catalog->definitions[i]; + + if (pg_strcasecmp(gen->name, + def->defname) =3D=3D 0) + if (lockmode < gen->lockmode) + lockmode =3D gen->lockmode; } } - return lockmode; } diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginu= til.c index d03d59d..50ed21c 100644 --- a/src/backend/access/gin/ginutil.c +++ b/src/backend/access/gin/ginutil.c @@ -16,7 +16,7 @@ =20 #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" =20 /* * GIN handler function: return IndexAmRoutine with access method paramete= rs @@ -60,7 +61,6 @@ ginhandler(PG_FUNCTION_ARGS) amroutine->amvacuumcleanup =3D ginvacuumcleanup; amroutine->amcanreturn =3D NULL; amroutine->amcostestimate =3D gincostestimate; - amroutine->amoptions =3D ginoptions; amroutine->amproperty =3D NULL; amroutine->amvalidate =3D ginvalidate; amroutine->ambeginscan =3D ginbeginscan; @@ -70,6 +70,7 @@ ginhandler(PG_FUNCTION_ARGS) amroutine->amendscan =3D ginendscan; amroutine->ammarkpos =3D NULL; amroutine->amrestrpos =3D NULL; + amroutine->amrelopt_catalog =3D gingetreloptcatalog; amroutine->amestimateparallelscan =3D NULL; amroutine->aminitparallelscan =3D NULL; amroutine->amparallelrescan =3D NULL; @@ -593,35 +594,6 @@ ginExtractEntries(GinState *ginstate, OffsetNumber att= num, return entries; } =20 -bytea * -ginoptions(Datum reloptions, bool validate) -{ - relopt_value *options; - GinOptions *rdopts; - int numoptions; - static const relopt_parse_elt tab[] =3D { - {"fastupdate", RELOPT_TYPE_BOOL, offsetof(GinOptions, useFastUpdate)}, - {"gin_pending_list_limit", RELOPT_TYPE_INT, offsetof(GinOptions, - pendingListCleanupSize)} - }; - - options =3D parseRelOptions(reloptions, validate, RELOPT_KIND_GIN, - &numoptions); - - /* if none set, we're done */ - if (numoptions =3D=3D 0) - return NULL; - - rdopts =3D 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 *sta= ts) =20 END_CRIT_SECTION(); } + + +static options_catalog * gin_relopt_catalog =3D NULL; + +void * +gingetreloptcatalog (void) +{ + if (! gin_relopt_catalog) + { + gin_relopt_catalog =3D allocateOptionsCatalog(NULL); + gin_relopt_catalog->struct_size =3D 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 =3D gistvacuumcleanup; amroutine->amcanreturn =3D gistcanreturn; amroutine->amcostestimate =3D gistcostestimate; - amroutine->amoptions =3D gistoptions; amroutine->amproperty =3D gistproperty; amroutine->amvalidate =3D gistvalidate; amroutine->ambeginscan =3D gistbeginscan; @@ -91,6 +90,7 @@ gisthandler(PG_FUNCTION_ARGS) amroutine->amendscan =3D gistendscan; amroutine->ammarkpos =3D NULL; amroutine->amrestrpos =3D NULL; + amroutine->amrelopt_catalog =3D gistgetreloptcatalog; amroutine->amestimateparallelscan =3D NULL; amroutine->aminitparallelscan =3D NULL; amroutine->amparallelrescan =3D 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 *in= dexInfo) { /* Get buffering mode from the options string */ GiSTOptions *options =3D (GiSTOptions *) index->rd_options; - char *bufferingMode =3D (char *) options + options->bufferingModeOffs= et; - - if (strcmp(bufferingMode, "on") =3D=3D 0) + if (options->buffering_mode =3D=3D GIST_OPTION_BUFFERING_ON) buildstate.bufferingMode =3D GIST_BUFFERING_STATS; - else if (strcmp(bufferingMode, "off") =3D=3D 0) + else if (options->buffering_mode =3D=3D GIST_OPTION_BUFFERING_OFF) buildstate.bufferingMode =3D GIST_BUFFERING_DISABLED; else buildstate.bufferingMode =3D GIST_BUFFERING_AUTO; @@ -234,25 +232,6 @@ gistbuild(Relation heap, Relation index, IndexInfo *in= dexInfo) } =20 /* - * Validator for "buffering" reloption on GiST indexes. Allows "on", "off" - * and "auto" values. - */ -void -gistValidateBufferingOption(char *value) -{ - if (value =3D=3D NULL || - (strcmp(value, "on") !=3D 0 && - strcmp(value, "off") !=3D 0 && - strcmp(value, "auto") !=3D 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/g= istutil.c index cbdaec9..efb5133 100644 --- a/src/backend/access/gist/gistutil.c +++ b/src/backend/access/gist/gistutil.c @@ -18,14 +18,13 @@ =20 #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" =20 - /* * Write itup vector to page, has no control of free space. */ @@ -812,34 +811,6 @@ gistNewBuffer(Relation r) return buffer; } =20 -bytea * -gistoptions(Datum reloptions, bool validate) -{ - relopt_value *options; - GiSTOptions *rdopts; - int numoptions; - static const relopt_parse_elt tab[] =3D { - {"fillfactor", RELOPT_TYPE_INT, offsetof(GiSTOptions, fillfactor)}, - {"buffering", RELOPT_TYPE_STRING, offsetof(GiSTOptions, bufferingModeOff= set)} - }; - - options =3D parseRelOptions(reloptions, validate, RELOPT_KIND_GIST, - &numoptions); - - /* if none set, we're done */ - if (numoptions =3D=3D 0) - return NULL; - - rdopts =3D 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 =3D NULL; + +void * +gistgetreloptcatalog (void) +{ + static const char *enum_names[] =3D GIST_OPTION_BUFFERING_VALUE_NAMES; + if (! gist_relopt_catalog) + { + gist_relopt_catalog =3D allocateOptionsCatalog(NULL); + gist_relopt_catalog->struct_size =3D 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 =3D hashvacuumcleanup; amroutine->amcanreturn =3D NULL; amroutine->amcostestimate =3D hashcostestimate; - amroutine->amoptions =3D hashoptions; amroutine->amproperty =3D NULL; amroutine->amvalidate =3D hashvalidate; amroutine->ambeginscan =3D hashbeginscan; @@ -89,6 +88,7 @@ hashhandler(PG_FUNCTION_ARGS) amroutine->amendscan =3D hashendscan; amroutine->ammarkpos =3D NULL; amroutine->amrestrpos =3D NULL; + amroutine->amrelopt_catalog =3D hashgetreloptcatalog; amroutine->amestimateparallelscan =3D NULL; amroutine->aminitparallelscan =3D NULL; amroutine->amparallelrescan =3D NULL; diff --git a/src/backend/access/hash/hashpage.c b/src/backend/access/hash/h= ashpage.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 =3D sizeof(uint32); item_width =3D MAXALIGN(sizeof(IndexTupleData)) + MAXALIGN(data_width) + sizeof(ItemIdData); /* include the line pointer */ - ffactor =3D RelationGetTargetPageUsage(rel, HASH_DEFAULT_FILLFACTOR) / it= em_width; + ffactor =3D (BLCKSZ * HashGetFillFactor(rel) / 100) / item_width; /* keep to a sane range */ if (ffactor < 10) ffactor =3D 10; diff --git a/src/backend/access/hash/hashutil.c b/src/backend/access/hash/h= ashutil.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" =20 #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) } } =20 -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 =3D NULL; + +void * +hashgetreloptcatalog(void) +{ + if (! hash_relopt_catalog) + { + hash_relopt_catalog =3D allocateOptionsCatalog(NULL); + hash_relopt_catalog->struct_size =3D 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/hea= pam.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 *tupl= es, int ntuples, bool need_cids =3D RelationIsAccessibleInLogicalDecoding(relation); =20 needwal =3D !(options & HEAP_INSERT_SKIP_WAL) && RelationNeedsWAL(relatio= n); - saveFreeSpace =3D RelationGetTargetPageFreeSpace(relation, + if (IsToastRelation(relation)) + saveFreeSpace =3D ToastGetTargetPageFreeSpace(); + else + saveFreeSpace =3D HeapGetTargetPageFreeSpace(relation, HEAP_DEFAULT_FILLFACTOR); =20 /* 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))); =20 /* Compute desired extra freespace due to fillfactor option */ - saveFreeSpace =3D RelationGetTargetPageFreeSpace(relation, - HEAP_DEFAULT_FILLFACTOR); + if (IsToastRelation(relation)) + saveFreeSpace =3D ToastGetTargetPageFreeSpace(); + else + saveFreeSpace =3D HeapGetTargetPageFreeSpace(relation, + HEAP_DEFAULT_FILLFACTOR); =20 if (otherBuffer !=3D InvalidBuffer) otherBlock =3D 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 =3D RelationGetTargetPageFreeSpace(relation, - HEAP_DEFAULT_FILLFACTOR); + + if (IsToastRelation(relation)) + minfree =3D ToastGetTargetPageFreeSpace(); + else + minfree =3D HeapGetTargetPageFreeSpace(relation, + HEAP_DEFAULT_FILLFACTOR); minfree =3D Max(minfree, BLCKSZ / 10); =20 if (PageIsFull(page) || PageGetHeapFreeSpace(page) < minfree) diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/hea= p/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))); =20 /* Compute desired extra freespace due to fillfactor option */ - saveFreeSpace =3D RelationGetTargetPageFreeSpace(state->rs_new_rel, + if (IsToastRelation(state->rs_new_rel)) + saveFreeSpace =3D ToastGetTargetPageFreeSpace(); + else + saveFreeSpace =3D HeapGetTargetPageFreeSpace(state->rs_new_rel, HEAP_DEFAULT_FILLFACTOR); =20 /* 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/nbt= ree/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 =3D P_RIGHTMOST(opaque); state.have_split =3D false; if (state.is_leaf) - state.fillfactor =3D RelationGetFillFactor(rel, - BTREE_DEFAULT_FILLFACTOR); + state.fillfactor =3D BTGetFillFactor(rel); else state.fillfactor =3D BTREE_NONLEAF_FILLFACTOR; state.newitemonleft =3D 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" =20 - /* Working state for btbuild and its callback */ typedef struct { @@ -116,8 +116,7 @@ static void btvacuumscan(IndexVacuumInfo *info, IndexBu= lkDeleteResult *stats, BTCycleId cycleid); static void btvacuumpage(BTVacState *vstate, BlockNumber blkno, BlockNumber orig_blkno); - - +void * btgetreloptcatalog (void); /* * Btree handler function: return IndexAmRoutine with access method parame= ters * and callbacks. @@ -150,7 +149,6 @@ bthandler(PG_FUNCTION_ARGS) amroutine->amvacuumcleanup =3D btvacuumcleanup; amroutine->amcanreturn =3D btcanreturn; amroutine->amcostestimate =3D btcostestimate; - amroutine->amoptions =3D btoptions; amroutine->amproperty =3D btproperty; amroutine->amvalidate =3D btvalidate; amroutine->ambeginscan =3D btbeginscan; @@ -160,6 +158,7 @@ bthandler(PG_FUNCTION_ARGS) amroutine->amendscan =3D btendscan; amroutine->ammarkpos =3D btmarkpos; amroutine->amrestrpos =3D btrestrpos; + amroutine->amrelopt_catalog =3D btgetreloptcatalog; amroutine->amestimateparallelscan =3D btestimateparallelscan; amroutine->aminitparallelscan =3D btinitparallelscan; amroutine->amparallelrescan =3D btparallelrescan; @@ -1391,3 +1390,26 @@ btcanreturn(Relation index, int attno) { return true; } + + +static options_catalog * bt_relopt_catalog =3D NULL; + +void * +btgetreloptcatalog (void) +{ + if (! bt_relopt_catalog) + { + bt_relopt_catalog =3D allocateOptionsCatalog(NULL); + bt_relopt_catalog->struct_size =3D 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/nbtre= e/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 =3D (BLCKSZ * (100 - BTREE_NONLEAF_FILLFACTOR) / 100); else - state->btps_full =3D RelationGetTargetPageFreeSpace(wstate->index, - BTREE_DEFAULT_FILLFACTOR); + state->btps_full =3D (BLCKSZ * (100 - BTGetFillFactor(wstate->index)) /= 100); +=09 /* no parent level, yet */ state->btps_next =3D NULL; =20 diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtr= ee/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 =20 #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); } =20 -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/spgi= st/spgutils.c index e57ac49..b24178c 100644 --- a/src/backend/access/spgist/spgutils.c +++ b/src/backend/access/spgist/spgutils.c @@ -15,7 +15,7 @@ =20 #include "postgres.h" =20 -#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 =3D spgvacuumcleanup; amroutine->amcanreturn =3D spgcanreturn; amroutine->amcostestimate =3D spgcostestimate; - amroutine->amoptions =3D spgoptions; amroutine->amproperty =3D NULL; amroutine->amvalidate =3D spgvalidate; amroutine->ambeginscan =3D spgbeginscan; @@ -69,6 +68,7 @@ spghandler(PG_FUNCTION_ARGS) amroutine->amendscan =3D spgendscan; amroutine->ammarkpos =3D NULL; amroutine->amrestrpos =3D NULL; + amroutine->amrelopt_catalog =3D spggetreloptcatalog; amroutine->amestimateparallelscan =3D NULL; amroutine->aminitparallelscan =3D NULL; amroutine->amparallelrescan =3D NULL; @@ -372,8 +372,8 @@ SpGistGetBuffer(Relation index, int flags, int needSpac= e, bool *isNew) * related to the ones already on it. But fillfactor mustn't cause an * error for requests that would otherwise be legal. */ - needSpace +=3D RelationGetTargetPageFreeSpace(index, - SPGIST_DEFAULT_FILLFACTOR); + needSpace +=3D (BLCKSZ * (100 - SpGistGetFillFactor(index)) / 100); + needSpace =3D Min(needSpace, SPGIST_PAGE_CAPACITY); =20 /* Get the cache entry for this flags setting */ @@ -537,15 +537,6 @@ SpGistInitMetapage(Page page) } =20 /* - * 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, It= em item, Size size, =20 return offnum; } + + + +static options_catalog * spgist_relopt_catalog =3D NULL; + +void * +spggetreloptcatalog (void) +{ + if (! spgist_relopt_catalog) + { + spgist_relopt_catalog =3D allocateOptionsCatalog(NULL); + spgist_relopt_catalog->struct_size =3D 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 =3D InvalidOid; =20 -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 toastIndexO= id, Datum reloptions, LOCKMODE lockmode, bool check); @@ -67,23 +67,26 @@ NewHeapCreateToastTable(Oid relOid, Datum reloptions, L= OCKMODE lockmode) CheckAndCreateToastTable(relOid, reloptions, lockmode, false); } =20 -void +bool NewRelationCreateToastTable(Oid relOid, Datum reloptions) { - CheckAndCreateToastTable(relOid, reloptions, AccessExclusiveLock, false); + return CheckAndCreateToastTable(relOid, reloptions, + AccessExclusiveLock, false); } =20 -static void +static bool=20 CheckAndCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmode, = bool check) { Relation rel; + bool success; =20 rel =3D heap_open(relOid, lockmode); =20 /* create_toast_table does all the work */ - (void) create_toast_table(rel, InvalidOid, InvalidOid, reloptions, lockmo= de, check); + success =3D create_toast_table(rel, InvalidOid, InvalidOid, reloptions, l= ockmode, check); =20 heap_close(rel, NoLock); + return success; } =20 /* diff --git a/src/backend/commands/createas.c b/src/backend/commands/createa= s.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[] =3D HEAP_RELOPT_NAMESPACES; ObjectAddress intoRelationAddr; + List *toastDefList; + bool toast_created; =20 /* This code supports both CREATE TABLE AS and CREATE MATERIALIZED VIEW */ is_matview =3D (into->viewQuery !=3D NULL); @@ -122,15 +124,20 @@ create_ctas_internal(List *attrList, IntoClause *into) CommandCounterIncrement(); =20 /* parse and validate reloptions for the toast table */ - toast_options =3D transformRelOptions((Datum) 0, - create->options, - "toast", - validnsps, - true, false); =20 - (void) heap_reloptions(RELKIND_TOASTVALUE, toast_options, true); =20 - NewRelationCreateToastTable(intoRelationAddr.objectId, toast_options); + optionsDefListValdateNamespaces(create->options, validnsps); + toastDefList =3D optionsDefListFilterNamespaces(create->options, "toast"); + + toast_options =3D transformOptions(get_toast_relopt_catalog(), (Datum) 0, + toastDefList, 0); + + toast_created =3D 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.* st= orage parameters"))); =20 /* Create the "view" part of a materialized view. */ if (is_matview) diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/fore= igncmds.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 =3D untransformRelOptions(oldOptions); + List *resultOptions =3D optionsTextArrayToDefList(oldOptions); ListCell *optcell; Datum result; =20 diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexc= mds.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))); =20 amcanorder =3D amRoutine->amcanorder; - amoptions =3D amRoutine->amoptions; + amoption_catalog =3D amRoutine->amrelopt_catalog; =20 pfree(amRoutine); ReleaseSysCache(tuple); @@ -537,10 +537,18 @@ DefineIndex(Oid relationId, /* * Parse AM-specific options, convert to text array form, validate. */ - reloptions =3D transformRelOptions((Datum) 0, stmt->options, - NULL, NULL, false, false); - - (void) index_reloptions(amoptions, reloptions, true); + if (amoption_catalog) + { + reloptions =3D 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))); + } =20 /* * Prepare arguments for index_create, primarily an IndexInfo structure. diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablec= mds.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 owne= rId, Datum reloptions; ListCell *listptr; AttrNumber attnum; - static char *validnsps[] =3D HEAP_RELOPT_NAMESPACES; Oid ofTypeId; ObjectAddress address; =20 @@ -585,13 +584,23 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ow= nerId, /* * Parse and validate reloptions, if any. */ - reloptions =3D transformRelOptions((Datum) 0, stmt->options, NULL, validn= sps, - true, false); - if (relkind =3D=3D RELKIND_VIEW) - (void) view_reloptions(reloptions, true); + { + reloptions =3D 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[] =3D HEAP_RELOPT_NAMESPACES; + List * heapDefList; + + optionsDefListValdateNamespaces(stmt->options, namespaces); + heapDefList =3D optionsDefListFilterNamespaces(stmt->options, NULL); + reloptions =3D transformOptions(get_heap_relopt_catalog(), + (Datum) 0, heapDefList, 0); + } =20 if (stmt->ofTypename) { @@ -3073,7 +3082,8 @@ void AlterTableInternal(Oid relid, List *cmds, bool recurse) { Relation rel; - LOCKMODE lockmode =3D AlterTableGetLockLevel(cmds); + + LOCKMODE lockmode =3D AlterTableGetLockLevel(relid, cmds); =20 rel =3D relation_open(relid, lockmode); =20 @@ -3115,13 +3125,15 @@ AlterTableInternal(Oid relid, List *cmds, bool recu= rse) * 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 =3D ShareUpdateExclusiveLock; + Relation rel; =20 foreach(lcmd, cmds) { @@ -3330,7 +3342,10 @@ AlterTableGetLockLevel(List *cmds) * getTables() */ case AT_ResetRelOptions: /* Uses MVCC in getIndexes() and * getTables() */ - cmd_lockmode =3D AlterTableGetRelOptionsLockLevel((List *) cmd->def); + rel =3D relation_open(relid, NoLock); + cmd_lockmode =3D AlterTableGetRelOptionsLockLevel(rel, + castNode(List, cmd->def)); + relation_close(rel,NoLock); break; =20 case AT_AttachPartition: @@ -4087,7 +4102,7 @@ ATRewriteTables(AlterTableStmt *parsetree, List **wqu= eue, LOCKMODE lockmode) errmsg("cannot rewrite system relation \"%s\"", RelationGetRelationName(OldHeap)))); =20 - 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 =3D SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions, &isnull); - newOptions =3D transformRelOptions(isnull ? (Datum) 0 : datum, - castNode(List, options), NULL, NULL, - false, isReset); - /* Validate new options */ - (void) attribute_reloptions(newOptions, true); + + newOptions =3D transformOptions(get_attribute_options_catalog(), + isnull ? (Datum) 0 : datum, + castNode(List, options), OPTIONS_PARSE_MODE_FOR_ALTER | + (isReset ? OPTIONS_PARSE_MODE_FOR_RESET : 0)); =20 /* Build new tuple. */ memset(repl_null, false, sizeof(repl_null)); @@ -9929,7 +9944,7 @@ ATExecSetRelOptions(Relation rel, List *defList, Alte= rTableType operation, Datum repl_val[Natts_pg_class]; bool repl_null[Natts_pg_class]; bool repl_repl[Natts_pg_class]; - static char *validnsps[] =3D HEAP_RELOPT_NAMESPACES; + options_parse_mode parse_mode; =20 if (defList =3D=3D NIL && operation !=3D AT_ReplaceRelOptions) return; /* nothing to do */ @@ -9958,25 +9973,52 @@ ATExecSetRelOptions(Relation rel, List *defList, Al= terTableType operation, &isnull); } =20 - /* Generate new proposed reloptions (text array) */ - newOptions =3D transformRelOptions(isnull ? (Datum) 0 : datum, - defList, NULL, validnsps, false, - operation =3D=3D AT_ResetRelOptions); - /* Validate */ + parse_mode =3D OPTIONS_PARSE_MODE_FOR_ALTER; + if (operation =3D=3D AT_ResetRelOptions) + parse_mode |=3D 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[] =3D HEAP_RELOPT_NAMESPACES; + List * heapDefList; + + optionsDefListValdateNamespaces(defList, namespaces); + heapDefList =3D optionsDefListFilterNamespaces( + defList, NULL); + newOptions =3D transformOptions(get_heap_relopt_catalog(), + isnull ? (Datum) 0 : datum, + heapDefList, parse_mode); + } break; case RELKIND_VIEW: - (void) view_reloptions(newOptions, true); + { + newOptions =3D 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 =3D 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, Alt= erTableType operation, if (rel->rd_rel->relkind =3D=3D RELKIND_VIEW) { Query *view_query =3D get_view_query(rel); - List *view_options =3D untransformRelOptions(newOptions); + List *view_options =3D optionsTextArrayToDefList(newOptions); ListCell *cell; bool check_option =3D false; =20 @@ -10050,6 +10092,8 @@ ATExecSetRelOptions(Relation rel, List *defList, Al= terTableType operation, { Relation toastrel; Oid toastid =3D rel->rd_rel->reltoastrelid; + List *toastDefList; + options_parse_mode parse_mode; =20 toastrel =3D heap_open(toastid, lockmode); =20 @@ -10073,12 +10117,15 @@ ATExecSetRelOptions(Relation rel, List *defList, = AlterTableType operation, datum =3D SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions, &isnull); } + parse_mode =3D OPTIONS_PARSE_MODE_FOR_ALTER; + if (operation =3D=3D AT_ResetRelOptions) + parse_mode |=3D OPTIONS_PARSE_MODE_FOR_RESET; =20 - newOptions =3D transformRelOptions(isnull ? (Datum) 0 : datum, - defList, "toast", validnsps, false, - operation =3D=3D AT_ResetRelOptions); + toastDefList =3D optionsDefListFilterNamespaces(defList, "toast"); =20 - (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true); + newOptions =3D transformOptions(get_toast_relopt_catalog(), + isnull ? (Datum) 0 : datum, + toastDefList, parse_mode); =20 memset(repl_val, 0, sizeof(repl_val)); memset(repl_null, false, sizeof(repl_null)); @@ -10106,6 +10153,17 @@ ATExecSetRelOptions(Relation rel, List *defList, A= lterTableType operation, =20 heap_close(toastrel, NoLock); } + else + { + List *toastDefList; + toastDefList =3D optionsDefListFilterNamespaces(defList, "toast"); + if (toastDefList) + { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("TOAST relation for this table does not exist. Can't change toa= st.* storage parameters"))); + } + } =20 heap_close(pgclass, RowExclusiveLock); } diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/table= space.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] =3D true; =20 /* Generate new proposed spcoptions (text array) */ - newOptions =3D transformRelOptions((Datum) 0, - stmt->options, - NULL, NULL, false, false); - (void) tablespace_reloptions(newOptions, true); + + newOptions =3D transformOptions( + get_tablespace_options_catalog(), + (Datum) 0, stmt->options, 0); + if (newOptions !=3D (Datum) 0) values[Anum_pg_tablespace_spcoptions - 1] =3D newOptions; else @@ -1023,10 +1024,13 @@ AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *= stmt) /* Generate new proposed spcoptions (text array) */ datum =3D heap_getattr(tup, Anum_pg_tablespace_spcoptions, RelationGetDescr(rel), &isnull); - newOptions =3D transformRelOptions(isnull ? (Datum) 0 : datum, - stmt->options, NULL, NULL, false, - stmt->isReset); - (void) tablespace_reloptions(newOptions, true); + + + newOptions =3D transformOptions(get_tablespace_options_catalog(), + isnull ? (Datum) 0 : datum, + stmt->options, + OPTIONS_PARSE_MODE_FOR_ALTER | + (stmt->isReset? OPTIONS_PARSE_MODE_FOR_RESET : 0)); =20 /* 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 =3D NIL; else - fdw->options =3D untransformRelOptions(datum); + fdw->options =3D optionsTextArrayToDefList(datum); =20 ReleaseSysCache(tp); =20 @@ -133,7 +133,7 @@ GetForeignServer(Oid serverid) if (isnull) server->options =3D NIL; else - server->options =3D untransformRelOptions(datum); + server->options =3D optionsTextArrayToDefList(datum); =20 ReleaseSysCache(tp); =20 @@ -201,7 +201,7 @@ GetUserMapping(Oid userid, Oid serverid) if (isnull) um->options =3D NIL; else - um->options =3D untransformRelOptions(datum); + um->options =3D optionsTextArrayToDefList(datum); =20 ReleaseSysCache(tp); =20 @@ -238,7 +238,7 @@ GetForeignTable(Oid relid) if (isnull) ft->options =3D NIL; else - ft->options =3D untransformRelOptions(datum); + ft->options =3D optionsTextArrayToDefList(datum); =20 ReleaseSysCache(tp); =20 @@ -271,7 +271,7 @@ GetForeignColumnOptions(Oid relid, AttrNumber attnum) if (isnull) options =3D NIL; else - options =3D untransformRelOptions(datum); + options =3D optionsTextArrayToDefList(datum); =20 ReleaseSysCache(tp); =20 @@ -540,7 +540,7 @@ pg_options_to_table(PG_FUNCTION_ARGS) Datum array =3D PG_GETARG_DATUM(0); =20 deflist_to_tuplestore((ReturnSetInfo *) fcinfo->resultinfo, - untransformRelOptions(array)); + optionsTextArrayToDefList(array)); =20 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 =3D untransformRelOptions(PG_GETARG_DATUM(0)); + List *options_list =3D optionsTextArrayToDefList(PG_GETARG_DATUM(0)); Oid catalog =3D PG_GETARG_OID(1); =20 ListCell *cell; diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/u= til/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 relationObject= Id, bool inhparent, &rel->pages, &rel->tuples, &rel->allvisfrac); =20 /* Retrieve the parallel_workers reloption, or -1 if not set. */ - rel->rel_parallel_workers =3D RelationGetParallelWorkers(relation, -1); + rel->rel_parallel_workers =3D HeapGetParallelWorkers(relation, -1); =20 /* * 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_c= lause.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)))); =20 /* 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 catalo= g 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, Relat= ion source_idx, datum =3D SysCacheGetAttr(RELOID, ht_idxrel, Anum_pg_class_reloptions, &isnull); if (!isnull) - index->options =3D untransformRelOptions(datum); + index->options =3D optionsTextArrayToDefList(datum); =20 /* If it's a partial index, decompile and append the predicate */ datum =3D SysCacheGetAttr(INDEXRELID, ht_idx, diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/a= utovacuum.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_clas= s_desc) { bytea *relopts; AutoVacOpts *av; + AutoVacOpts *src; =20 Assert(((Form_pg_class) GETSTRUCT(tup))->relkind =3D=3D RELKIND_RELATION = || ((Form_pg_class) GETSTRUCT(tup))->relkind =3D=3D RELKIND_MATVIEW || @@ -2518,8 +2519,13 @@ extract_autovac_opts(HeapTuple tup, TupleDesc pg_cla= ss_desc) if (relopts =3D=3D NULL) return NULL; =20 + if (((Form_pg_class) GETSTRUCT(tup))->relkind =3D=3D RELKIND_TOASTVALUE) + src =3D &(((ToastOptions *) relopts)->autovacuum); + else + src =3D &(((HeapOptions *) relopts)->autovacuum); + av =3D palloc(sizeof(AutoVacOpts)); - memcpy(av, &(((StdRdOptions *) relopts)->autovacuum), sizeof(AutoVacOpts)= ); + memcpy(av, src, sizeof(AutoVacOpts)); pfree(relopts); =20 return av; diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rew= riteHandler.c index 354e5d0..748ba3a 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -1488,7 +1488,7 @@ ApplyRetrieveRule(Query *parsetree, =20 rte->rtekind =3D RTE_SUBQUERY; rte->relid =3D InvalidOid; - rte->security_barrier =3D RelationIsSecurityView(relation); + rte->security_barrier =3D ViewIsSecurityView(relation); rte->subquery =3D rule_action; rte->inh =3D false; /* must not be set for a subquery */ =20 @@ -2989,7 +2989,7 @@ rewriteTargetView(Query *parsetree, Relation view) =20 ChangeVarNodes(viewqual, base_rt_index, new_rt_index, 0); =20 - 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 !=3D CMD_DELETE) { - bool has_wco =3D RelationHasCheckOption(view); - bool cascaded =3D RelationHasCascadedCheckOption(view); + + bool has_wco =3D ViewHasCheckOption(view); + bool cascaded =3D ViewHasCascadedCheckOption(view); =20 /* * 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[] =3D HEAP_RELOPT_NAMESPACES; + List *toastDefList; + bool success; =20 /* Create the table itself */ address =3D DefineRelation((CreateStmt *) stmt, @@ -1016,18 +1018,24 @@ ProcessUtilitySlow(ParseState *pstate, * parse and validate reloptions for the toast * table */ - toast_options =3D transformRelOptions((Datum) 0, - ((CreateStmt *) stmt)->options, - "toast", - validnsps, - true, - false); - (void) heap_reloptions(RELKIND_TOASTVALUE, - toast_options, - true); =20 - NewRelationCreateToastTable(address.objectId, - toast_options); + optionsDefListValdateNamespaces( + ((CreateStmt *) stmt)->options, + validnsps); + + toastDefList =3D optionsDefListFilterNamespaces( + ((CreateStmt *) stmt)->options, "toast"); + + toast_options =3D transformOptions( + get_toast_relopt_catalog(), (Datum) 0, + toastDefList, 0); + + success =3D 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 =3D AlterTableGetLockLevel(atstmt->cmds); - relid =3D AlterTableLookupRelation(atstmt, lockmode); + + relid =3D AlterTableLookupRelation(atstmt, NoLock); + if (OidIsValid(relid)) + { + lockmode =3D AlterTableGetLockLevel(relid, atstmt->cmds); + relid =3D AlterTableLookupRelation(atstmt, lockmode); + } =20 if (OidIsValid(relid)) { diff --git a/src/backend/utils/cache/attoptcache.c b/src/backend/utils/cach= e/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 =3D NULL; else { - bytea *bytea_opts =3D attribute_reloptions(datum, false); + bytea *bytea_opts; + + bytea_opts =3D optionsTextArrayToBytea( + get_attribute_options_catalog(), datum); =20 opts =3D MemoryContextAlloc(CacheMemoryContext, VARSIZE(bytea_opts)); diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/r= elcache.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 tu= ple) options =3D extractRelOptions(tuple, GetPgClassDescriptor(), relation->rd_rel->relkind =3D=3D RELKIND_INDEX ? - relation->rd_amroutine->amoptions : NULL); + relation->rd_amroutine->amrelopt_catalog : NULL + ); =20 /* * Copy parsed data into CacheMemoryContext. To guard against the diff --git a/src/backend/utils/cache/spccache.c b/src/backend/utils/cache/s= pccache.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 =3D NULL; else { - bytea *bytea_opts =3D tablespace_reloptions(datum, false); + bytea *bytea_opts; + bytea_opts =3D optionsTextArrayToBytea( + get_tablespace_options_catalog(), datum); =20 opts =3D 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 =3D 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 PlannerI= nfo *root, double *indexCorrelation, double *indexPages); =20 -/* 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); =20 +/* 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 */ =20 /* 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; =20 #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_i= nternal.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(IndexVacuu= mInfo *info, void *callback_state); extern IndexBulkDeleteResult *brinvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats); -extern bytea *brinoptions(Datum reloptions, bool validate); =20 /* brin_validate.c */ extern bool brinvalidate(Oid opclassoid); diff --git a/src/include/access/gin_private.h b/src/include/access/gin_priv= ate.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 =20 =20 /* 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, Off= setNumber attnum, extern OffsetNumber gintuple_get_attrnum(GinState *ginstate, IndexTuple tu= ple); extern Datum gintuple_get_key(GinState *ginstate, IndexTuple tuple, GinNullCategory *category); +extern void *gingetreloptcatalog (void); =20 /* gininsert.c */ extern IndexBuildResult *ginbuild(Relation heap, Relation index, diff --git a/src/include/access/gist_private.h b/src/include/access/gist_pr= ivate.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; =20 + +/* + * 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 =3D 0, + GIST_OPTION_BUFFERING_OFF =3D 1, + GIST_OPTION_BUFFERING_AUTO =3D 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; =20 /* gist.c */ @@ -435,7 +453,6 @@ extern bool gistvalidate(Oid opclassoid); #define GIST_MIN_FILLFACTOR 10 #define GIST_DEFAULT_FILLFACTOR 90 =20 -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); =20 extern XLogRecPtr gistGetFakeLSN(Relation rel); +extern void * gistgetreloptcatalog (void); =20 /* gistvacuum.c */ extern IndexBulkDeleteResult *gistbulkdelete(IndexVacuumInfo *info, @@ -503,7 +521,6 @@ extern void gistSplitByKey(Relation r, Page page, Index= Tuple *itup, /* gistbuild.c */ extern IndexBuildResult *gistbuild(Relation heap, Relation index, struct IndexInfo *indexInfo); -extern void gistValidateBufferingOption(char *value); =20 /* gistbuildbuffers.c */ extern GISTBuildBuffers *gistInitBuildBuffers(int pagesPerBuffer, int leve= lStep, 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 =20 typedef HashMetaPageData *HashMetaPage; =20 + +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(IndexVacuu= mInfo *info, void *callback_state); extern IndexBulkDeleteResult *hashvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats); -extern bytea *hashoptions(Datum reloptions, bool validate); extern bool hashvalidate(Oid opclassoid); =20 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); =20 +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) =20 +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 =3D (1 << 0), /* Altering this option= is forbidden */ + OPTION_DEFINITION_FLAG_IGNORE =3D (1 << 1), /* Skip this option while par= sing. Used for + WITH OIDS special case */ + OPTION_DEFINITION_FLAG_REJECT =3D (1 << 2) /* Option will be rejected wh= en 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 =3D (1 << 0), + OPTIONS_PARSE_MODE_FOR_ALTER =3D (1 << 1), + OPTIONS_PARSE_MODE_FOR_RESET =3D (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 f= or + 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 valid= ate); + +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 *nam= e, + 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 *nam= e, + 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 *n= ame, + 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 representa= tion + * "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 =3D=3D 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 *cat= alog, + options_parse_mode mode); +extern bytea *optionsValuesToBytea(List * options, options_catalog *catalo= g); + +/* + * 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/reloption= s.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 =20 #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" =20 -/* kinds supported by reloptions */ -typedef enum relopt_kind -{ - RELOPT_KIND_HEAP =3D (1 << 0), - RELOPT_KIND_TOAST =3D (1 << 1), - RELOPT_KIND_BTREE =3D (1 << 2), - RELOPT_KIND_HASH =3D (1 << 3), - RELOPT_KIND_GIN =3D (1 << 4), - RELOPT_KIND_GIST =3D (1 << 5), - RELOPT_KIND_ATTRIBUTE =3D (1 << 6), - RELOPT_KIND_TABLESPACE =3D (1 << 7), - RELOPT_KIND_SPGIST =3D (1 << 8), - RELOPT_KIND_VIEW =3D (1 << 9), - RELOPT_KIND_BRIN =3D (1 << 10), - /* if you add a new kind, make sure you update "last_default" too */ - RELOPT_KIND_LAST_DEFAULT =3D RELOPT_KIND_BRIN, - /* some compilers treat enums as signed ints, so we can't use 1 << 31 */ - RELOPT_KIND_MAX =3D (1 << 30) -} relopt_kind; =20 /* reloption namespaces allowed for heaps -- currently only TOAST */ #define HEAP_RELOPT_NAMESPACES { "toast", NULL } =20 -/* 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 consid= er - * 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 returne= d by - * parseRelOptions: - * for (i =3D 0; options[i].gen->name; i++) - * { - * if (HAVE_RELOPTION("fillfactor", options[i]) - * { - * HANDLE_INT_RELOPTION("fillfactor", rdopts->fillfactor, options[i], &i= sset); - * 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 on= ly - * 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) =3D= =3D 0) - -#define HANDLE_INT_RELOPTION(optname, var, option, wasset) \ - do { \ - if (option.isset) \ - var =3D option.values.int_val; \ - else \ - var =3D ((relopt_int *) option.gen)->default_val; \ - (wasset) !=3D NULL ? *(wasset) =3D option.isset : (dummyret)NULL; \ - } while (0) - -#define HANDLE_BOOL_RELOPTION(optname, var, option, wasset) \ - do { \ - if (option.isset) \ - var =3D option.values.bool_val; \ - else \ - var =3D ((relopt_bool *) option.gen)->default_val; \ - (wasset) !=3D NULL ? *(wasset) =3D option.isset : (dummyret) NULL; \ - } while (0) =20 -#define HANDLE_REAL_RELOPTION(optname, var, option, wasset) \ - do { \ - if (option.isset) \ - var =3D option.values.real_val; \ - else \ - var =3D ((relopt_real *) option.gen)->default_val; \ - (wasset) !=3D NULL ? *(wasset) =3D option.isset : (dummyret) NULL; \ - } while (0) - -/* - * Note that this assumes that the variable is already allocated at the ta= il of - * reloptions structure (StdRdOptions or equivalent). - * - * "base" is a pointer to the reloptions structure, and "offset" is an int= eger - * variable that must be initialized to sizeof(reloptions structure). This - * struct must have been allocated with enough space to hold any string op= tion - * 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 =3D (relopt_string *) option.gen;\ - char *string_val; \ - if (option.isset) \ - string_val =3D option.values.string_val; \ - else if (!optstring->default_isnull) \ - string_val =3D optstring->default_val; \ - else \ - string_val =3D NULL; \ - (wasset) !=3D NULL ? *(wasset) =3D option.isset : (dummyret) NULL; \ - if (string_val =3D=3D NULL) \ - var =3D 0; \ - else \ - { \ - strcpy(((char *)(base)) + (offset), string_val); \ - var =3D (offset); \ - (offset) +=3D 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); =20 /* - * For use by code reading options already parsed: get a pointer to the st= ring - * value itself. "optstruct" is the StdRdOption struct or equivalent, "me= mber" - * 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 =3D=3D 0 ? NULL : \ - (char *)(optstruct) + (optstruct)->member) =20 +extern options_catalog *get_heap_relopt_catalog(void); +extern options_catalog *get_toast_relopt_catalog(void); =20 -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 validat= e); -extern bytea *view_reloptions(Datum reloptions, bool validate); -extern bytea *index_reloptions(amoptions_function amoptions, Datum relopti= ons, - 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 *defLi= st); =20 #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" =20 =20 -/* 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 =20 =20 /* spgutils.c */ -extern bytea *spgoptions(Datum reloptions, bool validate); +extern Datum spghandler(PG_FUNCTION_ARGS); =20 /* spginsert.c */ extern IndexBuildResult *spgbuild(Relation heap, Relation index, diff --git a/src/include/access/spgist_private.h b/src/include/access/spgis= t_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" =20 =20 +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) =20 /* 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 *s= tate, Page page, Item item, Size size, OffsetNumber *startOffset, bool errorOK); +extern void * spggetreloptcatalog(void); =20 /* 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/tablec= mds.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); =20 extern void AlterTable(Oid relid, LOCKMODE lockmode, AlterTableStmt *stmt); =20 -extern LOCKMODE AlterTableGetLockLevel(List *cmds); +extern LOCKMODE AlterTableGetLockLevel(Oid relid, List *cmds); =20 extern void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recurs= ing, LOCKMODE lockmode); =20 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; =20 -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; =20 #define HEAP_MIN_FILLFACTOR 10 #define HEAP_DEFAULT_FILLFACTOR 100 =20 +#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)) =20 /* - * 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) =20 /* - * 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 =3D=3D RELKIND_RELATION || \ (relation)->rd_rel->relkind =3D=3D RELKIND_MATVIEW) ? \ - ((StdRdOptions *) (relation)->rd_options)->user_catalog_table : false) + ((HeapOptions *) (relation)->rd_options)->user_catalog_table : false) =20 /* - * 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)) =20 +#define ToastGetTargetPageFreeSpace() \ + (BLCKSZ * (100 - TOAST_DEFAULT_FILLFACTOR) / 100) =20 /* * 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 =3D -1, + VIEW_OPTION_CHECK_OPTION_LOCAL =3D 0, + VIEW_OPTION_CHECK_OPTION_CASCADED =3D 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) =20 /* - * 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 !=3D 0) =20 -/* - * 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 !=3D 0 ? \ - strcmp((char *) (relation)->rd_options + \ - ((ViewOptions *) (relation)->rd_options)->check_option_offset, \ - "local") =3D=3D 0 : false) +#define ViewHasCheckOption(relation) \ +((relation)->rd_options && \ + ((ViewOptions *) (relation)->rd_options)->check_option !=3D = \ + VIEW_OPTION_CHECK_OPTION_NOT_SET) =20 /* - * 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 !=3D 0 ? \ - strcmp((char *) (relation)->rd_options + \ - ((ViewOptions *) (relation)->rd_options)->check_option_offset, \ - "cascaded") =3D=3D 0 : false) =20 +#define ViewHasCascadedCheckOption(relation) \ +((relation)->rd_options && \ + ((ViewOptions *) (relation)->rd_options)->check_option =3D=3D = \ + VIEW_OPTION_CHECK_OPTION_CASCADED) =20 /* * RelationIsValid @@ -556,7 +564,7 @@ typedef struct ViewOptions #define RelationIsAccessibleInLogicalDecoding(relation) \ (XLogLogicalInfoActive() && \ RelationNeedsWAL(relation) && \ - (IsCatalogRelation(relation) || RelationIsUsedAsCatalogTable(relation))) + (IsCatalogRelation(relation) || HeapIsUsedAsCatalogTable(relation))) =20 /* * RelationIsLogicallyLogged diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/e= xpected/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 =3D 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=3Doff); 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 =3D 1, n_distinct_inherited= =3D 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) =20 +-- Check that changing reloptions for brin index is not allowed +ALTER INDEX brinidx SET (pages_per_range =3D 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=3Don, gin_pending_list_limit=3D128 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=3D60); CREATE UNLOGGED TABLE unlogged_hash_table (id int4); CREATE INDEX unlogged_hash_index ON unlogged_hash_table USING hash (id int= 4_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 =3D 1; +-- Test buffering and fillfactor reloption takes valid values +create index gist_pointidx2 on gist_point_tbl using gist(p) with (bufferin= g =3D on, fillfactor=3D50); +create index gist_pointidx3 on gist_point_tbl using gist(p) with (bufferin= g =3D off); +create index gist_pointidx4 on gist_point_tbl using gist(p) with (bufferin= g =3D auto); +--Test buffering and fillfactor for refising invalid values +create index gist_pointidx5 on gist_point_tbl using gist(p) with (bufferin= g =3D 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 (fillfact= or=3D9); +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 (fillfact= or=3D101); +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/ex= pected/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=3D9); +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=3D101); +ERROR: value 101 out of bounds for option "fillfactor" +DETAIL: Valid values are between "10" and "100". +ALTER INDEX hash_f8_index SET (fillfactor=3D99); +ERROR: changing parameter "fillfactor" is not allowed diff --git a/src/test/regress/expected/reloptions.out b/src/test/regress/ex= pected/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=3D30,autovacuum_enabl= ed =3D false, autovacuum_analyze_scale_factor =3D 0.2); +SELECT reloptions FROM pg_class WHERE oid =3D 'reloptions_test'::regclass; + reloptions=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20 +--------------------------------------------------------------------------= ---- + {fillfactor=3D30,autovacuum_enabled=3Dfalse,autovacuum_analyze_scale_fact= or=3D0.2} +(1 row) + +-- Test =D1=81ase insensitive +CREATE TABLE reloptions_test3(i INT) WITH (FiLlFaCtoR=3D40); +SELECT reloptions FROM pg_class WHERE oid =3D 'reloptions_test3'::regclass; + reloptions=20=20=20=20 +----------------- + {fillfactor=3D40} +(1 row) + +-- Fail on min/max values check +CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=3D2); +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=3D110); +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= =3D -10.0); +ERROR: value -10.0 out of bounds for option "autovacuum_analyze_scale_fac= tor" +DETAIL: Valid values are between "0.000000" and "100.000000". +CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor= =3D 110.0); +ERROR: value 110.0 out of bounds for option "autovacuum_analyze_scale_fac= tor" +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=3D2); +ERROR: unrecognized parameter "not_existing_option" +CREATE TABLE reloptions_test2(i INT) WITH (not_existing_namespace.fillfact= or=3D2); +ERROR: unrecognized parameter namespace "not_existing_namespace" +-- Fail while setting unproper value +CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=3D30.5); +ERROR: invalid value for integer option "fillfactor": 30.5 +CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=3D'string'); +ERROR: invalid value for integer option "fillfactor": string +CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=3Dtrue); +ERROR: invalid value for integer option "fillfactor": true +CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled=3D12); +ERROR: invalid value for boolean option "autovacuum_enabled": 12 +CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled=3D30.5); +ERROR: invalid value for boolean option "autovacuum_enabled": 30.5 +CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled=3D'string'); +ERROR: invalid value for boolean option "autovacuum_enabled": string +CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor= =3D'string'); +ERROR: invalid value for floating point option "autovacuum_analyze_scale_= factor": string +CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor= =3Dtrue); +ERROR: invalid value for floating point option "autovacuum_analyze_scale_= factor": true +-- Specifing name only should fail as if there was =3Dture after it +CREATE TABLE reloptions_test2(i INT) WITH (fillfactor);=20 +ERROR: invalid value for integer option "fillfactor": true +-- Simple ALTER TABLE +ALTER TABLE reloptions_test SET (fillfactor=3D31, autovacuum_analyze_scale= _factor =3D 0.3); +SELECT reloptions FROM pg_class WHERE oid =3D 'reloptions_test'::regclass; + reloptions=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20 +--------------------------------------------------------------------------= ---- + {autovacuum_enabled=3Dfalse,fillfactor=3D31,autovacuum_analyze_scale_fact= or=3D0.3} +(1 row) + +-- Check that we cat set boolean option to true just by mentioning it +ALTER TABLE reloptions_test SET (autovacuum_enabled, fillfactor=3D32); +SELECT reloptions FROM pg_class WHERE oid =3D 'reloptions_test'::regclass; + reloptions=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20 +--------------------------------------------------------------------------= --- + {autovacuum_analyze_scale_factor=3D0.3,autovacuum_enabled=3Dtrue,fillfact= or=3D32} +(1 row) + +-- Check that RESET works well +ALTER TABLE reloptions_test RESET (fillfactor); +SELECT reloptions FROM pg_class WHERE oid =3D 'reloptions_test'::regclass; + reloptions=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20=20=20=20=20=20=20=20=20=20=20 +--------------------------------------------------------------- + {autovacuum_analyze_scale_factor=3D0.3,autovacuum_enabled=3Dtrue} +(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 =3D 'reloptions_test'::regclass = AND reloptions IS NULL; + reloptions=20 +------------ +=20 +(1 row) + +-- Check RESET fails on att=3Dvalue +ALTER TABLE reloptions_test RESET (fillfactor=3D12); +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=3D20, oids=3Dtrue); +SELECT reloptions FROM pg_class WHERE oid =3D 'reloptions_test'::regclass; + reloptions=20=20=20=20 +----------------- + {fillfactor=3D20} +(1 row) + +-- Now testing toast.* options +DROP TABLE reloptions_test; +CREATE TABLE reloptions_test (s VARCHAR) WITH (toast.autovacuum_vacuum_cos= t_delay =3D 23 ); +SELECT reloptions FROM pg_class WHERE oid =3D (SELECT reltoastrelid FROM p= g_class WHERE oid =3D 'reloptions_test'::regclass); + reloptions=20=20=20=20=20=20=20=20=20=20=20=20=20 +----------------------------------- + {autovacuum_vacuum_cost_delay=3D23} +(1 row) + +ALTER TABLE reloptions_test SET (toast.autovacuum_vacuum_cost_delay =3D 24= ); +SELECT reloptions FROM pg_class WHERE oid =3D (SELECT reltoastrelid FROM p= g_class WHERE oid =3D 'reloptions_test'::regclass); + reloptions=20=20=20=20=20=20=20=20=20=20=20=20=20 +----------------------------------- + {autovacuum_vacuum_cost_delay=3D24} +(1 row) + +ALTER TABLE reloptions_test RESET (toast.autovacuum_vacuum_cost_delay); +SELECT reloptions FROM pg_class WHERE oid =3D (SELECT reltoastrelid FROM p= g_class WHERE oid =3D 'reloptions_test'::regclass); + reloptions=20 +------------ +=20 +(1 row) + +-- Fail on unexisting options in toast namespace +CREATE TABLE reloptions_test2 (i int) WITH (toast.not_existing_option =3D = 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_d= elay =3D 23 ); +ERROR: no TOAST relation was created for a table. Can't set toast.* stora= ge parameters +DROP TABLE reloptions_test; +CREATE TABLE reloptions_test(i INT); +ALTER TABLE reloptions_test SET (toast.autovacuum_vacuum_cost_delay =3D 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_fa= ctor=3D1, autovacuum_analyze_threshold=3D1); +CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analyze_s= cale_factor=3D1); +ERROR: unrecognized parameter "toast.autovacuum_analyze_scale_factor" +CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analyze_t= hreshold=3D1); +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_cos= t_delay =3D 23, autovacuum_vacuum_cost_delay =3D 24, fillfactor =3D 40); +SELECT reloptions FROM pg_class WHERE oid =3D 'reloptions_test'::regclass; + reloptions=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20= =20=20=20=20=20 +------------------------------------------------- + {autovacuum_vacuum_cost_delay=3D24,fillfactor=3D40} +(1 row) + +SELECT reloptions FROM pg_class WHERE oid =3D (SELECT reltoastrelid FROM p= g_class WHERE oid =3D 'reloptions_test'::regclass); + reloptions=20=20=20=20=20=20=20=20=20=20=20=20=20 +----------------------------------- + {autovacuum_vacuum_cost_delay=3D23} +(1 row) + +-- Same FOR CREATE and ALTER INDEX for btree indexes +CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (fillfactor= =3D30); +SELECT reloptions FROM pg_class WHERE oid =3D 'reloptions_test_idx'::regcl= ass; + reloptions=20=20=20=20 +----------------- + {fillfactor=3D30} +(1 row) + +-- Fail when option and namespase do not exist +CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_existing= _option=3D2); +ERROR: unrecognized parameter "not_existing_option" +CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_existing= _option.fillfactor=3D2); +ERROR: unrecognized parameter namespace "not_existing_option" +-- Check ranges +CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfactor= =3D1); +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= =3D130); +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=3D40); +SELECT reloptions FROM pg_class WHERE oid =3D 'reloptions_test_idx'::regcl= ass; + reloptions=20=20=20=20 +----------------- + {fillfactor=3D40} +(1 row) + +-- Check alter on empty relop list +CREATE INDEX reloptions_test_idx3 ON reloptions_test (s); +ALTER INDEX reloptions_test_idx3 SET (fillfactor=3D40); +SELECT reloptions FROM pg_class WHERE oid =3D 'reloptions_test_idx3'::regc= lass; + reloptions=20=20=20=20 +----------------- + {fillfactor=3D40} +(1 row) + diff --git a/src/test/regress/expected/spgist.out b/src/test/regress/expect= ed/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 (fi= llfactor =3D 75); -- Test vacuum-root operation. It gets invoked when the root is also a lea= f, -- 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 (f= illfactor =3D 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 (f= illfactor =3D 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 =3D 76); diff --git a/src/test/regress/expected/updatable_views.out b/src/test/regre= ss/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; =20 ALTER VIEW rw_view1 SET (check_option=3Dhere); -- 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=3Dlocal); 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/in= put/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@'; =20 -- try setting and resetting some properties for the new tablespace -ALTER TABLESPACE regress_tblspace SET (random_page_cost =3D 1.0); +ALTER TABLESPACE regress_tblspace SET (random_page_cost =3D 1.0, seq_page_= cost =3D 1.1); ALTER TABLESPACE regress_tblspace SET (some_nonexistent_parameter =3D true= ); -- fail ALTER TABLESPACE regress_tblspace RESET (random_page_cost =3D 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_co= ncurrency); -- ok =20 -- create a schema we can use CREATE SCHEMA testschema; diff --git a/src/test/regress/output/tablespace.source b/src/test/regress/o= utput/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 =3D 1.0); +ALTER TABLESPACE regress_tblspace SET (random_page_cost =3D 1.0, seq_page_= cost =3D 1.1); ALTER TABLESPACE regress_tblspace SET (some_nonexistent_parameter =3D true= ); -- fail ERROR: unrecognized parameter "some_nonexistent_parameter" ALTER TABLESPACE regress_tblspace RESET (random_page_cost =3D 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_co= ncurrency); -- 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 depen= dency 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 returni= ng largeobject with xml +test: plancache limit plpgsql copy2 temp domain rangefuncs prepare without= _oid conversion truncate alter_table sequence polymorphism rowtypes returni= ng largeobject with xml reloptions =20 # 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_sch= edule 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/al= ter_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 =3D 1, n_distinct_inherited= =3D 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 =3D '' 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 in= dex 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 =3D 10); +ALTER INDEX brinidx RESET (pages_per_range); diff --git a/src/test/regress/sql/create_index.sql b/src/test/regress/sql/c= reate_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 ha= sh (random name_ops); =20 CREATE INDEX hash_txt_index ON hash_txt_heap USING hash (random text_ops); =20 -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=3D60); =20 CREATE UNLOGGED TABLE unlogged_hash_table (id int4); CREATE INDEX unlogged_hash_index ON unlogged_hash_table USING hash (id int= 4_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_ser= ies(1, 10000) g; -- To test vacuum, delete some entries from all over the index. delete from gist_point_tbl where id % 2 =3D 1; =20 +-- Test buffering and fillfactor reloption takes valid values +create index gist_pointidx2 on gist_point_tbl using gist(p) with (bufferin= g =3D on, fillfactor=3D50); +create index gist_pointidx3 on gist_point_tbl using gist(p) with (bufferin= g =3D off); +create index gist_pointidx4 on gist_point_tbl using gist(p) with (bufferin= g =3D auto); +--Test buffering and fillfactor for refising invalid values +create index gist_pointidx5 on gist_point_tbl using gist(p) with (bufferin= g =3D invalid_value); +create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfact= or=3D9); +create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfact= or=3D101); + + + -- 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/has= h_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=3D9); +CREATE INDEX hash_f8_index2 ON hash_f8_heap USING hash (random float8_ops)= WITH (fillfactor=3D101); +ALTER INDEX hash_f8_index SET (fillfactor=3D99); \ No newline at end of file diff --git a/src/test/regress/sql/reloptions.sql b/src/test/regress/sql/rel= options.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=3D30,autovacuum_enabl= ed =3D false, autovacuum_analyze_scale_factor =3D 0.2); + +SELECT reloptions FROM pg_class WHERE oid =3D 'reloptions_test'::regclass; + +-- Test =D1=81ase insensitive +CREATE TABLE reloptions_test3(i INT) WITH (FiLlFaCtoR=3D40); + +SELECT reloptions FROM pg_class WHERE oid =3D 'reloptions_test3'::regclass; + +-- Fail on min/max values check +CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=3D2); + +CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=3D110); + +CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor= =3D -10.0); + +CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor= =3D 110.0); + +-- Fail when option and namespase do not exist + +CREATE TABLE reloptions_test2(i INT) WITH (not_existing_option=3D2); + +CREATE TABLE reloptions_test2(i INT) WITH (not_existing_namespace.fillfact= or=3D2); + +-- Fail while setting unproper value + +CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=3D30.5); + +CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=3D'string'); + +CREATE TABLE reloptions_test2(i INT) WITH (fillfactor=3Dtrue); + +CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled=3D12); + +CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled=3D30.5); + +CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_enabled=3D'string'); + +CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor= =3D'string'); + +CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_factor= =3Dtrue); + +-- Specifing name only should fail as if there was =3Dture after it +CREATE TABLE reloptions_test2(i INT) WITH (fillfactor);=20 + +-- Simple ALTER TABLE + +ALTER TABLE reloptions_test SET (fillfactor=3D31, autovacuum_analyze_scale= _factor =3D 0.3); + +SELECT reloptions FROM pg_class WHERE oid =3D 'reloptions_test'::regclass; + +-- Check that we cat set boolean option to true just by mentioning it + +ALTER TABLE reloptions_test SET (autovacuum_enabled, fillfactor=3D32); + +SELECT reloptions FROM pg_class WHERE oid =3D 'reloptions_test'::regclass; + +-- Check that RESET works well + +ALTER TABLE reloptions_test RESET (fillfactor); + +SELECT reloptions FROM pg_class WHERE oid =3D '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 =3D 'reloptions_test'::regclass = AND reloptions IS NULL; + +-- Check RESET fails on att=3Dvalue + +ALTER TABLE reloptions_test RESET (fillfactor=3D12); + +-- Check oids options is ignored +DROP TABLE reloptions_test; +CREATE TABLE reloptions_test(i INT) WITH (fillfactor=3D20, oids=3Dtrue); +SELECT reloptions FROM pg_class WHERE oid =3D 'reloptions_test'::regclass; + +-- Now testing toast.* options +DROP TABLE reloptions_test; + +CREATE TABLE reloptions_test (s VARCHAR) WITH (toast.autovacuum_vacuum_cos= t_delay =3D 23 ); + +SELECT reloptions FROM pg_class WHERE oid =3D (SELECT reltoastrelid FROM p= g_class WHERE oid =3D 'reloptions_test'::regclass); + +ALTER TABLE reloptions_test SET (toast.autovacuum_vacuum_cost_delay =3D 24= ); + +SELECT reloptions FROM pg_class WHERE oid =3D (SELECT reltoastrelid FROM p= g_class WHERE oid =3D 'reloptions_test'::regclass); + +ALTER TABLE reloptions_test RESET (toast.autovacuum_vacuum_cost_delay); + +SELECT reloptions FROM pg_class WHERE oid =3D (SELECT reltoastrelid FROM p= g_class WHERE oid =3D 'reloptions_test'::regclass); + +-- Fail on unexisting options in toast namespace +CREATE TABLE reloptions_test2 (i int) WITH (toast.not_existing_option =3D = 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_d= elay =3D 23 ); +DROP TABLE reloptions_test; + +CREATE TABLE reloptions_test(i INT); +ALTER TABLE reloptions_test SET (toast.autovacuum_vacuum_cost_delay =3D 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_fa= ctor=3D1, autovacuum_analyze_threshold=3D1); + +CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analyze_s= cale_factor=3D1); +CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analyze_t= hreshold=3D1); + +-- And now mixed toast + heap +DROP TABLE reloptions_test; + +CREATE TABLE reloptions_test (s VARCHAR) WITH (toast.autovacuum_vacuum_cos= t_delay =3D 23, autovacuum_vacuum_cost_delay =3D 24, fillfactor =3D 40); + +SELECT reloptions FROM pg_class WHERE oid =3D 'reloptions_test'::regclass; +SELECT reloptions FROM pg_class WHERE oid =3D (SELECT reltoastrelid FROM p= g_class WHERE oid =3D 'reloptions_test'::regclass); + +-- Same FOR CREATE and ALTER INDEX for btree indexes + +CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (fillfactor= =3D30); + +SELECT reloptions FROM pg_class WHERE oid =3D 'reloptions_test_idx'::regcl= ass; + +-- Fail when option and namespase do not exist + +CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_existing= _option=3D2); + +CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_existing= _option.fillfactor=3D2); + +-- Check ranges + +CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfactor= =3D1); + +CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfactor= =3D130); + +-- Check alter + +ALTER INDEX reloptions_test_idx SET (fillfactor=3D40); + +SELECT reloptions FROM pg_class WHERE oid =3D 'reloptions_test_idx'::regcl= ass; + +-- Check alter on empty relop list + +CREATE INDEX reloptions_test_idx3 ON reloptions_test (s); + +ALTER INDEX reloptions_test_idx3 SET (fillfactor=3D40); + +SELECT reloptions FROM pg_class WHERE oid =3D 'reloptions_test_idx3'::regc= lass; + + 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. =20 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 (fi= llfactor =3D 75); =20 -- Test vacuum-root operation. It gets invoked when the root is also a lea= f, -- 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 (f= illfactor =3D 9); +create index spgist_point_idx2 on spgist_point_tbl using spgist(p) with (f= illfactor =3D 101); + +-- ALTER reloption should work +alter index spgist_point_idx set (fillfactor =3D 76); --nextPart1556889.GIN69zsn4T Content-Type: text/plain Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 -- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers --nextPart1556889.GIN69zsn4T--