Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtp (Exim 4.84_2) (envelope-from ) id 1dPC0p-0006nS-Df for pgsql-hackers@arkaria.postgresql.org; Sun, 25 Jun 2017 18:11:56 +0000 Received: from localhost ([127.0.0.1] helo=postgresql.org) by malur.postgresql.org with smtp (Exim 4.84_2) (envelope-from ) id 1dPC0o-0001Hf-Lp for pgsql-hackers@arkaria.postgresql.org; Sun, 25 Jun 2017 18:11:54 +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 1dPBvd-0006eE-5M for pgsql-hackers@postgresql.org; Sun, 25 Jun 2017 18:06:34 +0000 Received: from enot.nataraj.su ([195.54.209.170]) by makus.postgresql.org with esmtp (Exim 4.84_2) (envelope-from ) id 1dPBvJ-0005Eq-TM for pgsql-hackers@postgresql.org; Sun, 25 Jun 2017 18:06:31 +0000 Received: from x200m.localnet (unknown [188.187.12.114]) by enot.nataraj.su (Postfix) with ESMTPSA id 81458511E348; Sun, 25 Jun 2017 14:05:04 -0400 (EDT) From: Nikolay Shaplov To: pgsql-hackers@postgresql.org Cc: Andres Freund , Alvaro Herrera Subject: Re: [PATCH v.7c] Move all am-related reloption code into src/backend/access/[am-name] and get rid of relopt_kind for custom AM Date: Sun, 25 Jun 2017 21:05:49 +0300 Message-ID: <20250381.REpx4ebY6V@x200m> User-Agent: KMail/4.14.1 (Linux/3.16.0-4-amd64; KDE/4.14.2; x86_64; ; ) In-Reply-To: <2911117.zAfR6qRW1g@x200m> References: <20170317172126.o452q2cjds6o3pfx@alvherre.pgsql> <20170403183128.cqzycjjqc63glgqf@alap3.anarazel.de> <2911117.zAfR6qRW1g@x200m> MIME-Version: 1.0 Content-Type: multipart/signed; boundary="nextPart1694667.WJnQhjP3oG"; micalg="pgp-sha256"; protocol="application/pgp-signature" 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 --nextPart1694667.WJnQhjP3oG Content-Type: multipart/mixed; boundary="nextPart5742593.9NUVLNG8jH" Content-Transfer-Encoding: quoted-printable This is a multi-part message in MIME format. =2D-nextPart5742593.9NUVLNG8jH Content-Transfer-Encoding: base64 Content-Type: text/plain; charset="utf-8" 0JIg0L/QuNGB0YzQvNC1INC+0YIgMTkg0LDQv9GA0LXQu9GPIDIwMTcgMjE6MzE6NDIg0L/QvtC7 0YzQt9C+0LLQsNGC0LXQu9GMIE5pa29sYXkgU2hhcGxvdiDQvdCw0L/QuNGB0LDQuzoKCkkgcmVi YXNlZCB0aGUgcGF0Y2ggYWdhaW4sIGFmdGVyIGNvZGUgcmVpbmRlbnQuCgpTdGlsbCB3YWl0aW5n IGZvciByZXZpZXcuCgotLSAKRG8gY29kZSBmb3IgZnVuLiBDYW4gZG8gaXQgZm9yIG1vbmV5IChQ ZXJsICYgQy9DKysgfjEwaC93ZWVrKQ== =2D-nextPart5742593.9NUVLNG8jH Content-Disposition: attachment; filename="reloptions7c.diff" Content-Transfer-Encoding: quoted-printable Content-Type: text/x-patch; charset="UTF-8"; name="reloptions7c.diff" diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h index f3df1af..9c27326 100644 =2D-- a/contrib/bloom/bloom.h +++ b/contrib/bloom/bloom.h @@ -204,7 +204,6 @@ extern IndexBulkDeleteResult *blbulkdelete(IndexVac= uumInfo *info, =09=09=09 void *callback_state); extern IndexBulkDeleteResult *blvacuumcleanup(IndexVacuumInfo *info, =09=09=09=09IndexBulkDeleteResult *stats); =2Dextern bytea *bloptions(Datum reloptions, bool validate); extern void blcostestimate(PlannerInfo *root, IndexPath *path, =09=09=09 double loop_count, Cost *indexStartupCost, =09=09=09 Cost *indexTotalCost, Selectivity *indexSelectivity, diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c index f2eda67..e937fed 100644 =2D-- a/contrib/bloom/blutils.c +++ b/contrib/bloom/blutils.c @@ -35,49 +35,13 @@ =20 PG_FUNCTION_INFO_V1(blhandler); =20 =2D/* Kind of relation options for bloom index */ =2Dstatic relopt_kind bl_relopt_kind; =2D =2D/* parse table for fillRelOptions */ =2Dstatic 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); =2D =2D/* =2D * Module initialize function: initialize info about Bloom relation op= tions. =2D * =2D * Note: keep this in sync with makeDefaultBloomOptions(). =2D */ =2Dvoid =2D_PG_init(void) =2D{ =2D=09int=09=09=09i; =2D=09char=09=09buf[16]; =2D =2D=09bl_relopt_kind =3D add_reloption_kind(); =2D =2D=09/* Option for length of signature */ =2D=09add_int_reloption(bl_relopt_kind, "length", =2D=09=09=09=09=09 "Length of signature in bits", =2D=09=09=09=09=09 DEFAULT_BLOOM_LENGTH, 1, MAX_BLOOM_LENGTH); =2D=09bl_relopt_tab[0].optname =3D "length"; =2D=09bl_relopt_tab[0].opttype =3D RELOPT_TYPE_INT; =2D=09bl_relopt_tab[0].offset =3D offsetof(BloomOptions, bloomLength); =2D =2D=09/* Number of bits for each possible index column: col1, col2, ... *= / =2D=09for (i =3D 0; i < INDEX_MAX_KEYS; i++) =2D=09{ =2D=09=09snprintf(buf, sizeof(buf), "col%d", i + 1); =2D=09=09add_int_reloption(bl_relopt_kind, buf, =2D=09=09=09=09=09=09 "Number of bits generated for each index column", =2D=09=09=09=09=09=09 DEFAULT_BLOOM_BITS, 1, MAX_BLOOM_BITS); =2D=09=09bl_relopt_tab[i + 1].optname =3D MemoryContextStrdup(TopMemoryCo= ntext, =2D=09=09=09=09=09=09=09=09=09=09=09=09=09=09 buf); =2D=09=09bl_relopt_tab[i + 1].opttype =3D RELOPT_TYPE_INT; =2D=09=09bl_relopt_tab[i + 1].offset =3D offsetof(BloomOptions, bitSize[0= ]) + sizeof(int) * i; =2D=09} =2D} +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) =09amroutine->amvacuumcleanup =3D blvacuumcleanup; =09amroutine->amcanreturn =3D NULL; =09amroutine->amcostestimate =3D blcostestimate; =2D=09amroutine->amoptions =3D bloptions; +=09amroutine->amrelopt_catalog =3D blrelopt_catalog; =09amroutine->amproperty =3D NULL; =09amroutine->amvalidate =3D blvalidate; =09amroutine->ambeginscan =3D blbeginscan; @@ -146,6 +110,28 @@ blhandler(PG_FUNCTION_ARGS) =09PG_RETURN_POINTER(amroutine); } =20 +void +blReloptionPostprocess(void *data, bool validate) +{ +=09BloomOptions *opts =3D (BloomOptions *) data; +=09int=09=09=09i; + +=09if (validate) +=09=09for (i =3D 0; i < INDEX_MAX_KEYS; i++) +=09=09{ +=09=09=09if (opts->bitSize[i] >=3D opts->bloomLength) +=09=09=09{ +=09=09=09=09ereport(ERROR, +=09=09=09=09=09=09(errcode(ERRCODE_INVALID_PARAMETER_VALUE), +=09=09=09=09=09 errmsg("col%i should not be grater than length", i))= ); +=09=09=09} +=09=09} + +=09/* Convert signature length from # of bits to # to words, rounding = up */ +=09opts->bloomLength =3D (opts->bloomLength + SIGNWORDBITS - 1) / SIGN= WORDBITS; +} + + /* * Fill BloomState structure for particular index. */ @@ -466,24 +452,40 @@ BloomInitMetapage(Relation index) =09UnlockReleaseBuffer(metaBuffer); } =20 =2D/* =2D * Parse reloptions for bloom index, producing a BloomOptions struct. =2D */ =2Dbytea * =2Dbloptions(Datum reloptions, bool validate) +static void * +blrelopt_catalog(void) { =2D=09relopt_value *options; =2D=09int=09=09=09numoptions; =2D=09BloomOptions *rdopts; +=09if (!bl_relopt_catalog) +=09{ =20 =2D=09/* Parse the user-given reloptions */ =2D=09options =3D parseRelOptions(reloptions, validate, bl_relopt_kind, &= numoptions); =2D=09rdopts =3D allocateReloptStruct(sizeof(BloomOptions), options, numo= ptions); =2D=09fillRelOptions((void *) rdopts, sizeof(BloomOptions), options, numo= ptions, =2D=09=09=09=09 validate, bl_relopt_tab, lengthof(bl_relopt_tab)); +=09=09int=09=09=09i; +=09=09char=09=09buf[16]; =20 =2D=09/* Convert signature length from # of bits to # to words, rounding = up */ =2D=09rdopts->bloomLength =3D (rdopts->bloomLength + SIGNWORDBITS - 1) / = SIGNWORDBITS; +=09=09bl_relopt_catalog =3D allocateOptionsCatalog(NULL, +=09=09=09=09=09=09=09=09 sizeof(BloomOptions), INDEX_MAX_KEYS + 1); +=09=09bl_relopt_catalog->postprocess_fun =3D blReloptionPostprocess; + +=09=09optionsCatalogAddItemInt(bl_relopt_catalog, "length", +=09=09=09=09=09=09=09=09 "Length of signature in bits", +=09=09=09=09=09=09=09=09 NoLock,=09=09/* No lock as far as ALTER is +=09=09=09=09=09=09=09=09=09=09=09=09 * forbidden */ +=09=09=09=09=09=09=09=09 OPTION_DEFINITION_FLAG_FORBID_ALTER, +=09=09=09=09=09=09=09=09 offsetof(BloomOptions, bloomLength), +=09=09=09=09=09=09=09=09 DEFAULT_BLOOM_LENGTH, 1, MAX_BLOOM_LENGTH); =20 =2D=09return (bytea *) rdopts; +=09=09/* Number of bits for each possible index column: col1, col2, ..= . */ +=09=09for (i =3D 0; i < INDEX_MAX_KEYS; i++) +=09=09{ +=09=09=09snprintf(buf, 16, "col%d", i + 1); +=09=09=09optionsCatalogAddItemInt(bl_relopt_catalog, buf, +=09=09=09=09=09=09=09=09 "Number of bits for corresponding column", +=09=09=09=09=09=09=09=09=09 NoLock,=09/* No lock as far as ALTER is +=09=09=09=09=09=09=09=09=09=09=09=09 * forbidden */ +=09=09=09=09=09=09=09=09=09 OPTION_DEFINITION_FLAG_FORBID_ALTER, +=09=09=09=09=09=09=09=09=09 offsetof(BloomOptions, bitSize[i]), +=09=09=09=09=09=09=09=09=09 DEFAULT_BLOOM_BITS, 1, MAX_BLOOM_BITS); +=09=09} + +=09} +=09return (void *) bl_relopt_catalog; } diff --git a/contrib/bloom/expected/bloom.out b/contrib/bloom/expected/= bloom.out index cbc50f7..7e3bbc0 100644 =2D-- 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 +------------------- + {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; =09amroutine->amcanreturn =3D NULL; =09amroutine->amcostestimate =3D brincostestimate; =2D=09amroutine->amoptions =3D brinoptions; =09amroutine->amproperty =3D NULL; =09amroutine->amvalidate =3D brinvalidate; =09amroutine->ambeginscan =3D brinbeginscan; @@ -116,6 +116,7 @@ brinhandler(PG_FUNCTION_ARGS) =09amroutine->amendscan =3D brinendscan; =09amroutine->ammarkpos =3D NULL; =09amroutine->amrestrpos =3D NULL; +=09amroutine->amrelopt_catalog =3D bringetreloptcatalog; =09amroutine->amestimateparallelscan =3D NULL; =09amroutine->aminitparallelscan =3D NULL; =09amroutine->amparallelrescan =3D NULL; @@ -798,37 +799,6 @@ brinvacuumcleanup(IndexVacuumInfo *info, IndexBulk= DeleteResult *stats) } =20 /* =2D * reloptions processor for BRIN indexes =2D */ =2Dbytea * =2Dbrinoptions(Datum reloptions, bool validate) =2D{ =2D=09relopt_value *options; =2D=09BrinOptions *rdopts; =2D=09int=09=09=09numoptions; =2D=09static const relopt_parse_elt tab[] =3D { =2D=09=09{"pages_per_range", RELOPT_TYPE_INT, offsetof(BrinOptions, pages= PerRange)}, =2D=09=09{"autosummarize", RELOPT_TYPE_BOOL, offsetof(BrinOptions, autosu= mmarize)} =2D=09}; =2D =2D=09options =3D parseRelOptions(reloptions, validate, RELOPT_KIND_BRIN,= =2D=09=09=09=09=09=09=09 &numoptions); =2D =2D=09/* if none set, we're done */ =2D=09if (numoptions =3D=3D 0) =2D=09=09return NULL; =2D =2D=09rdopts =3D allocateReloptStruct(sizeof(BrinOptions), options, numop= tions); =2D =2D=09fillRelOptions((void *) rdopts, sizeof(BrinOptions), options, numop= tions, =2D=09=09=09=09 validate, tab, lengthof(tab)); =2D =2D=09pfree(options); =2D =2D=09return (bytea *) rdopts; =2D} =2D =2D/* * SQL-callable function to scan through an index and summarize all ra= nges * that are not currently summarized. */ @@ -1426,3 +1396,33 @@ brin_vacuum_scan(Relation idxrel, BufferAccessSt= rategy strategy) =09if (vacuum_fsm) =09=09FreeSpaceMapVacuum(idxrel); } + +static options_catalog *brin_relopt_catalog =3D NULL; + +static void * +bringetreloptcatalog(void) +{ +=09if (!brin_relopt_catalog) +=09{ +=09=09brin_relopt_catalog =3D allocateOptionsCatalog(NULL, +=09=09=09=09=09=09=09=09=09=09=09=09=09 sizeof(BrinOptions), 2); + +=09=09optionsCatalogAddItemInt(brin_relopt_catalog, "pages_per_range",= +=09=09=09 "Number of pages that each page range covers in a BRIN ind= ex", +=09=09=09=09=09=09=09=09 NoLock,=09=09/* since ALTER is not allowed +=09=09=09=09=09=09=09=09=09=09=09=09 * no lock needed */ +=09=09=09=09=09=09=09=09 OPTION_DEFINITION_FLAG_FORBID_ALTER, +=09=09=09=09=09=09=09=09 offsetof(BrinOptions, pagesPerRange), +=09=09=09=09=09=09=09=09 BRIN_DEFAULT_PAGES_PER_RANGE, +=09=09=09=09=09=09=09=09 BRIN_MIN_PAGES_PER_RANGE, +=09=09=09=09=09=09=09=09 BRIN_MAX_PAGES_PER_RANGE); + +=09=09optionsCatalogAddItemBool(brin_relopt_catalog, "autosummarize", +=09=09=09=09=09=09"Enables automatic summarization on this BRIN index"= , +=09=09=09=09=09=09=09=09 AccessExclusiveLock, +=09=09=09=09=09=09=09=09 0, +=09=09=09=09=09=09=09=09 offsetof(BrinOptions, autosummarize), +=09=09=09=09=09=09=09=09 false); +=09} +=09return brin_relopt_catalog; +} diff --git a/src/backend/access/common/Makefile b/src/backend/access/co= mmon/Makefile index fb27944..c9fb063 100644 =2D-- 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 =2DOBJS =3D bufmask.o heaptuple.o indextuple.o printsimple.o printtup.o \= +OBJS =3D bufmask.o heaptuple.o indextuple.o options.o printsimple.o pr= inttup.o \ =09reloptions.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/c= ommon/options.c new file mode 100644 index 0000000..4250173 =2D-- /dev/null +++ b/src/backend/access/common/options.c @@ -0,0 +1,1445 @@ +/*--------------------------------------------------------------------= =2D---- + * + * options.c + *=09 Core support for options used for relotions (pg_class.reloption= s), + *=09 attoptions (pg_attribute.attoptions), and can be used for other= + *=09 kinds of options. + * + *=09 Here you can find functions that allow to define available opti= ons + *=09 for certain object (certain retaion type, attribute type etc), = and + *=09 functions that allows to convert options from one represenation= to + *=09 another and validate them. + * + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Gro= up + * Portions Copyright (c) 1994, Regents of the University of Californi= a + * + * + * IDENTIFICATION + *=09 src/backend/access/common/options.c + * + *--------------------------------------------------------------------= =2D---- + */ + +#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 cr= eate an + * options catalog using allocateOptionsCatalog function, fill it wit= h + * optionsCatalogAddItemXxxx functions with definition of available op= tions. + * Then instance of this object can take option values from syntax ana= lyzer + * when instance in created using SQL command or from attribute of + * pg_catalog.yyyy table, when object that already exists is loaded fo= rm + * pg_catalog. + * + * Now options are mainly used for all kind or relations (pg_class.rel= option) + * 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 an= d 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, cha= r *name, +=09=09=09=09=09=09 char *desc, LOCKMODE lockmode, +=09=09=09=09=09=09 option_definition_flags flags, int struct_offset); + +static void parse_one_option(option_value * option, char *text_str, +=09=09=09=09 int text_len, bool validate); +static void *optionsAllocateBytea(options_catalog * catalog, List *opt= ions); + + +static List * +optionsDefListToRawValues(List *defList, options_parse_mode +=09=09=09=09=09=09 parse_mode); +static Datum optionsValuesToTextArray(List *options_values); +static List *optionsTextArrayToRawValues(Datum array_datum); +static List *optionsMergeOptionValues(List *old_options, List *new_opt= ions); +static List *optionsParseRawValues(List *raw_values, options_catalog *= catalog, +=09=09=09=09=09 options_parse_mode mode); +static bytea *optionsValuesToBytea(List *options, options_catalog * ca= talog); + + +/* + * Options catalog functions + */ + +/* + * Options catalog describes options available for certain object. Cat= alog has + * all necessary information for parsing transforming and validating o= ptions + * for an object. All parsing/validation/transformation functions shou= ld not + * know any details of option implementation for certain object, all t= his + * information should be stored in catalog instead and interpreted by + * pars/valid/transf functions blindly. + * + * The heart of the option catalog is an array of option definitions. = Options + * definition specifies name of option, type, range of acceptable valu= es, and + * default value. + * + * Options values can be one of the following types: bool, int, real, = enum, + * string. For more info see "option_type" and "optionsCatalogAddItemY= yyy" + * functions. + * + * Option definition flags allows to define parser behavior for specia= l (or not + * so special) cases. See option_definition_flags for more info. + * + * Options and Lock levels: + * + * The default choice for any new option should be AccessExclusiveLock= . + * In some cases the lock level can be reduced from there, but the loc= k + * level chosen should always conflict with itself to ensure that mult= iple + * 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 chan= ge it. + * Safety first. Existing choices are documented here, and elsewhere i= n + * backend code where the parameters are used. + * + * In general, anything that affects the results obtained from a SELEC= T must be + * protected by AccessExclusiveLock. + * + * Autovacuum related parameters can be set at ShareUpdateExclusiveLoc= k + * since they are only used by the AV procs and don't change anything + * currently executing. + * + * Fillfactor can be set because it applies only to subsequent changes= made to + * data blocks, as documented in heapio.c + * + * n_distinct options can be set at ShareUpdateExclusiveLock because t= hey + * 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 stronge= r lock. + * + * Planner-related parameters can be set with ShareUpdateExclusiveLock= because + * they only affect planning and not the correctness of the execution.= Plans + * cannot be changed in mid-flight, so changes here could not easily r= esult in + * new improved plans in any case. So we allow existing queries to con= tinue + * and existing plans to survive, a small price to pay for allowing be= tter + * 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 d= oesn't + * affect existing plans or queries. +*/ + +/* + * allocateOptionsCatalog + *=09=09Allocate memory for a new catalog and initializes structure me= mbers. + * + * namespace - name of a namespace in which all items of the catalog + * exists (E.g. namespace.option=3Dvalue). For now postgres uses only = toast. + * namespace for tables. + * + * size_of_bytea - size of bytea-packed C-structure where all option v= alues + * would be finally mapped, so it can be fast and easily used by pg co= re + * + * num_items_expected - expected number of items in options catalog. I= f it is + * set to positive value, proper chunk of memory would be allocated, a= nd postgres + * will assert if you try to add more options to the catalog. If it is= set to -1, then + * you can add any number of options to catalog, memory for entries wo= uld be dynamically + * reallocated while adding. This feature was left for backward compat= ibility and is + * not encouraged for production usage. + */ + +options_catalog * +allocateOptionsCatalog(char *namespace, int size_of_bytea, int num_ite= ms_expected) +{ +=09MemoryContext oldcxt; +=09options_catalog *catalog; + +=09oldcxt =3D MemoryContextSwitchTo(TopMemoryContext); +=09catalog =3D palloc(sizeof(options_catalog)); +=09if (namespace) +=09{ +=09=09catalog->namespace =3D palloc(strlen(namespace) + 1); +=09=09strcpy(catalog->namespace, namespace); +=09} +=09else +=09=09catalog->namespace =3D NULL; +=09if (num_items_expected > 0) +=09{ +=09=09catalog->num_allocated =3D num_items_expected; +=09=09catalog->forbid_realloc =3D true; +=09=09catalog->definitions =3D palloc( +=09=09=09=09 catalog->num_allocated * sizeof(option_definition_basic *= )); +=09} +=09else +=09{ +=09=09catalog->num_allocated =3D 0; +=09=09catalog->forbid_realloc =3D false; +=09=09catalog->definitions =3D NULL; +=09} +=09catalog->num =3D 0; +=09catalog->struct_size =3D size_of_bytea; +=09catalog->postprocess_fun =3D NULL; +=09MemoryContextSwitchTo(oldcxt); +=09return catalog; +} + +/* + * optionCatalogAddItem + *=09=09Add an already-created option definition to the catalog + */ +static void +optionCatalogAddItem(option_definition_basic * newoption, +=09=09=09=09=09 options_catalog * catalog) +{ +=09if (catalog->num >=3D catalog->num_allocated) +=09{ +=09=09MemoryContext oldcxt; + +=09=09Assert(!catalog->forbid_realloc); +=09=09oldcxt =3D MemoryContextSwitchTo(TopMemoryContext); + +=09=09if (catalog->num_allocated =3D=3D 0) +=09=09{ +=09=09=09catalog->num_allocated =3D 8; +=09=09=09catalog->definitions =3D palloc( +=09=09=09=09 catalog->num_allocated * sizeof(option_definition_basic *= )); +=09=09} +=09=09else +=09=09{ +=09=09=09catalog->num_allocated *=3D 2; +=09=09=09catalog->definitions =3D repalloc(catalog->definitions, +=09=09=09=09 catalog->num_allocated * sizeof(option_definition_basic *= )); +=09=09} +=09=09MemoryContextSwitchTo(oldcxt); +=09} +=09catalog->definitions[catalog->num] =3D newoption; +=09catalog->num++; +} + +/* + * allocateOptionDefinition + *=09=09Allocate a new option definition and initialize the type-agnos= tic + *=09=09fields + */ +static option_definition_basic * +allocateOptionDefinition(int type, char *name, char *desc, LOCKMODE lo= ckmode, +=09=09=09=09=09=09 option_definition_flags flags, int struct_offset) +{ +=09MemoryContext oldcxt; +=09size_t=09=09size; +=09option_definition_basic *newoption; + +=09oldcxt =3D MemoryContextSwitchTo(TopMemoryContext); + +=09switch (type) +=09{ +=09=09case OPTION_TYPE_BOOL: +=09=09=09size =3D sizeof(option_definition_bool); +=09=09=09break; +=09=09case OPTION_TYPE_INT: +=09=09=09size =3D sizeof(option_definition_int); +=09=09=09break; +=09=09case OPTION_TYPE_REAL: +=09=09=09size =3D sizeof(option_definition_real); +=09=09=09break; +=09=09case OPTION_TYPE_ENUM: +=09=09=09size =3D sizeof(option_definition_enum); +=09=09=09break; +=09=09case OPTION_TYPE_STRING: +=09=09=09size =3D sizeof(option_definition_string); +=09=09=09break; +=09=09default: +=09=09=09elog(ERROR, "unsupported reloption type %d", type); +=09=09=09return NULL;=09=09/* keep compiler quiet */ +=09} + +=09newoption =3D palloc(size); + +=09newoption->name =3D pstrdup(name); +=09if (desc) +=09=09newoption->desc =3D pstrdup(desc); +=09else +=09=09newoption->desc =3D NULL; +=09newoption->type =3D type; +=09newoption->lockmode =3D lockmode; +=09newoption->flags =3D flags; +=09newoption->struct_offset =3D struct_offset; + +=09MemoryContextSwitchTo(oldcxt); + +=09return newoption; +} + +/* + * optionsCatalogAddItemBool + *=09=09Add a new boolean option definition to the catalog + */ +void +optionsCatalogAddItemBool(options_catalog * catalog, char *name, char = *desc, +=09=09=09=09=09=09 LOCKMODE lockmode, option_definition_flags flags, +=09=09=09=09=09=09 int struct_offset, bool default_val) +{ +=09option_definition_bool *catalog_item; + +=09catalog_item =3D (option_definition_bool *) +=09=09allocateOptionDefinition(OPTION_TYPE_BOOL, name, desc, lockmode,= +=09=09=09=09=09=09=09=09 flags, struct_offset); + +=09catalog_item->default_val =3D default_val; + +=09optionCatalogAddItem((option_definition_basic *) catalog_item, cata= log); +} + +/* + * optionsCatalogAddItemInt + *=09=09Add a new integer option definition to the catalog + */ +void +optionsCatalogAddItemInt(options_catalog * catalog, char *name, +=09=09=09=09char *desc, LOCKMODE lockmode, option_definition_flags fla= gs, +=09=09=09=09int struct_offset, int default_val, int min_val, int max_v= al) +{ +=09option_definition_int *catalog_item; + +=09catalog_item =3D (option_definition_int *) +=09=09allocateOptionDefinition(OPTION_TYPE_INT, name, desc, lockmode, +=09=09=09=09=09=09=09=09 flags, struct_offset); + +=09catalog_item->default_val =3D default_val; +=09catalog_item->min =3D min_val; +=09catalog_item->max =3D max_val; + +=09optionCatalogAddItem((option_definition_basic *) catalog_item, cata= log); +} + +/* + * optionsCatalogAddItemReal + *=09=09Add a new float option to the catalog + */ +void +optionsCatalogAddItemReal(options_catalog * catalog, char *name, char = *desc, +=09=09 LOCKMODE lockmode, option_definition_flags flags, int struct_of= fset, +=09=09=09=09=09=09 double default_val, double min_val, double max_val= ) +{ +=09option_definition_real *catalog_item; + +=09catalog_item =3D (option_definition_real *) +=09=09allocateOptionDefinition(OPTION_TYPE_REAL, name, desc, lockmode,= +=09=09=09=09=09=09=09=09 flags, struct_offset); + +=09catalog_item->default_val =3D default_val; +=09catalog_item->min =3D min_val; +=09catalog_item->max =3D max_val; + +=09optionCatalogAddItem((option_definition_basic *) catalog_item, cata= log); +} + +/* + * optionsCatalogAddItemEnum + *=09=09Add a new enum option to the catalog + * + * "allowed_values" is a pointer to a NULL-terminated char* array (las= t item of + * an array should be null). This array contains a list of acceptable = values + * for the option. + * + * "default_val" is a number of item in allowed_values array that shou= ld be + * set as default value. If you want to handle "option was not set" sp= ecial + * case, you can set default_val to -1 + */ +void +optionsCatalogAddItemEnum(options_catalog * catalog, char *name, char = *desc, +=09=09 LOCKMODE lockmode, option_definition_flags flags, int struct_of= fset, +=09=09=09=09=09=09 const char **allowed_values, int default_val) +{ +=09option_definition_enum *catalog_item; + +=09catalog_item =3D (option_definition_enum *) +=09=09allocateOptionDefinition(OPTION_TYPE_ENUM, name, desc, lockmode,= +=09=09=09=09=09=09=09=09 flags, struct_offset); + +=09catalog_item->default_val =3D default_val; +=09catalog_item->allowed_values =3D allowed_values; + +=09optionCatalogAddItem((option_definition_basic *) catalog_item, cata= log); +} + +/* + * optionsCatalogAddItemString + *=09=09Add a new string option definition to the catalog + * + * "validator" is an optional function pointer that can be used to tes= t the + * validity of the values. It must elog(ERROR) when the argument stri= ng is + * not acceptable for the variable. Note that the default value must = pass + * the validation. + */ +void +optionsCatalogAddItemString(options_catalog * catalog, char *name, cha= r *desc, +=09=09 LOCKMODE lockmode, option_definition_flags flags, int struct_of= fset, +=09=09=09=09=09=09 char *default_val, validate_string_relopt validator= ) +{ +=09option_definition_string *catalog_item; + +=09/* make sure the validator/default combination is sane */ +=09if (validator) +=09=09(validator) (default_val); + +=09catalog_item =3D (option_definition_string *) +=09=09allocateOptionDefinition(OPTION_TYPE_STRING, name, desc, lockmod= e, +=09=09=09=09=09=09=09=09 flags, struct_offset); +=09catalog_item->validate_cb =3D validator; + +=09if (default_val) +=09=09catalog_item->default_val =3D MemoryContextStrdup(TopMemoryConte= xt, +=09=09=09=09=09=09=09=09=09=09=09=09=09=09default_val); +=09else +=09=09catalog_item->default_val =3D NULL; +=09optionCatalogAddItem((option_definition_basic *) catalog_item, cata= log); +} + + +/* + * Options transform functions + */ + +/* + * Option values exists in five representations: DefList, TextArray, V= alues and + * Bytea: + * + * DefList: Is a List of DefElem structures, that comes from syntax an= alyzer. + * It can be transformed to Values representation for further parsing = and + * validating + * + * Values: A List of option_value structures. Is divided into two subc= lasses: + * RawValues, when values are already transformed from DefList or Text= Array, + * but not parsed yet. (In this case you should use raw_name and raw_v= alue + * structure members to see option content). ParsedValues (or just sim= ple + * Values) is crated after finding a definition for this option in a c= atalog + * and after parsing of the raw value. For ParsedValues content is sto= red in + * values structure member, and name can be taken from option definiti= on in gen + * structure member. Actually Value list can have both Raw and Parsed= values, + * as we do not validate options that came from database, and db optio= n that + * does not exist in catalog is just ignored, and kept as RawValues + * + * TextArray: The representation in which options for existing object= comes + * and goes from/to database; for example from pg_class.reloptions. It= is a + * plain TEXT[] db object with name=3Dvalue text inside. This represen= tation can + * be transformed into Values for further processing, using options ca= talog. + * + * Bytea: Is a binary representation of options. Each object that has = code that + * uses options, should create a C-structure for this options, with va= rlen + * 4-byte header in front of the data; all items of options catalog sh= ould have + * an offset of a corresponding binary data in this structure, so tran= sform + * function can put this data in the correct place. One can transform = options + * data from values representation into Bytea, using catalog data, and= then use + * it as a usual Datum object, when needed. This Datum should be cache= d + * somewhere (for example in rel->rd_options for relations) when objec= t that + * has option is loaded from db. + */ + + +/* optionsDefListToRawValues + *=09=09Converts option values that came from syntax analyzer (DefList= ) into + *=09=09Values List. + * + * No parsing is done here except for checking that RESET syntax is co= rrect + * (syntax analyzer do not see difference between SET and RESET cases,= we + * should treat it here manually + */ +static List * +optionsDefListToRawValues(List *defList, options_parse_mode parse_mode= ) +{ +=09ListCell *cell; +=09List=09 *result =3D NIL; + +=09foreach(cell, defList) +=09{ +=09=09option_value *option_dst; +=09=09DefElem *def =3D (DefElem *) lfirst(cell); +=09=09char=09 *value; + +=09=09option_dst =3D palloc(sizeof(option_value)); + +=09=09if (def->defnamespace) +=09=09{ +=09=09=09option_dst->namespace =3D palloc(strlen(def->defnamespace) + = 1); +=09=09=09strcpy(option_dst->namespace, def->defnamespace); +=09=09} +=09=09else +=09=09{ +=09=09=09option_dst->namespace =3D NULL; +=09=09} +=09=09option_dst->raw_name =3D palloc(strlen(def->defname) + 1); +=09=09strcpy(option_dst->raw_name, def->defname); + +=09=09if (parse_mode & OPTIONS_PARSE_MODE_FOR_RESET) +=09=09{ +=09=09=09/* +=09=09=09 * If this option came from RESET statement we should throw e= rror +=09=09=09 * it it brings us name=3Dvalue data, as syntax analyzer do n= ot +=09=09=09 * prevent it +=09=09=09 */ +=09=09=09if (def->arg !=3D NULL) +=09=09=09=09ereport(ERROR, +=09=09=09=09=09=09(errcode(ERRCODE_SYNTAX_ERROR), +=09=09=09=09=09errmsg("RESET must not include values for parameters"))= ); + +=09=09=09option_dst->status =3D OPTION_VALUE_STATUS_FOR_RESET; +=09=09} +=09=09else +=09=09{ +=09=09=09/* +=09=09=09 * For SET statement we should treat (name) expression as if = it is +=09=09=09 * actually (name=3Dtrue) so do it here manually. In other ca= ses +=09=09=09 * just use value as we should use it +=09=09=09 */ +=09=09=09option_dst->status =3D OPTION_VALUE_STATUS_RAW; +=09=09=09if (def->arg !=3D NULL) +=09=09=09=09value =3D defGetString(def); +=09=09=09else +=09=09=09=09value =3D "true"; +=09=09=09option_dst->raw_value =3D palloc(strlen(value) + 1); +=09=09=09strcpy(option_dst->raw_value, value); +=09=09} + +=09=09result =3D lappend(result, option_dst); +=09} +=09return result; +} + +/* + * optionsValuesToTextArray + *=09=09Converts List of option_values into TextArray + * + *=09Convertation is made to put options into database (e.g. in + *=09pg_class.reloptions for all relation options) + */ + +Datum +optionsValuesToTextArray(List *options_values) +{ +=09ArrayBuildState *astate =3D NULL; +=09ListCell *cell; +=09Datum=09=09result; + +=09foreach(cell, options_values) +=09{ +=09=09option_value *option =3D (option_value *) lfirst(cell); +=09=09const char *name; +=09=09char=09 *value; +=09=09text=09 *t; +=09=09int=09=09=09len; + +=09=09/* +=09=09 * Raw value were not cleared while parsing, so instead of conve= rting +=09=09 * it back, just use it to store value as text +=09=09 */ +=09=09value =3D option->raw_value; + +=09=09Assert(option->status !=3D OPTION_VALUE_STATUS_EMPTY); + +=09=09/* +=09=09 * Name will be taken from option definition, if option were par= sed or +=09=09 * from raw_name if option were not parsed for some reason +=09=09 */ +=09=09if (option->status =3D=3D OPTION_VALUE_STATUS_PARSED) +=09=09=09name =3D option->gen->name; +=09=09else +=09=09=09name =3D option->raw_name; + +=09=09/* +=09=09 * Now build "name=3Dvalue" string and append it to the array +=09=09 */ +=09=09len =3D VARHDRSZ + strlen(name) + strlen(value) + 1; +=09=09t =3D (text *) palloc(len + 1); +=09=09SET_VARSIZE(t, len); +=09=09sprintf(VARDATA(t), "%s=3D%s", name, value); +=09=09astate =3D accumArrayResult(astate, PointerGetDatum(t), false, +=09=09=09=09=09=09=09=09 TEXTOID, CurrentMemoryContext); +=09} +=09if (astate) +=09=09result =3D makeArrayResult(astate, CurrentMemoryContext); +=09else +=09=09result =3D (Datum) 0; + +=09return result; +} + +/* + * optionsTextArrayToRawValues + *=09=09Converts options from TextArray format into RawValues list. + * + *=09This function is used to convert options data that comes from dat= abase to + *=09List of option_values, for further parsing, and, in the case of A= LTER + *=09command, for merging with new option values. + */ +static List * +optionsTextArrayToRawValues(Datum array_datum) +{ +=09List=09 *result =3D NIL; + +=09if (PointerIsValid(DatumGetPointer(array_datum))) +=09{ +=09=09ArrayType *array =3D DatumGetArrayTypeP(array_datum); +=09=09Datum=09 *options; +=09=09int=09=09=09noptions; +=09=09int=09=09=09i; + +=09=09deconstruct_array(array, TEXTOID, -1, false, 'i', +=09=09=09=09=09=09 &options, NULL, &noptions); + +=09=09for (i =3D 0; i < noptions; i++) +=09=09{ +=09=09=09option_value *option_dst; +=09=09=09char=09 *text_str =3D VARDATA(options[i]); +=09=09=09int=09=09=09text_len =3D VARSIZE(options[i]) - VARHDRSZ; +=09=09=09int=09=09=09i; +=09=09=09int=09=09=09name_len =3D -1; +=09=09=09char=09 *name; +=09=09=09int=09=09=09raw_value_len; +=09=09=09char=09 *raw_value; + +=09=09=09/* +=09=09=09 * Find position of '=3D' sign and treat id as a separator be= tween +=09=09=09 * name and value in "name=3Dvalue" item +=09=09=09 */ +=09=09=09for (i =3D 0; i < text_len; i =3D i + pg_mblen(text_str)) +=09=09=09{ +=09=09=09=09if (text_str[i] =3D=3D '=3D') +=09=09=09=09{ +=09=09=09=09=09name_len =3D i; +=09=09=09=09=09break; +=09=09=09=09} +=09=09=09} +=09=09=09Assert(name_len >=3D 1);=09=09/* Just in case */ + +=09=09=09raw_value_len =3D text_len - name_len - 1; + +=09=09=09/* +=09=09=09 * Copy name from src +=09=09=09 */ +=09=09=09name =3D palloc(name_len + 1); +=09=09=09memcpy(name, text_str, name_len); +=09=09=09name[name_len] =3D '\0'; + +=09=09=09/* +=09=09=09 * Copy value from src +=09=09=09 */ +=09=09=09raw_value =3D palloc(raw_value_len + 1); +=09=09=09memcpy(raw_value, text_str + name_len + 1, raw_value_len); +=09=09=09raw_value[raw_value_len] =3D '\0'; + +=09=09=09/* +=09=09=09 * Create new option_value item +=09=09=09 */ +=09=09=09option_dst =3D palloc(sizeof(option_value)); +=09=09=09option_dst->status =3D OPTION_VALUE_STATUS_RAW; +=09=09=09option_dst->raw_name =3D name; +=09=09=09option_dst->raw_value =3D raw_value; + +=09=09=09result =3D lappend(result, option_dst); +=09=09} +=09} +=09return result; +} + +/* + * optionsMergeOptionValues + *=09=09Merges 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 w= ith new + * options list. This function also process RESET variant of ALTER com= mand. It + * merges two lists as usual, and then removes all items with RESET fl= ag on. + * + * Both incoming lists will be destroyed while merging + */ +static List * +optionsMergeOptionValues(List *old_options, List *new_options) +{ +=09List=09 *result =3D NIL; +=09ListCell *old_cell; +=09ListCell *old_prev; +=09ListCell *old_next; +=09ListCell *new_cell; +=09ListCell *new_prev; +=09ListCell *new_next; + +=09old_prev =3D NULL; + +=09/* +=09 * First try to remove from old options list all values that exists= in a +=09 * new options list +=09 */ +=09for (old_cell =3D list_head(old_options); old_cell; old_cell =3D ol= d_next) +=09{ +=09=09bool=09=09found; +=09=09const char *old_name; +=09=09option_value *old_option; + +=09=09old_next =3D lnext(old_cell); +=09=09old_option =3D (option_value *) lfirst(old_cell); +=09=09if (old_option->status =3D=3D OPTION_VALUE_STATUS_PARSED) +=09=09=09old_name =3D old_option->gen->name; +=09=09else +=09=09=09old_name =3D old_option->raw_name; + +=09=09/* +=09=09 * Try to find old_name option among new option +=09=09 */ +=09=09found =3D false; +=09=09foreach(new_cell, new_options) +=09=09{ +=09=09=09option_value *new_option; +=09=09=09const char *new_name; + +=09=09=09new_option =3D (option_value *) lfirst(new_cell); +=09=09=09if (new_option->status =3D=3D OPTION_VALUE_STATUS_PARSED) +=09=09=09=09new_name =3D new_option->gen->name; +=09=09=09else +=09=09=09=09new_name =3D new_option->raw_name; +=09=09=09if (pg_strcasecmp(new_name, old_name) =3D=3D 0) +=09=09=09{ +=09=09=09=09found =3D true; +=09=09=09=09break; +=09=09=09} +=09=09} + +=09=09/* +=09=09 * If found, delete old option from the list +=09=09 */ +=09=09if (found) +=09=09{ +=09=09=09old_options =3D list_delete_cell(old_options, old_cell, old_p= rev); +=09=09} +=09=09else +=09=09{ +=09=09=09old_prev =3D old_cell; +=09=09} +=09} + +=09/* +=09 * Remove from new_options all options that are for RESET. In old l= ist all +=09 * this options were already removed in previous block, so all RESE= T +=09 * options are cleaned now +=09 */ +=09new_prev =3D NULL; +=09for (new_cell =3D list_head(new_options); new_cell; new_cell =3D ne= w_next) +=09{ +=09=09option_value *new_option =3D (option_value *) lfirst(new_cell); + +=09=09new_next =3D lnext(new_cell); + +=09=09if (new_option->status =3D=3D OPTION_VALUE_STATUS_FOR_RESET) +=09=09=09new_options =3D list_delete_cell(new_options, new_cell, new_p= rev); +=09=09else +=09=09=09new_prev =3D new_cell; +=09} + +=09/* +=09 * Now merge what remained of both lists +=09 */ +=09result =3D list_concat(old_options, new_options); +=09return result; +} + +/* + * optionsDefListValdateNamespaces + *=09=09Function checks that all options represented as DefList has no= + *=09=09namespaces or have namespaces only from allowed list + * + * Function accept options as DefList and NULL terminated list of allo= wed + * namespaces. It throws an error if not proper namespace was found. + * + * This function actually used only for tables with it's toast. namesp= ace + */ +void +optionsDefListValdateNamespaces(List *defList, char **allowed_namespac= es) +{ +=09ListCell *cell; + +=09foreach(cell, defList) +=09{ +=09=09DefElem *def =3D (DefElem *) lfirst(cell); + +=09=09/* +=09=09 * Checking namespace only for options that have namespaces. Opt= ions +=09=09 * with no namespaces are always accepted +=09=09 */ +=09=09if (def->defnamespace) +=09=09{ +=09=09=09bool=09=09found =3D false; +=09=09=09int=09=09=09i =3D 0; + +=09=09=09while (allowed_namespaces[i]) +=09=09=09{ +=09=09=09=09if (pg_strcasecmp(def->defnamespace, +=09=09=09=09=09=09=09=09 allowed_namespaces[i]) =3D=3D 0) +=09=09=09=09{ +=09=09=09=09=09found =3D true; +=09=09=09=09=09break; +=09=09=09=09} +=09=09=09=09i++; +=09=09=09} +=09=09=09if (!found) +=09=09=09=09ereport(ERROR, +=09=09=09=09=09=09(errcode(ERRCODE_INVALID_PARAMETER_VALUE), +=09=09=09=09=09=09 errmsg("unrecognized parameter namespace \"%s\"", +=09=09=09=09=09=09=09=09def->defnamespace))); +=09=09} +=09} +} + +/* + * optionsDefListFilterNamespaces + *=09=09Iterates over DefList, choose items with specified namespace a= nd adds + *=09=09them to a result List + * + * This function does not destroy source DefList but does not create c= opies + * of List nodes. + * It is actually used only for tables, in order to split toast and he= ap + * reloptions, so each one can be stored in on it's own pg_class recor= d + */ +List * +optionsDefListFilterNamespaces(List *defList, char *namespace) +{ +=09ListCell *cell; +=09List=09 *result =3D NIL; + +=09foreach(cell, defList) +=09{ +=09=09DefElem *def =3D (DefElem *) lfirst(cell); + +=09=09if ((!namespace && !def->defnamespace) || +=09=09=09(namespace && def->defnamespace && +=09=09=09 pg_strcasecmp(namespace, def->defnamespace) =3D=3D 0)) +=09=09{ +=09=09=09result =3D lappend(result, def); +=09=09} +=09} +=09return result; +} + +/* + * optionsTextArrayToDefList + *=09=09Convert the text-array format of reloptions into a List of Def= Elem. + */ +List * +optionsTextArrayToDefList(Datum options) +{ +=09List=09 *result =3D NIL; +=09ArrayType *array; +=09Datum=09 *optiondatums; +=09int=09=09=09noptions; +=09int=09=09=09i; + +=09/* Nothing to do if no options */ +=09if (!PointerIsValid(DatumGetPointer(options))) +=09=09return result; + +=09array =3D DatumGetArrayTypeP(options); + +=09deconstruct_array(array, TEXTOID, -1, false, 'i', +=09=09=09=09=09 &optiondatums, NULL, &noptions); + +=09for (i =3D 0; i < noptions; i++) +=09{ +=09=09char=09 *s; +=09=09char=09 *p; +=09=09Node=09 *val =3D NULL; + +=09=09s =3D TextDatumGetCString(optiondatums[i]); +=09=09p =3D strchr(s, '=3D'); +=09=09if (p) +=09=09{ +=09=09=09*p++ =3D '\0'; +=09=09=09val =3D (Node *) makeString(pstrdup(p)); +=09=09} +=09=09result =3D lappend(result, makeDefElem(pstrdup(s), val, -1)); +=09} + +=09return result; +} + +/* + * optionsParseRawValues + *=09=09Parses and vlaidates (if proper flag is set) option_values. As= a result + *=09=09caller will get the list of parsed (or partly parsed) option_v= alues + * + * This function is used in cases when caller gets raw values from db = or + * syntax and want to parse them. + * This function uses option_catalog to get information about how each= option + * should be parsed. + * If validate mode is off, function found an option that do not have = proper + * option_catalog entry, this option kept unparsed (if some garbage ca= me from + * the DB, we should put it back there) + * + * This function destroys incoming list. + */ +static List * +optionsParseRawValues(List *raw_values, options_catalog * catalog, +=09=09=09=09=09 options_parse_mode mode) +{ +=09ListCell *cell; +=09List=09 *result =3D NIL; +=09bool=09 *is_set; +=09int=09=09=09i; +=09bool=09=09validate =3D mode & OPTIONS_PARSE_MODE_VALIDATE; +=09bool=09=09for_alter =3D mode & OPTIONS_PARSE_MODE_FOR_ALTER; + + +=09is_set =3D palloc0(sizeof(bool) * catalog->num); +=09foreach(cell, raw_values) +=09{ +=09=09option_value *option =3D (option_value *) lfirst(cell); +=09=09bool=09=09found =3D false; +=09=09bool=09=09skip =3D false; + + +=09=09if (option->status =3D=3D OPTION_VALUE_STATUS_PARSED) +=09=09{ +=09=09=09/* +=09=09=09 * This can happen while ALTER, when new values were already +=09=09=09 * parsed, but old values merged from DB are still raw +=09=09=09 */ +=09=09=09result =3D lappend(result, option); +=09=09=09continue; +=09=09} +=09=09if (validate && option->namespace && (!catalog->namespace || +=09=09=09=09 pg_strcasecmp(catalog->namespace, option->namespace) !=3D= 0)) +=09=09{ +=09=09=09ereport(ERROR, +=09=09=09=09=09(errcode(ERRCODE_INVALID_PARAMETER_VALUE), +=09=09=09=09=09 errmsg("unrecognized parameter namespace \"%s\"", +=09=09=09=09=09=09=09option->namespace))); +=09=09} + +=09=09for (i =3D 0; i < catalog->num; i++) +=09=09{ +=09=09=09option_definition_basic *definition =3D catalog->definitions[= i]; + +=09=09=09if (pg_strcasecmp(option->raw_name, +=09=09=09=09=09=09=09 definition->name) =3D=3D 0) +=09=09=09{ +=09=09=09=09/* +=09=09=09=09 * Skip option with "ignore" flag, as it is processed +=09=09=09=09 * somewhere else. (WITH OIDS special case) +=09=09=09=09 */ +=09=09=09=09if (definition->flags & OPTION_DEFINITION_FLAG_IGNORE) +=09=09=09=09{ +=09=09=09=09=09found =3D true; +=09=09=09=09=09skip =3D true; +=09=09=09=09=09break; +=09=09=09=09} + +=09=09=09=09/* +=09=09=09=09 * Reject option as if it was not in catalog. Needed for c= ases +=09=09=09=09 * when option should have default value, but should not b= e +=09=09=09=09 * changed +=09=09=09=09 */ +=09=09=09=09if (definition->flags & OPTION_DEFINITION_FLAG_REJECT) +=09=09=09=09{ +=09=09=09=09=09found =3D false; +=09=09=09=09=09break; +=09=09=09=09} + +=09=09=09=09if (validate && is_set[i]) +=09=09=09=09{ +=09=09=09=09=09ereport(ERROR, +=09=09=09=09=09=09=09(errcode(ERRCODE_INVALID_PARAMETER_VALUE), +=09=09=09=09=09=09 errmsg("parameter \"%s\" specified more than once"= , +=09=09=09=09=09=09=09=09 option->raw_name))); +=09=09=09=09} +=09=09=09=09if ((for_alter) && +=09=09=09=09=09(definition->flags & OPTION_DEFINITION_FLAG_FORBID_ALTE= R)) +=09=09=09=09{ +=09=09=09=09=09ereport(ERROR, +=09=09=09=09=09=09=09(errcode(ERRCODE_INVALID_PARAMETER_VALUE), +=09=09=09=09=09=09 errmsg("changing parameter \"%s\" is not allowed"= , +=09=09=09=09=09=09=09=09 definition->name))); +=09=09=09=09} +=09=09=09=09if (option->status =3D=3D OPTION_VALUE_STATUS_FOR_RESET) +=09=09=09=09{ +=09=09=09=09=09/* +=09=09=09=09=09 * For RESET options do not need further processing so +=09=09=09=09=09 * mark it found and stop searching +=09=09=09=09=09 */ +=09=09=09=09=09found =3D true; +=09=09=09=09=09break; +=09=09=09=09} +=09=09=09=09pfree(option->raw_name); +=09=09=09=09option->raw_name =3D NULL; +=09=09=09=09option->gen =3D definition; +=09=09=09=09parse_one_option(option, NULL, -1, validate); +=09=09=09=09is_set[i] =3D true; +=09=09=09=09found =3D true; +=09=09=09=09break; +=09=09=09} +=09=09} +=09=09if (!found) +=09=09{ +=09=09=09if (validate) +=09=09=09{ +=09=09=09=09if (option->namespace) +=09=09=09=09=09ereport(ERROR, +=09=09=09=09=09=09=09(errcode(ERRCODE_INVALID_PARAMETER_VALUE), +=09=09=09=09=09=09=09 errmsg("unrecognized parameter \"%s.%s\"", +=09=09=09=09=09=09=09=09=09option->namespace, option->raw_name))); +=09=09=09=09else +=09=09=09=09=09ereport(ERROR, +=09=09=09=09=09=09=09(errcode(ERRCODE_INVALID_PARAMETER_VALUE), +=09=09=09=09=09=09=09 errmsg("unrecognized parameter \"%s\"", +=09=09=09=09=09=09=09=09=09option->raw_name))); +=09=09=09} + +=09=09=09/* +=09=09=09 * If we are parsing not in validate mode, then we should kee= p +=09=09=09 * unknown node, because non-validate mode is for data that i= s +=09=09=09 * already in the DB and should not be changed after altering= +=09=09=09 * another entries +=09=09=09 */ +=09=09} +=09=09if (!skip) +=09=09=09result =3D lappend(result, option); +=09} +=09return result; +} + +/* + * parse_one_option + * + *=09=09Subroutine for optionsParseRawValues, to parse and validate a + *=09=09single option's value + */ +static void +parse_one_option(option_value * option, char *text_str, int text_len, +=09=09=09=09 bool validate) +{ +=09char=09 *value; +=09bool=09=09parsed; + +=09value =3D option->raw_value; + +=09switch (option->gen->type) +=09{ +=09=09case OPTION_TYPE_BOOL: +=09=09=09{ +=09=09=09=09parsed =3D parse_bool(value, &option->values.bool_val); +=09=09=09=09if (validate && !parsed) +=09=09=09=09=09ereport(ERROR, +=09=09=09=09=09=09=09(errcode(ERRCODE_INVALID_PARAMETER_VALUE), +=09=09=09=09=09=09errmsg("invalid value for boolean option \"%s\": %s"= , +=09=09=09=09=09=09=09 option->gen->name, value))); +=09=09=09} +=09=09=09break; +=09=09case OPTION_TYPE_INT: +=09=09=09{ +=09=09=09=09option_definition_int *optint =3D +=09=09=09=09(option_definition_int *) option->gen; + +=09=09=09=09parsed =3D parse_int(value, &option->values.int_val, 0, NU= LL); +=09=09=09=09if (validate && !parsed) +=09=09=09=09=09ereport(ERROR, +=09=09=09=09=09=09=09(errcode(ERRCODE_INVALID_PARAMETER_VALUE), +=09=09=09=09=09=09errmsg("invalid value for integer option \"%s\": %s"= , +=09=09=09=09=09=09=09 option->gen->name, value))); +=09=09=09=09if (validate && (option->values.int_val < optint->min || +=09=09=09=09=09=09=09=09 option->values.int_val > optint->max)) +=09=09=09=09=09ereport(ERROR, +=09=09=09=09=09=09=09(errcode(ERRCODE_INVALID_PARAMETER_VALUE), +=09=09=09=09=09=09 errmsg("value %s out of bounds for option \"%s\""= , +=09=09=09=09=09=09=09=09 value, option->gen->name), +=09=09=09=09=09 errdetail("Valid values are between \"%d\" and \"%d\".= ", +=09=09=09=09=09=09=09 optint->min, optint->max))); +=09=09=09} +=09=09=09break; +=09=09case OPTION_TYPE_REAL: +=09=09=09{ +=09=09=09=09option_definition_real *optreal =3D +=09=09=09=09(option_definition_real *) option->gen; + +=09=09=09=09parsed =3D parse_real(value, &option->values.real_val); +=09=09=09=09if (validate && !parsed) +=09=09=09=09=09ereport(ERROR, +=09=09=09=09=09=09=09(errcode(ERRCODE_INVALID_PARAMETER_VALUE), +=09=09=09=09=09=09=09 errmsg("invalid value for floating point option = \"%s\": %s", +=09=09=09=09=09=09=09=09=09option->gen->name, value))); +=09=09=09=09if (validate && (option->values.real_val < optreal->min ||= +=09=09=09=09=09=09=09=09 option->values.real_val > optreal->max)) +=09=09=09=09=09ereport(ERROR, +=09=09=09=09=09=09=09(errcode(ERRCODE_INVALID_PARAMETER_VALUE), +=09=09=09=09=09=09 errmsg("value %s out of bounds for option \"%s\""= , +=09=09=09=09=09=09=09=09 value, option->gen->name), +=09=09=09=09=09 errdetail("Valid values are between \"%f\" and \"%f\".= ", +=09=09=09=09=09=09=09 optreal->min, optreal->max))); +=09=09=09} +=09=09=09break; +=09=09case OPTION_TYPE_ENUM: +=09=09=09{ +=09=09=09=09option_definition_enum *opt_enum =3D +=09=09=09=09(option_definition_enum *) option->gen; +=09=09=09=09int=09=09=09i =3D 0; + +=09=09=09=09parsed =3D false; +=09=09=09=09while (opt_enum->allowed_values[i]) +=09=09=09=09{ +=09=09=09=09=09if (pg_strcasecmp(value, opt_enum->allowed_values[i]) =3D= =3D 0) +=09=09=09=09=09{ +=09=09=09=09=09=09option->values.enum_val =3D i; +=09=09=09=09=09=09parsed =3D true; +=09=09=09=09=09=09break; +=09=09=09=09=09} +=09=09=09=09=09i++; +=09=09=09=09} +=09=09=09=09if (!parsed) +=09=09=09=09{ +=09=09=09=09=09int=09=09=09length =3D 0; +=09=09=09=09=09char=09 *str; +=09=09=09=09=09char=09 *ptr; + +=09=09=09=09=09/* +=09=09=09=09=09 * Generating list of allowed values: "value1", "value2= ", +=09=09=09=09=09 * ... "valueN" +=09=09=09=09=09 */ +=09=09=09=09=09i =3D 0; +=09=09=09=09=09while (opt_enum->allowed_values[i]) +=09=09=09=09=09{ +=09=09=09=09=09=09length +=3D strlen(opt_enum->allowed_values[i]) + 4;= +=09=09=09=09=09=09/* +4: two quotes, one comma, one space */ +=09=09=09=09=09=09i++; +=09=09=09=09=09} + +=09=09=09=09=09/* +=09=09=09=09=09 * one byte not used for comma after the last item will= be +=09=09=09=09=09 * used for \0; for another byte will do -1 +=09=09=09=09=09 */ +=09=09=09=09=09str =3D palloc((length - 1) * sizeof(char)); +=09=09=09=09=09i =3D 0; +=09=09=09=09=09ptr =3D str; +=09=09=09=09=09while (opt_enum->allowed_values[i]) +=09=09=09=09=09{ +=09=09=09=09=09=09if (i !=3D 0) +=09=09=09=09=09=09{ +=09=09=09=09=09=09=09ptr[0] =3D ','; +=09=09=09=09=09=09=09ptr[1] =3D ' '; +=09=09=09=09=09=09=09ptr +=3D 2; +=09=09=09=09=09=09} +=09=09=09=09=09=09ptr[0] =3D '"'; +=09=09=09=09=09=09ptr++; +=09=09=09=09=09=09sprintf(ptr, "%s", opt_enum->allowed_values[i]); +=09=09=09=09=09=09ptr +=3D strlen(ptr); +=09=09=09=09=09=09ptr[0] =3D '"'; +=09=09=09=09=09=09ptr++; +=09=09=09=09=09=09i++; +=09=09=09=09=09} +=09=09=09=09=09*ptr =3D '\0'; + +=09=09=09=09=09ereport(ERROR, +=09=09=09=09=09=09=09(errcode(ERRCODE_INVALID_PARAMETER_VALUE), +=09=09=09=09=09=09=09 errmsg("invalid value for \"%s\" option", +=09=09=09=09=09=09=09=09=09option->gen->name), +=09=09=09=09=09=09=09 errdetail("Valid values are %s.", str))); +=09=09=09=09} +=09=09=09} +=09=09=09break; +=09=09case OPTION_TYPE_STRING: +=09=09=09{ +=09=09=09=09option_definition_string *optstring =3D +=09=09=09=09(option_definition_string *) option->gen; + +=09=09=09=09option->values.string_val =3D value; +=09=09=09=09if (validate && optstring->validate_cb) +=09=09=09=09=09(optstring->validate_cb) (value); +=09=09=09=09parsed =3D true; +=09=09=09} +=09=09=09break; +=09=09default: +=09=09=09elog(ERROR, "unsupported reloption type %d", option->gen->typ= e); +=09=09=09parsed =3D true;=09=09/* quiet compiler */ +=09=09=09break; +=09} + +=09if (parsed) +=09=09option->status =3D OPTION_VALUE_STATUS_PARSED; + +} + +/* + * optionsAllocateBytea + *=09=09Allocates memory for bytea options representation + * + * Function allocates memory for byrea structure of an option, plus ad= ds space + * for values of string options. We should keep all data including str= ing + * values in the same memory chunk, because Cache code copies bytea op= tion + * data from one MemoryConext to another without knowing about it's in= ternal + * structure, so it would not be able to copy string values if they ar= e outside + * of bytea memory chunk. + */ +static void * +optionsAllocateBytea(options_catalog * catalog, List *options) +{ +=09Size=09=09size; +=09int=09=09=09i; +=09ListCell *cell; +=09int=09=09=09length; +=09void=09 *res; + +=09size =3D catalog->struct_size; + +=09/* Calculate size needed to store all string values for this option= */ +=09for (i =3D 0; i < catalog->num; i++) +=09{ +=09=09option_definition_basic *definition =3D catalog->definitions[i];= +=09=09bool=09=09found =3D false; +=09=09option_value *option; + +=09=09/* Not interested in non-string options, skipping */ +=09=09if (definition->type !=3D OPTION_TYPE_STRING) +=09=09=09continue; + +=09=09/* +=09=09 * Trying to find option_value that references definition catalo= g +=09=09 * entry +=09=09 */ +=09=09foreach(cell, options) +=09=09{ +=09=09=09option =3D (option_value *) lfirst(cell); +=09=09=09if (option->status =3D=3D OPTION_VALUE_STATUS_PARSED && +=09=09=09=09pg_strcasecmp(option->gen->name, definition->name) =3D=3D = 0) +=09=09=09{ +=09=09=09=09found =3D true; +=09=09=09=09break; +=09=09=09} +=09=09} +=09=09if (found) +=09=09=09/* If found, it'value will be stored */ +=09=09=09length =3D strlen(option->values.string_val) + 1; +=09=09else +=09=09=09/* If not found, then there would be default value there */ +=09=09if (((option_definition_string *) definition)->default_val) +=09=09=09length =3D strlen( +=09=09=09=09 ((option_definition_string *) definition)->default_val) += 1; +=09=09else +=09=09=09length =3D 0; +=09=09/* Add total length of all string values to basic size */ +=09=09size +=3D length; +=09} + +=09res =3D palloc0(size); +=09SET_VARSIZE(res, size); +=09return res; +} + +/* + * optionsValuesToBytea + *=09=09Converts options from List of option_values to binary bytea st= ructure + * + * 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 a= llocated + * by optionsAllocateBytea which adds some more space for string value= s to + * the size of original structure. All string values are copied there = and + * inside the bytea structure an offset to that value is kept. + * + */ +static bytea * +optionsValuesToBytea(List *options, options_catalog * catalog) +{ +=09char=09 *data; +=09char=09 *string_values_buffer; +=09int=09=09=09i; + +=09data =3D optionsAllocateBytea(catalog, options); + +=09/* place for string data starts right after original structure */ +=09string_values_buffer =3D data + catalog->struct_size; + +=09for (i =3D 0; i < catalog->num; i++) +=09{ +=09=09option_value *found =3D NULL; +=09=09ListCell *cell; +=09=09char=09 *item_pos; +=09=09option_definition_basic *definition =3D catalog->definitions[i];= + +=09=09if (definition->flags & OPTION_DEFINITION_FLAG_IGNORE) +=09=09=09continue; + +=09=09/* Calculate the position of the item inside the structure */ +=09=09item_pos =3D data + definition->struct_offset; + +=09=09/* Looking for the corresponding option from options list */ +=09=09foreach(cell, options) +=09=09{ +=09=09=09option_value *option =3D (option_value *) lfirst(cell); + +=09=09=09if (option->status =3D=3D OPTION_VALUE_STATUS_RAW) +=09=09=09=09continue;=09=09/* raw can come from db. Just ignore them t= hen */ +=09=09=09Assert(option->status !=3D OPTION_VALUE_STATUS_EMPTY); + +=09=09=09if (pg_strcasecmp(definition->name, option->gen->name) =3D=3D= 0) +=09=09=09{ +=09=09=09=09found =3D option; +=09=09=09=09break; +=09=09=09} +=09=09} +=09=09/* writing to the proper position either option value or default= val */ +=09=09switch (definition->type) +=09=09{ +=09=09=09case OPTION_TYPE_BOOL: +=09=09=09=09*(bool *) item_pos =3D found ? +=09=09=09=09=09found->values.bool_val : +=09=09=09=09=09((option_definition_bool *) definition)->default_val; +=09=09=09=09break; +=09=09=09case OPTION_TYPE_INT: +=09=09=09=09*(int *) item_pos =3D found ? +=09=09=09=09=09found->values.int_val : +=09=09=09=09=09((option_definition_int *) definition)->default_val; +=09=09=09=09break; +=09=09=09case OPTION_TYPE_REAL: +=09=09=09=09*(double *) item_pos =3D found ? +=09=09=09=09=09found->values.real_val : +=09=09=09=09=09((option_definition_real *) definition)->default_val; +=09=09=09=09break; +=09=09=09case OPTION_TYPE_ENUM: +=09=09=09=09*(int *) item_pos =3D found ? +=09=09=09=09=09found->values.enum_val : +=09=09=09=09=09((option_definition_enum *) definition)->default_val; +=09=09=09=09break; + +=09=09=09case OPTION_TYPE_STRING: +=09=09=09=09{ +=09=09=09=09=09/* +=09=09=09=09=09 * For string options: writing string value at the stri= ng +=09=09=09=09=09 * buffer after the structure, and storing and offset t= o +=09=09=09=09=09 * that value +=09=09=09=09=09 */ +=09=09=09=09=09char=09 *value =3D NULL; + +=09=09=09=09=09if (found) +=09=09=09=09=09=09value =3D found->values.string_val; +=09=09=09=09=09else +=09=09=09=09=09=09value =3D ((option_definition_string *) definition) +=09=09=09=09=09=09=09->default_val; +=09=09=09=09=09*(int *) item_pos =3D value ? +=09=09=09=09=09=09string_values_buffer - data : +=09=09=09=09=09=09OPTION_STRING_VALUE_NOT_SET_OFFSET; +=09=09=09=09=09if (value) +=09=09=09=09=09{ +=09=09=09=09=09=09strcpy(string_values_buffer, value); +=09=09=09=09=09=09string_values_buffer +=3D strlen(value) + 1; +=09=09=09=09=09} +=09=09=09=09} +=09=09=09=09break; +=09=09=09default: +=09=09=09=09elog(ERROR, "unsupported reloption type %d", +=09=09=09=09=09 definition->type); +=09=09=09=09break; +=09=09} +=09} +=09return (void *) data; +} + + +/* + * transformOptions + *=09=09This function is used by src/backend/commands/Xxxx in order to= process + *=09=09new option values, merge them with existing values (in the cas= e of + *=09=09ALTER command) and prepare to put them [back] into DB + */ + +Datum +transformOptions(options_catalog * catalog, Datum oldOptions, +=09=09=09=09 List *defList, options_parse_mode parse_mode) +{ +=09Datum=09=09result; +=09List=09 *new_values; +=09List=09 *old_values; +=09List=09 *merged_values; + +=09/* +=09 * Parse and validate New values +=09 */ +=09new_values =3D optionsDefListToRawValues(defList, parse_mode); +=09new_values =3D optionsParseRawValues(new_values, catalog, +=09=09=09=09=09=09=09=09 parse_mode | OPTIONS_PARSE_MODE_VALIDATE); + +=09/* +=09 * Old values exists in case of ALTER commands. Transform them to r= aw +=09 * values and merge them with new_values, and parse it. +=09 */ +=09if (PointerIsValid(DatumGetPointer(oldOptions))) +=09{ +=09=09old_values =3D optionsTextArrayToRawValues(oldOptions); +=09=09merged_values =3D optionsMergeOptionValues(old_values, new_value= s); + +=09=09/* +=09=09 * Parse options only after merging in order not to parse option= s that +=09=09 * would be removed by merging later +=09=09 */ +=09=09merged_values =3D optionsParseRawValues(merged_values, catalog, = 0); +=09} +=09else +=09{ +=09=09merged_values =3D new_values; +=09} + +=09/* +=09 * If we have postprocess_fun function defined in catalog, then the= re +=09 * might be some custom options checks there, with error throwing. = So we +=09 * should do it here to throw these errors while CREATing or ALTERi= ng +=09 * options +=09 */ +=09if (catalog->postprocess_fun) +=09{ +=09=09bytea=09 *data =3D optionsValuesToBytea(merged_values, catalog= ); + +=09=09catalog->postprocess_fun(data, true); +=09=09pfree(data); +=09} + +=09/* +=09 * Convert options to TextArray format so caller can store them int= o +=09 * database +=09 */ +=09result =3D optionsValuesToTextArray(merged_values); +=09return result; +} + + +/* + * optionsTextArrayToBytea + *=09=09A meta-function that transforms options stored as TextArray in= to binary + *=09=09(bytea) representation. + * + *=09This function runs other transform functions that leads to the de= sired + *=09result in no-validation mode. This function is used by cache mech= anism, + *=09in order to load and cache options when object itself is loaded a= nd cached + */ +bytea * +optionsTextArrayToBytea(options_catalog * catalog, Datum data) +{ +=09List=09 *values; +=09bytea=09 *options; + +=09values =3D optionsTextArrayToRawValues(data); +=09values =3D optionsParseRawValues(values, catalog, 0); +=09options =3D optionsValuesToBytea(values, catalog); + +=09if (catalog->postprocess_fun) +=09{ +=09=09catalog->postprocess_fun(options, false); +=09} +=09return options; +} diff --git a/src/backend/access/common/reloptions.c b/src/backend/acces= s/common/reloptions.c index ec10762..19816d5 100644 =2D-- a/src/backend/access/common/reloptions.c +++ b/src/backend/access/common/reloptions.c @@ -1,7 +1,10 @@ /*--------------------------------------------------------------------= =2D---- * * reloptions.c =2D *=09 Core support for relation options (pg_class.reloptions) + *=09 Support for options relotions (pg_class.reloptions). + *=09 Reloptions for non-Access Metod relations are defined here. + *=09 There is also extractRelOptions function to extract reloptions + *=09 from pg_class table for all kind of relations. * * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Gro= up * Portions Copyright (c) 1994, Regents of the University of Californi= a @@ -17,928 +20,16 @@ =20 #include =20 =2D#include "access/gist_private.h" =2D#include "access/hash.h" #include "access/htup_details.h" =2D#include "access/nbtree.h" +#include "access/options.h" #include "access/reloptions.h" =2D#include "access/spgist.h" =2D#include "catalog/pg_type.h" =2D#include "commands/defrem.h" #include "commands/tablespace.h" =2D#include "commands/view.h" =2D#include "nodes/makefuncs.h" =2D#include "postmaster/postmaster.h" =2D#include "utils/array.h" #include "utils/attoptcache.h" =2D#include "utils/builtins.h" =2D#include "utils/guc.h" =2D#include "utils/memutils.h" #include "utils/rel.h" +#include "storage/bufmgr.h" =20 =2D/* =2D * Contents of pg_class.reloptions =2D * =2D * To add an option: =2D * =2D * (i) decide on a type (integer, real, bool, string), name, default v= alue, =2D * upper and lower bounds (if applicable); for strings, consider a val= idation =2D * routine. =2D * (ii) add a record below (or use add__reloption). =2D * (iii) add it to the appropriate options struct (perhaps StdRdOption= s) =2D * (iv) add it to the appropriate handling routine (perhaps =2D * default_reloptions) =2D * (v) make sure the lock level is set correctly for that operation =2D * (vi) don't forget to document the option =2D * =2D * Note that we don't handle "oids" in relOpts because it is handled b= y =2D * interpretOidsOption(). =2D * =2D * The default choice for any new option should be AccessExclusiveLock= . =2D * In some cases the lock level can be reduced from there, but the loc= k =2D * level chosen should always conflict with itself to ensure that mult= iple =2D * changes aren't lost when we attempt concurrent changes. =2D * The choice of lock level depends completely upon how that parameter= =2D * is used within the server, not upon how and when you'd like to chan= ge it. =2D * Safety first. Existing choices are documented here, and elsewhere i= n =2D * backend code where the parameters are used. =2D * =2D * In general, anything that affects the results obtained from a SELEC= T must be =2D * protected by AccessExclusiveLock. =2D * =2D * Autovacuum related parameters can be set at ShareUpdateExclusiveLoc= k =2D * since they are only used by the AV procs and don't change anything =2D * currently executing. =2D * =2D * Fillfactor can be set because it applies only to subsequent changes= made to =2D * data blocks, as documented in heapio.c =2D * =2D * n_distinct options can be set at ShareUpdateExclusiveLock because t= hey =2D * are only used during ANALYZE, which uses a ShareUpdateExclusiveLock= , =2D * so the ANALYZE will not be affected by in-flight changes. Changing = those =2D * values has no affect until the next ANALYZE, so no need for stronge= r lock. =2D * =2D * Planner-related parameters can be set with ShareUpdateExclusiveLock= because =2D * they only affect planning and not the correctness of the execution.= Plans =2D * cannot be changed in mid-flight, so changes here could not easily r= esult in =2D * new improved plans in any case. So we allow existing queries to con= tinue =2D * and existing plans to survive, a small price to pay for allowing be= tter =2D * plans to be introduced concurrently without interfering with users.= =2D * =2D * Setting parallel_workers is safe, since it acts the same as =2D * max_parallel_workers_per_gather which is a USERSET parameter that d= oesn't =2D * affect existing plans or queries. =2D */ =2D =2Dstatic relopt_bool boolRelOpts[] =3D =2D{ =2D=09{ =2D=09=09{ =2D=09=09=09"autosummarize", =2D=09=09=09"Enables automatic summarization on this BRIN index", =2D=09=09=09RELOPT_KIND_BRIN, =2D=09=09=09AccessExclusiveLock =2D=09=09}, =2D=09=09false =2D=09}, =2D=09{ =2D=09=09{ =2D=09=09=09"autovacuum_enabled", =2D=09=09=09"Enables autovacuum in this relation", =2D=09=09=09RELOPT_KIND_HEAP | RELOPT_KIND_TOAST, =2D=09=09=09ShareUpdateExclusiveLock =2D=09=09}, =2D=09=09true =2D=09}, =2D=09{ =2D=09=09{ =2D=09=09=09"user_catalog_table", =2D=09=09=09"Declare a table as an additional catalog table, e.g. for the= purpose of logical replication", =2D=09=09=09RELOPT_KIND_HEAP, =2D=09=09=09AccessExclusiveLock =2D=09=09}, =2D=09=09false =2D=09}, =2D=09{ =2D=09=09{ =2D=09=09=09"fastupdate", =2D=09=09=09"Enables \"fast update\" feature for this GIN index", =2D=09=09=09RELOPT_KIND_GIN, =2D=09=09=09AccessExclusiveLock =2D=09=09}, =2D=09=09true =2D=09}, =2D=09{ =2D=09=09{ =2D=09=09=09"security_barrier", =2D=09=09=09"View acts as a row security barrier", =2D=09=09=09RELOPT_KIND_VIEW, =2D=09=09=09AccessExclusiveLock =2D=09=09}, =2D=09=09false =2D=09}, =2D=09/* list terminator */ =2D=09{{NULL}} =2D}; =2D =2Dstatic relopt_int intRelOpts[] =3D =2D{ =2D=09{ =2D=09=09{ =2D=09=09=09"fillfactor", =2D=09=09=09"Packs table pages only to this percentage", =2D=09=09=09RELOPT_KIND_HEAP, =2D=09=09=09ShareUpdateExclusiveLock=09/* since it applies only to later =2D=09=09=09=09=09=09=09=09=09=09 * inserts */ =2D=09=09}, =2D=09=09HEAP_DEFAULT_FILLFACTOR, HEAP_MIN_FILLFACTOR, 100 =2D=09}, =2D=09{ =2D=09=09{ =2D=09=09=09"fillfactor", =2D=09=09=09"Packs btree index pages only to this percentage", =2D=09=09=09RELOPT_KIND_BTREE, =2D=09=09=09ShareUpdateExclusiveLock=09/* since it applies only to later =2D=09=09=09=09=09=09=09=09=09=09 * inserts */ =2D=09=09}, =2D=09=09BTREE_DEFAULT_FILLFACTOR, BTREE_MIN_FILLFACTOR, 100 =2D=09}, =2D=09{ =2D=09=09{ =2D=09=09=09"fillfactor", =2D=09=09=09"Packs hash index pages only to this percentage", =2D=09=09=09RELOPT_KIND_HASH, =2D=09=09=09ShareUpdateExclusiveLock=09/* since it applies only to later =2D=09=09=09=09=09=09=09=09=09=09 * inserts */ =2D=09=09}, =2D=09=09HASH_DEFAULT_FILLFACTOR, HASH_MIN_FILLFACTOR, 100 =2D=09}, =2D=09{ =2D=09=09{ =2D=09=09=09"fillfactor", =2D=09=09=09"Packs gist index pages only to this percentage", =2D=09=09=09RELOPT_KIND_GIST, =2D=09=09=09ShareUpdateExclusiveLock=09/* since it applies only to later =2D=09=09=09=09=09=09=09=09=09=09 * inserts */ =2D=09=09}, =2D=09=09GIST_DEFAULT_FILLFACTOR, GIST_MIN_FILLFACTOR, 100 =2D=09}, =2D=09{ =2D=09=09{ =2D=09=09=09"fillfactor", =2D=09=09=09"Packs spgist index pages only to this percentage", =2D=09=09=09RELOPT_KIND_SPGIST, =2D=09=09=09ShareUpdateExclusiveLock=09/* since it applies only to later =2D=09=09=09=09=09=09=09=09=09=09 * inserts */ =2D=09=09}, =2D=09=09SPGIST_DEFAULT_FILLFACTOR, SPGIST_MIN_FILLFACTOR, 100 =2D=09}, =2D=09{ =2D=09=09{ =2D=09=09=09"autovacuum_vacuum_threshold", =2D=09=09=09"Minimum number of tuple updates or deletes prior to vacuum",= =2D=09=09=09RELOPT_KIND_HEAP | RELOPT_KIND_TOAST, =2D=09=09=09ShareUpdateExclusiveLock =2D=09=09}, =2D=09=09-1, 0, INT_MAX =2D=09}, =2D=09{ =2D=09=09{ =2D=09=09=09"autovacuum_analyze_threshold", =2D=09=09=09"Minimum number of tuple inserts, updates or deletes prior to= analyze", =2D=09=09=09RELOPT_KIND_HEAP, =2D=09=09=09ShareUpdateExclusiveLock =2D=09=09}, =2D=09=09-1, 0, INT_MAX =2D=09}, =2D=09{ =2D=09=09{ =2D=09=09=09"autovacuum_vacuum_cost_delay", =2D=09=09=09"Vacuum cost delay in milliseconds, for autovacuum", =2D=09=09=09RELOPT_KIND_HEAP | RELOPT_KIND_TOAST, =2D=09=09=09ShareUpdateExclusiveLock =2D=09=09}, =2D=09=09-1, 0, 100 =2D=09}, =2D=09{ =2D=09=09{ =2D=09=09=09"autovacuum_vacuum_cost_limit", =2D=09=09=09"Vacuum cost amount available before napping, for autovacuum"= , =2D=09=09=09RELOPT_KIND_HEAP | RELOPT_KIND_TOAST, =2D=09=09=09ShareUpdateExclusiveLock =2D=09=09}, =2D=09=09-1, 1, 10000 =2D=09}, =2D=09{ =2D=09=09{ =2D=09=09=09"autovacuum_freeze_min_age", =2D=09=09=09"Minimum age at which VACUUM should freeze a table row, for a= utovacuum", =2D=09=09=09RELOPT_KIND_HEAP | RELOPT_KIND_TOAST, =2D=09=09=09ShareUpdateExclusiveLock =2D=09=09}, =2D=09=09-1, 0, 1000000000 =2D=09}, =2D=09{ =2D=09=09{ =2D=09=09=09"autovacuum_multixact_freeze_min_age", =2D=09=09=09"Minimum multixact age at which VACUUM should freeze a row mu= ltixact's, for autovacuum", =2D=09=09=09RELOPT_KIND_HEAP | RELOPT_KIND_TOAST, =2D=09=09=09ShareUpdateExclusiveLock =2D=09=09}, =2D=09=09-1, 0, 1000000000 =2D=09}, =2D=09{ =2D=09=09{ =2D=09=09=09"autovacuum_freeze_max_age", =2D=09=09=09"Age at which to autovacuum a table to prevent transaction ID= wraparound", =2D=09=09=09RELOPT_KIND_HEAP | RELOPT_KIND_TOAST, =2D=09=09=09ShareUpdateExclusiveLock =2D=09=09}, =2D=09=09-1, 100000, 2000000000 =2D=09}, =2D=09{ =2D=09=09{ =2D=09=09=09"autovacuum_multixact_freeze_max_age", =2D=09=09=09"Multixact age at which to autovacuum a table to prevent mult= ixact wraparound", =2D=09=09=09RELOPT_KIND_HEAP | RELOPT_KIND_TOAST, =2D=09=09=09ShareUpdateExclusiveLock =2D=09=09}, =2D=09=09-1, 10000, 2000000000 =2D=09}, =2D=09{ =2D=09=09{ =2D=09=09=09"autovacuum_freeze_table_age", =2D=09=09=09"Age at which VACUUM should perform a full table sweep to fre= eze row versions", =2D=09=09=09RELOPT_KIND_HEAP | RELOPT_KIND_TOAST, =2D=09=09=09ShareUpdateExclusiveLock =2D=09=09}, -1, 0, 2000000000 =2D=09}, =2D=09{ =2D=09=09{ =2D=09=09=09"autovacuum_multixact_freeze_table_age", =2D=09=09=09"Age of multixact at which VACUUM should perform a full table= sweep to freeze row versions", =2D=09=09=09RELOPT_KIND_HEAP | RELOPT_KIND_TOAST, =2D=09=09=09ShareUpdateExclusiveLock =2D=09=09}, -1, 0, 2000000000 =2D=09}, =2D=09{ =2D=09=09{ =2D=09=09=09"log_autovacuum_min_duration", =2D=09=09=09"Sets the minimum execution time above which autovacuum actio= ns will be logged", =2D=09=09=09RELOPT_KIND_HEAP | RELOPT_KIND_TOAST, =2D=09=09=09ShareUpdateExclusiveLock =2D=09=09}, =2D=09=09-1, -1, INT_MAX =2D=09}, =2D=09{ =2D=09=09{ =2D=09=09=09"pages_per_range", =2D=09=09=09"Number of pages that each page range covers in a BRIN index"= , =2D=09=09=09RELOPT_KIND_BRIN, =2D=09=09=09AccessExclusiveLock =2D=09=09}, 128, 1, 131072 =2D=09}, =2D=09{ =2D=09=09{ =2D=09=09=09"gin_pending_list_limit", =2D=09=09=09"Maximum size of the pending list for this GIN index, in kilo= bytes.", =2D=09=09=09RELOPT_KIND_GIN, =2D=09=09=09AccessExclusiveLock =2D=09=09}, =2D=09=09-1, 64, MAX_KILOBYTES =2D=09}, =2D=09{ =2D=09=09{ =2D=09=09=09"effective_io_concurrency", =2D=09=09=09"Number of simultaneous requests that can be handled efficien= tly by the disk subsystem.", =2D=09=09=09RELOPT_KIND_TABLESPACE, =2D=09=09=09ShareUpdateExclusiveLock =2D=09=09}, =2D#ifdef USE_PREFETCH =2D=09=09-1, 0, MAX_IO_CONCURRENCY =2D#else =2D=09=090, 0, 0 =2D#endif =2D=09}, =2D=09{ =2D=09=09{ =2D=09=09=09"parallel_workers", =2D=09=09=09"Number of parallel processes that can be used per executor n= ode for this relation.", =2D=09=09=09RELOPT_KIND_HEAP, =2D=09=09=09ShareUpdateExclusiveLock =2D=09=09}, =2D=09=09-1, 0, 1024 =2D=09}, =2D =2D=09/* list terminator */ =2D=09{{NULL}} =2D}; =2D =2Dstatic relopt_real realRelOpts[] =3D =2D{ =2D=09{ =2D=09=09{ =2D=09=09=09"autovacuum_vacuum_scale_factor", =2D=09=09=09"Number of tuple updates or deletes prior to vacuum as a frac= tion of reltuples", =2D=09=09=09RELOPT_KIND_HEAP | RELOPT_KIND_TOAST, =2D=09=09=09ShareUpdateExclusiveLock =2D=09=09}, =2D=09=09-1, 0.0, 100.0 =2D=09}, =2D=09{ =2D=09=09{ =2D=09=09=09"autovacuum_analyze_scale_factor", =2D=09=09=09"Number of tuple inserts, updates or deletes prior to analyze= as a fraction of reltuples", =2D=09=09=09RELOPT_KIND_HEAP, =2D=09=09=09ShareUpdateExclusiveLock =2D=09=09}, =2D=09=09-1, 0.0, 100.0 =2D=09}, =2D=09{ =2D=09=09{ =2D=09=09=09"seq_page_cost", =2D=09=09=09"Sets the planner's estimate of the cost of a sequentially fe= tched disk page.", =2D=09=09=09RELOPT_KIND_TABLESPACE, =2D=09=09=09ShareUpdateExclusiveLock =2D=09=09}, =2D=09=09-1, 0.0, DBL_MAX =2D=09}, =2D=09{ =2D=09=09{ =2D=09=09=09"random_page_cost", =2D=09=09=09"Sets the planner's estimate of the cost of a nonsequentially= fetched disk page.", =2D=09=09=09RELOPT_KIND_TABLESPACE, =2D=09=09=09ShareUpdateExclusiveLock =2D=09=09}, =2D=09=09-1, 0.0, DBL_MAX =2D=09}, =2D=09{ =2D=09=09{ =2D=09=09=09"n_distinct", =2D=09=09=09"Sets the planner's estimate of the number of distinct values= appearing in a column (excluding child relations).", =2D=09=09=09RELOPT_KIND_ATTRIBUTE, =2D=09=09=09ShareUpdateExclusiveLock =2D=09=09}, =2D=09=090, -1.0, DBL_MAX =2D=09}, =2D=09{ =2D=09=09{ =2D=09=09=09"n_distinct_inherited", =2D=09=09=09"Sets the planner's estimate of the number of distinct values= appearing in a column (including child relations).", =2D=09=09=09RELOPT_KIND_ATTRIBUTE, =2D=09=09=09ShareUpdateExclusiveLock =2D=09=09}, =2D=09=090, -1.0, DBL_MAX =2D=09}, =2D=09/* list terminator */ =2D=09{{NULL}} =2D}; =2D =2Dstatic relopt_string stringRelOpts[] =3D =2D{ =2D=09{ =2D=09=09{ =2D=09=09=09"buffering", =2D=09=09=09"Enables buffering build for this GiST index", =2D=09=09=09RELOPT_KIND_GIST, =2D=09=09=09AccessExclusiveLock =2D=09=09}, =2D=09=094, =2D=09=09false, =2D=09=09gistValidateBufferingOption, =2D=09=09"auto" =2D=09}, =2D=09{ =2D=09=09{ =2D=09=09=09"check_option", =2D=09=09=09"View has WITH CHECK OPTION defined (local or cascaded).", =2D=09=09=09RELOPT_KIND_VIEW, =2D=09=09=09AccessExclusiveLock =2D=09=09}, =2D=09=090, =2D=09=09true, =2D=09=09validateWithCheckOption, =2D=09=09NULL =2D=09}, =2D=09/* list terminator */ =2D=09{{NULL}} =2D}; =2D =2Dstatic relopt_gen **relOpts =3D NULL; =2Dstatic bits32 last_assigned_kind =3D RELOPT_KIND_LAST_DEFAULT; =2D =2Dstatic int=09num_custom_options =3D 0; =2Dstatic relopt_gen **custom_options =3D NULL; =2Dstatic bool need_initialization =3D true; =2D =2Dstatic void initialize_reloptions(void); =2Dstatic void parse_one_reloption(relopt_value *option, char *text_str, =2D=09=09=09=09=09int text_len, bool validate); =2D =2D/* =2D * initialize_reloptions =2D *=09=09initialization routine, must be called before parsing =2D * =2D * Initialize the relOpts array and fill each variable's type and name= length. =2D */ =2Dstatic void =2Dinitialize_reloptions(void) =2D{ =2D=09int=09=09=09i; =2D=09int=09=09=09j; =2D =2D=09j =3D 0; =2D=09for (i =3D 0; boolRelOpts[i].gen.name; i++) =2D=09{ =2D=09=09Assert(DoLockModesConflict(boolRelOpts[i].gen.lockmode, =2D=09=09=09=09=09=09=09=09 boolRelOpts[i].gen.lockmode)); =2D=09=09j++; =2D=09} =2D=09for (i =3D 0; intRelOpts[i].gen.name; i++) =2D=09{ =2D=09=09Assert(DoLockModesConflict(intRelOpts[i].gen.lockmode, =2D=09=09=09=09=09=09=09=09 intRelOpts[i].gen.lockmode)); =2D=09=09j++; =2D=09} =2D=09for (i =3D 0; realRelOpts[i].gen.name; i++) =2D=09{ =2D=09=09Assert(DoLockModesConflict(realRelOpts[i].gen.lockmode, =2D=09=09=09=09=09=09=09=09 realRelOpts[i].gen.lockmode)); =2D=09=09j++; =2D=09} =2D=09for (i =3D 0; stringRelOpts[i].gen.name; i++) =2D=09{ =2D=09=09Assert(DoLockModesConflict(stringRelOpts[i].gen.lockmode, =2D=09=09=09=09=09=09=09=09 stringRelOpts[i].gen.lockmode)); =2D=09=09j++; =2D=09} =2D=09j +=3D num_custom_options; =2D =2D=09if (relOpts) =2D=09=09pfree(relOpts); =2D=09relOpts =3D MemoryContextAlloc(TopMemoryContext, =2D=09=09=09=09=09=09=09=09 (j + 1) * sizeof(relopt_gen *)); =2D =2D=09j =3D 0; =2D=09for (i =3D 0; boolRelOpts[i].gen.name; i++) =2D=09{ =2D=09=09relOpts[j] =3D &boolRelOpts[i].gen; =2D=09=09relOpts[j]->type =3D RELOPT_TYPE_BOOL; =2D=09=09relOpts[j]->namelen =3D strlen(relOpts[j]->name); =2D=09=09j++; =2D=09} =2D =2D=09for (i =3D 0; intRelOpts[i].gen.name; i++) =2D=09{ =2D=09=09relOpts[j] =3D &intRelOpts[i].gen; =2D=09=09relOpts[j]->type =3D RELOPT_TYPE_INT; =2D=09=09relOpts[j]->namelen =3D strlen(relOpts[j]->name); =2D=09=09j++; =2D=09} =2D =2D=09for (i =3D 0; realRelOpts[i].gen.name; i++) =2D=09{ =2D=09=09relOpts[j] =3D &realRelOpts[i].gen; =2D=09=09relOpts[j]->type =3D RELOPT_TYPE_REAL; =2D=09=09relOpts[j]->namelen =3D strlen(relOpts[j]->name); =2D=09=09j++; =2D=09} =2D =2D=09for (i =3D 0; stringRelOpts[i].gen.name; i++) =2D=09{ =2D=09=09relOpts[j] =3D &stringRelOpts[i].gen; =2D=09=09relOpts[j]->type =3D RELOPT_TYPE_STRING; =2D=09=09relOpts[j]->namelen =3D strlen(relOpts[j]->name); =2D=09=09j++; =2D=09} =2D =2D=09for (i =3D 0; i < num_custom_options; i++) =2D=09{ =2D=09=09relOpts[j] =3D custom_options[i]; =2D=09=09j++; =2D=09} =2D =2D=09/* add a list terminator */ =2D=09relOpts[j] =3D NULL; =2D =2D=09/* flag the work is complete */ =2D=09need_initialization =3D false; =2D} =2D =2D/* =2D * add_reloption_kind =2D *=09=09Create a new relopt_kind value, to be used in custom reloption= s by =2D *=09=09user-defined AMs. =2D */ =2Drelopt_kind =2Dadd_reloption_kind(void) =2D{ =2D=09/* don't hand out the last bit so that the enum's behavior is porta= ble */ =2D=09if (last_assigned_kind >=3D RELOPT_KIND_MAX) =2D=09=09ereport(ERROR, =2D=09=09=09=09(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), =2D=09=09=09=09 errmsg("user-defined relation parameter types limit excee= ded"))); =2D=09last_assigned_kind <<=3D 1; =2D=09return (relopt_kind) last_assigned_kind; =2D} =2D =2D/* =2D * add_reloption =2D *=09=09Add an already-created custom reloption to the list, and recom= pute the =2D *=09=09main parser table. =2D */ =2Dstatic void =2Dadd_reloption(relopt_gen *newoption) =2D{ =2D=09static int=09max_custom_options =3D 0; =2D =2D=09if (num_custom_options >=3D max_custom_options) =2D=09{ =2D=09=09MemoryContext oldcxt; =2D =2D=09=09oldcxt =3D MemoryContextSwitchTo(TopMemoryContext); =2D =2D=09=09if (max_custom_options =3D=3D 0) =2D=09=09{ =2D=09=09=09max_custom_options =3D 8; =2D=09=09=09custom_options =3D palloc(max_custom_options * sizeof(relopt_= gen *)); =2D=09=09} =2D=09=09else =2D=09=09{ =2D=09=09=09max_custom_options *=3D 2; =2D=09=09=09custom_options =3D repalloc(custom_options, =2D=09=09=09=09=09=09=09=09=09 max_custom_options * sizeof(relopt_gen *)= ); =2D=09=09} =2D=09=09MemoryContextSwitchTo(oldcxt); =2D=09} =2D=09custom_options[num_custom_options++] =3D newoption; =2D =2D=09need_initialization =3D true; =2D} =2D =2D/* =2D * allocate_reloption =2D *=09=09Allocate a new reloption and initialize the type-agnostic fiel= ds =2D *=09=09(for types other than string) =2D */ =2Dstatic relopt_gen * =2Dallocate_reloption(bits32 kinds, int type, char *name, char *desc) =2D{ =2D=09MemoryContext oldcxt; =2D=09size_t=09=09size; =2D=09relopt_gen *newoption; =2D =2D=09oldcxt =3D MemoryContextSwitchTo(TopMemoryContext); =2D =2D=09switch (type) =2D=09{ =2D=09=09case RELOPT_TYPE_BOOL: =2D=09=09=09size =3D sizeof(relopt_bool); =2D=09=09=09break; =2D=09=09case RELOPT_TYPE_INT: =2D=09=09=09size =3D sizeof(relopt_int); =2D=09=09=09break; =2D=09=09case RELOPT_TYPE_REAL: =2D=09=09=09size =3D sizeof(relopt_real); =2D=09=09=09break; =2D=09=09case RELOPT_TYPE_STRING: =2D=09=09=09size =3D sizeof(relopt_string); =2D=09=09=09break; =2D=09=09default: =2D=09=09=09elog(ERROR, "unsupported reloption type %d", type); =2D=09=09=09return NULL;=09=09/* keep compiler quiet */ =2D=09} =2D =2D=09newoption =3D palloc(size); =2D =2D=09newoption->name =3D pstrdup(name); =2D=09if (desc) =2D=09=09newoption->desc =3D pstrdup(desc); =2D=09else =2D=09=09newoption->desc =3D NULL; =2D=09newoption->kinds =3D kinds; =2D=09newoption->namelen =3D strlen(name); =2D=09newoption->type =3D type; =2D =2D=09MemoryContextSwitchTo(oldcxt); =2D =2D=09return newoption; =2D} =2D =2D/* =2D * add_bool_reloption =2D *=09=09Add a new boolean reloption =2D */ =2Dvoid =2Dadd_bool_reloption(bits32 kinds, char *name, char *desc, bool default_= val) =2D{ =2D=09relopt_bool *newoption; =2D =2D=09newoption =3D (relopt_bool *) allocate_reloption(kinds, RELOPT_TYPE= _BOOL, =2D=09=09=09=09=09=09=09=09=09=09=09=09 name, desc); =2D=09newoption->default_val =3D default_val; =2D =2D=09add_reloption((relopt_gen *) newoption); =2D} =2D =2D/* =2D * add_int_reloption =2D *=09=09Add a new integer reloption =2D */ =2Dvoid =2Dadd_int_reloption(bits32 kinds, char *name, char *desc, int default_va= l, =2D=09=09=09=09 int min_val, int max_val) =2D{ =2D=09relopt_int *newoption; =2D =2D=09newoption =3D (relopt_int *) allocate_reloption(kinds, RELOPT_TYPE_= INT, =2D=09=09=09=09=09=09=09=09=09=09=09=09 name, desc); =2D=09newoption->default_val =3D default_val; =2D=09newoption->min =3D min_val; =2D=09newoption->max =3D max_val; =2D =2D=09add_reloption((relopt_gen *) newoption); =2D} =2D =2D/* =2D * add_real_reloption =2D *=09=09Add a new float reloption =2D */ =2Dvoid =2Dadd_real_reloption(bits32 kinds, char *name, char *desc, double defaul= t_val, =2D=09=09=09=09 double min_val, double max_val) =2D{ =2D=09relopt_real *newoption; =2D =2D=09newoption =3D (relopt_real *) allocate_reloption(kinds, RELOPT_TYPE= _REAL, =2D=09=09=09=09=09=09=09=09=09=09=09=09 name, desc); =2D=09newoption->default_val =3D default_val; =2D=09newoption->min =3D min_val; =2D=09newoption->max =3D max_val; =2D =2D=09add_reloption((relopt_gen *) newoption); =2D} =2D =2D/* =2D * add_string_reloption =2D *=09=09Add a new string reloption =2D * =2D * "validator" is an optional function pointer that can be used to tes= t the =2D * validity of the values. It must elog(ERROR) when the argument stri= ng is =2D * not acceptable for the variable. Note that the default value must = pass =2D * the validation. =2D */ =2Dvoid =2Dadd_string_reloption(bits32 kinds, char *name, char *desc, char *defau= lt_val, =2D=09=09=09=09=09 validate_string_relopt validator) =2D{ =2D=09relopt_string *newoption; =2D =2D=09/* make sure the validator/default combination is sane */ =2D=09if (validator) =2D=09=09(validator) (default_val); =2D =2D=09newoption =3D (relopt_string *) allocate_reloption(kinds, RELOPT_TY= PE_STRING, =2D=09=09=09=09=09=09=09=09=09=09=09=09=09 name, desc); =2D=09newoption->validate_cb =3D validator; =2D=09if (default_val) =2D=09{ =2D=09=09newoption->default_val =3D MemoryContextStrdup(TopMemoryContext,= =2D=09=09=09=09=09=09=09=09=09=09=09=09=09 default_val); =2D=09=09newoption->default_len =3D strlen(default_val); =2D=09=09newoption->default_isnull =3D false; =2D=09} =2D=09else =2D=09{ =2D=09=09newoption->default_val =3D ""; =2D=09=09newoption->default_len =3D 0; =2D=09=09newoption->default_isnull =3D true; =2D=09} =2D =2D=09add_reloption((relopt_gen *) newoption); =2D} =2D =2D/* =2D * Transform a relation options list (list of DefElem) into the text a= rray =2D * format that is kept in pg_class.reloptions, including only those op= tions =2D * that are in the passed namespace. The output values do not include= the =2D * namespace. =2D * =2D * This is used for three cases: CREATE TABLE/INDEX, ALTER TABLE SET, = and =2D * ALTER TABLE RESET. In the ALTER cases, oldOptions is the existing =2D * reloptions value (possibly NULL), and we replace or remove entries =2D * as needed. =2D * =2D * If ignoreOids is true, then we should ignore any occurrence of "oid= s" =2D * in the list (it will be or has been handled by interpretOidsOption(= )). =2D * =2D * Note that this is not responsible for determining whether the optio= ns =2D * are valid, but it does check that namespaces for all the options gi= ven are =2D * listed in validnsps. The NULL namespace is always valid and need n= ot be =2D * explicitly listed. Passing a NULL pointer means that only the NULL= =2D * namespace is valid. =2D * =2D * Both oldOptions and the result are text arrays (or NULL for "defaul= t"), =2D * but we declare them as Datums to avoid including array.h in relopti= ons.h. =2D */ =2DDatum =2DtransformRelOptions(Datum oldOptions, List *defList, char *namspace, =2D=09=09=09=09=09char *validnsps[], bool ignoreOids, bool isReset) =2D{ =2D=09Datum=09=09result; =2D=09ArrayBuildState *astate; =2D=09ListCell *cell; =2D =2D=09/* no change if empty list */ =2D=09if (defList =3D=3D NIL) =2D=09=09return oldOptions; =2D =2D=09/* We build new array using accumArrayResult */ =2D=09astate =3D NULL; =2D =2D=09/* Copy any oldOptions that aren't to be replaced */ =2D=09if (PointerIsValid(DatumGetPointer(oldOptions))) =2D=09{ =2D=09=09ArrayType *array =3D DatumGetArrayTypeP(oldOptions); =2D=09=09Datum=09 *oldoptions; =2D=09=09int=09=09=09noldoptions; =2D=09=09int=09=09=09i; =2D =2D=09=09deconstruct_array(array, TEXTOID, -1, false, 'i', =2D=09=09=09=09=09=09 &oldoptions, NULL, &noldoptions); =2D =2D=09=09for (i =3D 0; i < noldoptions; i++) =2D=09=09{ =2D=09=09=09char=09 *text_str =3D VARDATA(oldoptions[i]); =2D=09=09=09int=09=09=09text_len =3D VARSIZE(oldoptions[i]) - VARHDRSZ; =2D =2D=09=09=09/* Search for a match in defList */ =2D=09=09=09foreach(cell, defList) =2D=09=09=09{ =2D=09=09=09=09DefElem *def =3D (DefElem *) lfirst(cell); =2D=09=09=09=09int=09=09=09kw_len; =2D =2D=09=09=09=09/* ignore if not in the same namespace */ =2D=09=09=09=09if (namspace =3D=3D NULL) =2D=09=09=09=09{ =2D=09=09=09=09=09if (def->defnamespace !=3D NULL) =2D=09=09=09=09=09=09continue; =2D=09=09=09=09} =2D=09=09=09=09else if (def->defnamespace =3D=3D NULL) =2D=09=09=09=09=09continue; =2D=09=09=09=09else if (pg_strcasecmp(def->defnamespace, namspace) !=3D 0= ) =2D=09=09=09=09=09continue; =2D =2D=09=09=09=09kw_len =3D strlen(def->defname); =2D=09=09=09=09if (text_len > kw_len && text_str[kw_len] =3D=3D '=3D' && =2D=09=09=09=09=09pg_strncasecmp(text_str, def->defname, kw_len) =3D=3D 0= ) =2D=09=09=09=09=09break; =2D=09=09=09} =2D=09=09=09if (!cell) =2D=09=09=09{ =2D=09=09=09=09/* No match, so keep old option */ =2D=09=09=09=09astate =3D accumArrayResult(astate, oldoptions[i], =2D=09=09=09=09=09=09=09=09=09=09 false, TEXTOID, =2D=09=09=09=09=09=09=09=09=09=09 CurrentMemoryContext); =2D=09=09=09} =2D=09=09} =2D=09} =2D =2D=09/* =2D=09 * If CREATE/SET, add new options to array; if RESET, just check th= at the =2D=09 * user didn't say RESET (option=3Dval). (Must do this because the= grammar =2D=09 * doesn't enforce it.) =2D=09 */ =2D=09foreach(cell, defList) =2D=09{ =2D=09=09DefElem *def =3D (DefElem *) lfirst(cell); =2D =2D=09=09if (isReset) =2D=09=09{ =2D=09=09=09if (def->arg !=3D NULL) =2D=09=09=09=09ereport(ERROR, =2D=09=09=09=09=09=09(errcode(ERRCODE_SYNTAX_ERROR), =2D=09=09=09=09=09=09 errmsg("RESET must not include values for parameter= s"))); =2D=09=09} =2D=09=09else =2D=09=09{ =2D=09=09=09text=09 *t; =2D=09=09=09const char *value; =2D=09=09=09Size=09=09len; =2D =2D=09=09=09/* =2D=09=09=09 * Error out if the namespace is not valid. A NULL namespace= is =2D=09=09=09 * always valid. =2D=09=09=09 */ =2D=09=09=09if (def->defnamespace !=3D NULL) =2D=09=09=09{ =2D=09=09=09=09bool=09=09valid =3D false; =2D=09=09=09=09int=09=09=09i; =2D =2D=09=09=09=09if (validnsps) =2D=09=09=09=09{ =2D=09=09=09=09=09for (i =3D 0; validnsps[i]; i++) =2D=09=09=09=09=09{ =2D=09=09=09=09=09=09if (pg_strcasecmp(def->defnamespace, =2D=09=09=09=09=09=09=09=09=09=09 validnsps[i]) =3D=3D 0) =2D=09=09=09=09=09=09{ =2D=09=09=09=09=09=09=09valid =3D true; =2D=09=09=09=09=09=09=09break; =2D=09=09=09=09=09=09} =2D=09=09=09=09=09} =2D=09=09=09=09} =2D =2D=09=09=09=09if (!valid) =2D=09=09=09=09=09ereport(ERROR, =2D=09=09=09=09=09=09=09(errcode(ERRCODE_INVALID_PARAMETER_VALUE), =2D=09=09=09=09=09=09=09 errmsg("unrecognized parameter namespace \"%s\""= , =2D=09=09=09=09=09=09=09=09=09def->defnamespace))); =2D=09=09=09} =2D =2D=09=09=09if (ignoreOids && pg_strcasecmp(def->defname, "oids") =3D=3D = 0) =2D=09=09=09=09continue; =2D =2D=09=09=09/* ignore if not in the same namespace */ =2D=09=09=09if (namspace =3D=3D NULL) =2D=09=09=09{ =2D=09=09=09=09if (def->defnamespace !=3D NULL) =2D=09=09=09=09=09continue; =2D=09=09=09} =2D=09=09=09else if (def->defnamespace =3D=3D NULL) =2D=09=09=09=09continue; =2D=09=09=09else if (pg_strcasecmp(def->defnamespace, namspace) !=3D 0) =2D=09=09=09=09continue; =2D =2D=09=09=09/* =2D=09=09=09 * Flatten the DefElem into a text string like "name=3Darg". = If we =2D=09=09=09 * have just "name", assume "name=3Dtrue" is meant. Note: th= e =2D=09=09=09 * namespace is not output. =2D=09=09=09 */ =2D=09=09=09if (def->arg !=3D NULL) =2D=09=09=09=09value =3D defGetString(def); =2D=09=09=09else =2D=09=09=09=09value =3D "true"; =2D=09=09=09len =3D VARHDRSZ + strlen(def->defname) + 1 + strlen(value); =2D=09=09=09/* +1 leaves room for sprintf's trailing null */ =2D=09=09=09t =3D (text *) palloc(len + 1); =2D=09=09=09SET_VARSIZE(t, len); =2D=09=09=09sprintf(VARDATA(t), "%s=3D%s", def->defname, value); =2D =2D=09=09=09astate =3D accumArrayResult(astate, PointerGetDatum(t), =2D=09=09=09=09=09=09=09=09=09 false, TEXTOID, =2D=09=09=09=09=09=09=09=09=09 CurrentMemoryContext); =2D=09=09} =2D=09} =2D =2D=09if (astate) =2D=09=09result =3D makeArrayResult(astate, CurrentMemoryContext); =2D=09else =2D=09=09result =3D (Datum) 0; =2D =2D=09return result; =2D} =2D =2D =2D/* =2D * Convert the text-array format of reloptions into a List of DefElem.= =2D * This is the inverse of transformRelOptions(). =2D */ =2DList * =2DuntransformRelOptions(Datum options) =2D{ =2D=09List=09 *result =3D NIL; =2D=09ArrayType *array; =2D=09Datum=09 *optiondatums; =2D=09int=09=09=09noptions; =2D=09int=09=09=09i; =2D =2D=09/* Nothing to do if no options */ =2D=09if (!PointerIsValid(DatumGetPointer(options))) =2D=09=09return result; =2D =2D=09array =3D DatumGetArrayTypeP(options); =2D =2D=09deconstruct_array(array, TEXTOID, -1, false, 'i', =2D=09=09=09=09=09 &optiondatums, NULL, &noptions); =2D =2D=09for (i =3D 0; i < noptions; i++) =2D=09{ =2D=09=09char=09 *s; =2D=09=09char=09 *p; =2D=09=09Node=09 *val =3D NULL; =2D =2D=09=09s =3D TextDatumGetCString(optiondatums[i]); =2D=09=09p =3D strchr(s, '=3D'); =2D=09=09if (p) =2D=09=09{ =2D=09=09=09*p++ =3D '\0'; =2D=09=09=09val =3D (Node *) makeString(pstrdup(p)); =2D=09=09} =2D=09=09result =3D lappend(result, makeDefElem(pstrdup(s), val, -1)); =2D=09} =2D =2D=09return result; =2D} +void add_autovacuum_options(options_catalog * catalog, int base_offset= , +=09=09=09=09=09 bool for_toast); =20 /* * Extract and parse reloptions from a pg_class tuple. @@ -954,11 +45,12 @@ untransformRelOptions(Datum options) */ bytea * extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, =2D=09=09=09=09 amoptions_function amoptions) +=09=09=09=09 amrelopt_catalog_function catalog_fun) { =2D=09bytea=09 *options; +=09bytea=09 *options =3D NULL; =09bool=09=09isnull; =09Datum=09=09datum; +=09options_catalog *catalog =3D NULL; =09Form_pg_class classForm; =20 =09datum =3D fastgetattr(tuple, @@ -974,548 +66,321 @@ extractRelOptions(HeapTuple tuple, TupleDesc tup= desc, =09switch (classForm->relkind) =09{ =09=09case RELKIND_RELATION: =2D=09=09case RELKIND_TOASTVALUE: =09=09case RELKIND_MATVIEW: =2D=09=09case RELKIND_PARTITIONED_TABLE: =2D=09=09=09options =3D heap_reloptions(classForm->relkind, datum, false)= ; +=09=09=09catalog =3D get_heap_relopt_catalog(); +=09=09=09break; +=09=09case RELKIND_TOASTVALUE: +=09=09=09catalog =3D get_toast_relopt_catalog(); =09=09=09break; =09=09case RELKIND_VIEW: =2D=09=09=09options =3D view_reloptions(datum, false); +=09=09=09catalog =3D get_view_relopt_catalog(); =09=09=09break; =09=09case RELKIND_INDEX: =2D=09=09=09options =3D index_reloptions(amoptions, datum, false); +=09=09=09if (catalog_fun) +=09=09=09=09catalog =3D catalog_fun(); =09=09=09break; =09=09case RELKIND_FOREIGN_TABLE: =09=09=09options =3D NULL; =09=09=09break; +=09=09case RELKIND_PARTITIONED_TABLE: +=09=09=09catalog =3D NULL; /* No options for parted table for now */ +=09=09=09break; =09=09default: =09=09=09Assert(false);=09=09/* can't get here */ =09=09=09options =3D NULL;=09=09/* keep compiler quiet */ =09=09=09break; =09} =20 +=09if (catalog) +=09=09options =3D optionsTextArrayToBytea(catalog, datum); =09return options; } =20 =2D/* =2D * Interpret reloptions that are given in text-array format. =2D * =2D * options is a reloption text array as constructed by transformRelOpt= ions. =2D * kind specifies the family of options to be processed. =2D * =2D * The return value is a relopt_value * array on which the options act= ually =2D * set in the options array are marked with isset=3Dtrue. The length = of this =2D * array is returned in *numrelopts. Options not set are also present= in the =2D * array; this is so that the caller can easily locate the default val= ues. =2D * =2D * If there are no options of the given kind, numrelopts is set to 0 a= nd NULL =2D * is returned (unless options are illegally supplied despite none bei= ng =2D * defined, in which case an error occurs). =2D * =2D * Note: values of type int, bool and real are allocated as part of th= e =2D * returned array. Values of type string are allocated separately and= must =2D * be freed by the caller. =2D */ =2Drelopt_value * =2DparseRelOptions(Datum options, bool validate, relopt_kind kind, =2D=09=09=09=09int *numrelopts) =2D{ =2D=09relopt_value *reloptions =3D NULL; =2D=09int=09=09=09numoptions =3D 0; =2D=09int=09=09=09i; =2D=09int=09=09=09j; =2D =2D=09if (need_initialization) =2D=09=09initialize_reloptions(); =2D =2D=09/* Build a list of expected options, based on kind */ =2D =2D=09for (i =3D 0; relOpts[i]; i++) =2D=09=09if (relOpts[i]->kinds & kind) =2D=09=09=09numoptions++; =2D =2D=09if (numoptions > 0) =2D=09{ =2D=09=09reloptions =3D palloc(numoptions * sizeof(relopt_value)); =2D =2D=09=09for (i =3D 0, j =3D 0; relOpts[i]; i++) =2D=09=09{ =2D=09=09=09if (relOpts[i]->kinds & kind) =2D=09=09=09{ =2D=09=09=09=09reloptions[j].gen =3D relOpts[i]; =2D=09=09=09=09reloptions[j].isset =3D false; =2D=09=09=09=09j++; =2D=09=09=09} =2D=09=09} =2D=09} =2D =2D=09/* Done if no options */ =2D=09if (PointerIsValid(DatumGetPointer(options))) =2D=09{ =2D=09=09ArrayType *array =3D DatumGetArrayTypeP(options); =2D=09=09Datum=09 *optiondatums; =2D=09=09int=09=09=09noptions; =2D =2D=09=09deconstruct_array(array, TEXTOID, -1, false, 'i', =2D=09=09=09=09=09=09 &optiondatums, NULL, &noptions); =2D =2D=09=09for (i =3D 0; i < noptions; i++) =2D=09=09{ =2D=09=09=09char=09 *text_str =3D VARDATA(optiondatums[i]); =2D=09=09=09int=09=09=09text_len =3D VARSIZE(optiondatums[i]) - VARHDRSZ;= =2D=09=09=09int=09=09=09j; =2D =2D=09=09=09/* Search for a match in reloptions */ =2D=09=09=09for (j =3D 0; j < numoptions; j++) =2D=09=09=09{ =2D=09=09=09=09int=09=09=09kw_len =3D reloptions[j].gen->namelen; =2D =2D=09=09=09=09if (text_len > kw_len && text_str[kw_len] =3D=3D '=3D' && =2D=09=09=09=09=09pg_strncasecmp(text_str, reloptions[j].gen->name, =2D=09=09=09=09=09=09=09=09 kw_len) =3D=3D 0) =2D=09=09=09=09{ =2D=09=09=09=09=09parse_one_reloption(&reloptions[j], text_str, text_len,= =2D=09=09=09=09=09=09=09=09=09=09validate); =2D=09=09=09=09=09break; =2D=09=09=09=09} =2D=09=09=09} =2D =2D=09=09=09if (j >=3D numoptions && validate) =2D=09=09=09{ =2D=09=09=09=09char=09 *s; =2D=09=09=09=09char=09 *p; =2D =2D=09=09=09=09s =3D TextDatumGetCString(optiondatums[i]); =2D=09=09=09=09p =3D strchr(s, '=3D'); =2D=09=09=09=09if (p) =2D=09=09=09=09=09*p =3D '\0'; =2D=09=09=09=09ereport(ERROR, =2D=09=09=09=09=09=09(errcode(ERRCODE_INVALID_PARAMETER_VALUE), =2D=09=09=09=09=09=09 errmsg("unrecognized parameter \"%s\"", s))); =2D=09=09=09} =2D=09=09} =2D =2D=09=09/* It's worth avoiding memory leaks in this function */ =2D=09=09pfree(optiondatums); =2D=09=09if (((void *) array) !=3D DatumGetPointer(options)) =2D=09=09=09pfree(array); =2D=09} =2D =2D=09*numrelopts =3D numoptions; =2D=09return reloptions; =2D} =20 /* =2D * Subroutine for parseRelOptions, to parse and validate a single opti= on's =2D * value + * Here goes functions that build catalogs for non-index relations. Ca= talogs + * for index relations are available from Access Methods, all other + * relation catalogs are here */ =2Dstatic void =2Dparse_one_reloption(relopt_value *option, char *text_str, int text_len= , =2D=09=09=09=09=09bool validate) =2D{ =2D=09char=09 *value; =2D=09int=09=09=09value_len; =2D=09bool=09=09parsed; =2D=09bool=09=09nofree =3D false; =2D =2D=09if (option->isset && validate) =2D=09=09ereport(ERROR, =2D=09=09=09=09(errcode(ERRCODE_INVALID_PARAMETER_VALUE), =2D=09=09=09=09 errmsg("parameter \"%s\" specified more than once", =2D=09=09=09=09=09=09option->gen->name))); =2D =2D=09value_len =3D text_len - option->gen->namelen - 1; =2D=09value =3D (char *) palloc(value_len + 1); =2D=09memcpy(value, text_str + option->gen->namelen + 1, value_len); =2D=09value[value_len] =3D '\0'; =2D =2D=09switch (option->gen->type) =2D=09{ =2D=09=09case RELOPT_TYPE_BOOL: =2D=09=09=09{ =2D=09=09=09=09parsed =3D parse_bool(value, &option->values.bool_val); =2D=09=09=09=09if (validate && !parsed) =2D=09=09=09=09=09ereport(ERROR, =2D=09=09=09=09=09=09=09(errcode(ERRCODE_INVALID_PARAMETER_VALUE), =2D=09=09=09=09=09=09=09 errmsg("invalid value for boolean option \"%s\":= %s", =2D=09=09=09=09=09=09=09=09=09option->gen->name, value))); =2D=09=09=09} =2D=09=09=09break; =2D=09=09case RELOPT_TYPE_INT: =2D=09=09=09{ =2D=09=09=09=09relopt_int *optint =3D (relopt_int *) option->gen; =2D =2D=09=09=09=09parsed =3D parse_int(value, &option->values.int_val, 0, NU= LL); =2D=09=09=09=09if (validate && !parsed) =2D=09=09=09=09=09ereport(ERROR, =2D=09=09=09=09=09=09=09(errcode(ERRCODE_INVALID_PARAMETER_VALUE), =2D=09=09=09=09=09=09=09 errmsg("invalid value for integer option \"%s\":= %s", =2D=09=09=09=09=09=09=09=09=09option->gen->name, value))); =2D=09=09=09=09if (validate && (option->values.int_val < optint->min || =2D=09=09=09=09=09=09=09=09 option->values.int_val > optint->max)) =2D=09=09=09=09=09ereport(ERROR, =2D=09=09=09=09=09=09=09(errcode(ERRCODE_INVALID_PARAMETER_VALUE), =2D=09=09=09=09=09=09=09 errmsg("value %s out of bounds for option \"%s\"= ", =2D=09=09=09=09=09=09=09=09=09value, option->gen->name), =2D=09=09=09=09=09=09=09 errdetail("Valid values are between \"%d\" and \= "%d\".", =2D=09=09=09=09=09=09=09=09=09 optint->min, optint->max))); =2D=09=09=09} =2D=09=09=09break; =2D=09=09case RELOPT_TYPE_REAL: =2D=09=09=09{ =2D=09=09=09=09relopt_real *optreal =3D (relopt_real *) option->gen; =2D =2D=09=09=09=09parsed =3D parse_real(value, &option->values.real_val); =2D=09=09=09=09if (validate && !parsed) =2D=09=09=09=09=09ereport(ERROR, =2D=09=09=09=09=09=09=09(errcode(ERRCODE_INVALID_PARAMETER_VALUE), =2D=09=09=09=09=09=09=09 errmsg("invalid value for floating point option = \"%s\": %s", =2D=09=09=09=09=09=09=09=09=09option->gen->name, value))); =2D=09=09=09=09if (validate && (option->values.real_val < optreal->min ||= =2D=09=09=09=09=09=09=09=09 option->values.real_val > optreal->max)) =2D=09=09=09=09=09ereport(ERROR, =2D=09=09=09=09=09=09=09(errcode(ERRCODE_INVALID_PARAMETER_VALUE), =2D=09=09=09=09=09=09=09 errmsg("value %s out of bounds for option \"%s\"= ", =2D=09=09=09=09=09=09=09=09=09value, option->gen->name), =2D=09=09=09=09=09=09=09 errdetail("Valid values are between \"%f\" and \= "%f\".", =2D=09=09=09=09=09=09=09=09=09 optreal->min, optreal->max))); =2D=09=09=09} =2D=09=09=09break; =2D=09=09case RELOPT_TYPE_STRING: =2D=09=09=09{ =2D=09=09=09=09relopt_string *optstring =3D (relopt_string *) option->gen= ; =2D =2D=09=09=09=09option->values.string_val =3D value; =2D=09=09=09=09nofree =3D true; =2D=09=09=09=09if (validate && optstring->validate_cb) =2D=09=09=09=09=09(optstring->validate_cb) (value); =2D=09=09=09=09parsed =3D true; =2D=09=09=09} =2D=09=09=09break; =2D=09=09default: =2D=09=09=09elog(ERROR, "unsupported reloption type %d", option->gen->typ= e); =2D=09=09=09parsed =3D true;=09=09/* quiet compiler */ =2D=09=09=09break; =2D=09} =2D =2D=09if (parsed) =2D=09=09option->isset =3D true; =2D=09if (!nofree) =2D=09=09pfree(value); =2D} =20 /* =2D * Given the result from parseRelOptions, allocate a struct that's of = the =2D * specified base size plus any extra space that's needed for string v= ariables. + * Both heap and toast relation have almost the same autovacuum set of= options. + * These autovacuum options are stored in the same structure (AutoVacO= pts) + * that is a part of both HeapOptions and ToastOptions. * =2D * "base" should be sizeof(struct) of the reloptions struct (StdRdOpti= ons or =2D * equivalent). =2D */ =2Dvoid * =2DallocateReloptStruct(Size base, relopt_value *options, int numoptions)= =2D{ =2D=09Size=09=09size =3D base; =2D=09int=09=09=09i; =2D =2D=09for (i =3D 0; i < numoptions; i++) =2D=09=09if (options[i].gen->type =3D=3D RELOPT_TYPE_STRING) =2D=09=09=09size +=3D GET_STRING_RELOPTION_LEN(options[i]) + 1; =2D =2D=09return palloc0(size); =2D} =2D =2D/* =2D * Given the result of parseRelOptions and a parsing table, fill in th= e =2D * struct (previously allocated with allocateReloptStruct) with the pa= rsed =2D * values. + * Function add_autovacuum_options adds to the "catalog" catalog autov= acuum + * options definition. In binary mode this options are saved into Auto= VacOpts + * structure, which is located in result bytea chunk with base_offset = offset. * =2D * rdopts is the pointer to the allocated struct to be filled. =2D * basesize is the sizeof(struct) that was passed to allocateReloptStr= uct. =2D * options, of length numoptions, is parseRelOptions' output. =2D * elems, of length numelems, is the table describing the allowed opti= ons. =2D * When validate is true, it is expected that all options appear in el= ems. + * Heap has two options (autovacuum_analyze_threshold and + * autovacuum_analyze_scale_factor) that is not used for toast. So for= toast + * case this options are added to catalog with OPTION_DEFINITION_FLAG_= REJECT + * flag set, so postgres will reject this toast options from the CREAT= E or + * ALTER command, but still will save default value in binary represen= tation */ + void =2DfillRelOptions(void *rdopts, Size basesize, =2D=09=09=09 relopt_value *options, int numoptions, =2D=09=09=09 bool validate, =2D=09=09=09 const relopt_parse_elt *elems, int numelems) +add_autovacuum_options(options_catalog * catalog, int base_offset, +=09=09=09=09=09 bool for_toast) { =2D=09int=09=09=09i; =2D=09int=09=09=09offset =3D basesize; =2D =2D=09for (i =3D 0; i < numoptions; i++) =2D=09{ =2D=09=09int=09=09=09j; =2D=09=09bool=09=09found =3D false; =2D =2D=09=09for (j =3D 0; j < numelems; j++) =2D=09=09{ =2D=09=09=09if (pg_strcasecmp(options[i].gen->name, elems[j].optname) =3D= =3D 0) =2D=09=09=09{ =2D=09=09=09=09relopt_string *optstring; =2D=09=09=09=09char=09 *itempos =3D ((char *) rdopts) + elems[j].offset= ; =2D=09=09=09=09char=09 *string_val; =2D =2D=09=09=09=09switch (options[i].gen->type) =2D=09=09=09=09{ =2D=09=09=09=09=09case RELOPT_TYPE_BOOL: =2D=09=09=09=09=09=09*(bool *) itempos =3D options[i].isset ? =2D=09=09=09=09=09=09=09options[i].values.bool_val : =2D=09=09=09=09=09=09=09((relopt_bool *) options[i].gen)->default_val; =2D=09=09=09=09=09=09break; =2D=09=09=09=09=09case RELOPT_TYPE_INT: =2D=09=09=09=09=09=09*(int *) itempos =3D options[i].isset ? =2D=09=09=09=09=09=09=09options[i].values.int_val : =2D=09=09=09=09=09=09=09((relopt_int *) options[i].gen)->default_val; =2D=09=09=09=09=09=09break; =2D=09=09=09=09=09case RELOPT_TYPE_REAL: =2D=09=09=09=09=09=09*(double *) itempos =3D options[i].isset ? =2D=09=09=09=09=09=09=09options[i].values.real_val : =2D=09=09=09=09=09=09=09((relopt_real *) options[i].gen)->default_val; =2D=09=09=09=09=09=09break; =2D=09=09=09=09=09case RELOPT_TYPE_STRING: =2D=09=09=09=09=09=09optstring =3D (relopt_string *) options[i].gen; =2D=09=09=09=09=09=09if (options[i].isset) =2D=09=09=09=09=09=09=09string_val =3D options[i].values.string_val; =2D=09=09=09=09=09=09else if (!optstring->default_isnull) =2D=09=09=09=09=09=09=09string_val =3D optstring->default_val; =2D=09=09=09=09=09=09else =2D=09=09=09=09=09=09=09string_val =3D NULL; =2D =2D=09=09=09=09=09=09if (string_val =3D=3D NULL) =2D=09=09=09=09=09=09=09*(int *) itempos =3D 0; =2D=09=09=09=09=09=09else =2D=09=09=09=09=09=09{ =2D=09=09=09=09=09=09=09strcpy((char *) rdopts + offset, string_val); =2D=09=09=09=09=09=09=09*(int *) itempos =3D offset; =2D=09=09=09=09=09=09=09offset +=3D strlen(string_val) + 1; =2D=09=09=09=09=09=09} =2D=09=09=09=09=09=09break; =2D=09=09=09=09=09default: =2D=09=09=09=09=09=09elog(ERROR, "unsupported reloption type %d", =2D=09=09=09=09=09=09=09 options[i].gen->type); =2D=09=09=09=09=09=09break; =2D=09=09=09=09} =2D=09=09=09=09found =3D true; =2D=09=09=09=09break; =2D=09=09=09} =2D=09=09} =2D=09=09if (validate && !found) =2D=09=09=09elog(ERROR, "reloption \"%s\" not found in parse table", =2D=09=09=09=09 options[i].gen->name); =2D=09} =2D=09SET_VARSIZE(rdopts, offset); +=09optionsCatalogAddItemBool(catalog, "autovacuum_enabled", +=09=09=09=09=09=09=09 "Enables autovacuum in this relation", +=09=09=09=09=09=09=09 ShareUpdateExclusiveLock, +=09=09=09=09=09 0, base_offset + offsetof(AutoVacOpts, enabled), true= ); + +=09optionsCatalogAddItemInt(catalog, "autovacuum_vacuum_threshold", +=09=09=09=09"Minimum number of tuple updates or deletes prior to vacuu= m", +=09=09=09=09=09=09=09 ShareUpdateExclusiveLock, +=09=09=09=09=090, base_offset + offsetof(AutoVacOpts, vacuum_threshold= ), +=09=09=09=09=09=09=09 -1, 0, INT_MAX); + +=09optionsCatalogAddItemInt(catalog, "autovacuum_analyze_threshold", +=09=09=09=09"Minimum number of tuple updates or deletes prior to vacuu= m", +=09=09=09=09=09=09=09 ShareUpdateExclusiveLock, +=09=09=09=09=09=09=09 for_toast ? OPTION_DEFINITION_FLAG_REJECT : 0, +=09=09=09=09=09 base_offset + offsetof(AutoVacOpts, analyze_threshold= ), +=09=09=09=09=09=09=09 -1, 0, INT_MAX); + +=09optionsCatalogAddItemInt(catalog, "autovacuum_vacuum_cost_delay", +=09=09=09=09=09=09 "Vacuum cost delay in milliseconds, for autovacuum"= , +=09=09=09=09=09=09=09 ShareUpdateExclusiveLock, +=09=09=09=09 0, base_offset + offsetof(AutoVacOpts, vacuum_cost_dela= y), +=09=09=09=09=09=09=09 -1, 0, 100); + +=09optionsCatalogAddItemInt(catalog, "autovacuum_vacuum_cost_limit", +=09=09=09 "Vacuum cost amount available before napping, for autovacu= um", +=09=09=09=09=09=09=09 ShareUpdateExclusiveLock, +=09=09=09=09 0, base_offset + offsetof(AutoVacOpts, vacuum_cost_limi= t), +=09=09=09=09=09=09=09 -1, 0, 10000); + +=09optionsCatalogAddItemInt(catalog, "autovacuum_freeze_min_age", +=09 "Minimum age at which VACUUM should freeze a table row, for autova= cuum", +=09=09=09=09=09=09=09 ShareUpdateExclusiveLock, +=09=09=09=09=09 0, base_offset + offsetof(AutoVacOpts, freeze_min_age= ), +=09=09=09=09=09=09=09 -1, 0, 1000000000); + +=09optionsCatalogAddItemInt(catalog, "autovacuum_freeze_max_age", +=09"Age at which to autovacuum a table to prevent transaction ID wrapa= round", +=09=09=09=09=09=09=09 ShareUpdateExclusiveLock, +=09=09=09=09=09 0, base_offset + offsetof(AutoVacOpts, freeze_max_age= ), +=09=09=09=09=09=09=09 -1, 100000, 2000000000); + +=09optionsCatalogAddItemInt(catalog, "autovacuum_freeze_table_age", +=09=09=09=09=09=09=09 "Age at which VACUUM should perform a full table= sweep to freeze row versions", +=09=09=09=09=09=09=09 ShareUpdateExclusiveLock, +=09=09=09=09=090, base_offset + offsetof(AutoVacOpts, freeze_table_age= ), +=09=09=09=09=09=09=09 -1, 0, 2000000000); + +=09optionsCatalogAddItemInt(catalog, "autovacuum_multixact_freeze_min_= age", +=09=09=09=09=09=09=09 "Minimum multixact age at which VACUUM should fr= eeze a row multixact's, for autovacuum", +=09=09=09=09=09=09=09 ShareUpdateExclusiveLock, +=09=09=090, base_offset + offsetof(AutoVacOpts, multixact_freeze_min_a= ge), +=09=09=09=09=09=09=09 -1, 0, 1000000000); + +=09optionsCatalogAddItemInt(catalog, "autovacuum_multixact_freeze_max_= age", +=09=09=09=09=09=09=09 "Multixact age at which to autovacuum a table to= prevent multixact wraparound", +=09=09=09=09=09=09=09 ShareUpdateExclusiveLock, +=09=09=090, base_offset + offsetof(AutoVacOpts, multixact_freeze_max_a= ge), +=09=09=09=09=09=09=09 -1, 10000, 2000000000); + +=09optionsCatalogAddItemInt(catalog, "autovacuum_multixact_freeze_tabl= e_age", +=09=09=09=09=09=09=09 "Age of multixact at which VACUUM should perform= a full table sweep to freeze row versions", +=09=09=09=09=09=09=09 ShareUpdateExclusiveLock, +=09=09 0, base_offset + offsetof(AutoVacOpts, multixact_freeze_table_= age), +=09=09=09=09=09=09=09 -1, 0, 2000000000); + +=09optionsCatalogAddItemInt(catalog, "log_autovacuum_min_duration", +=09=09=09=09=09=09=09 "Sets the minimum execution time above which aut= ovacuum actions will be logged", +=09=09=09=09=09=09=09 ShareUpdateExclusiveLock, +=09=09=09=09=090, base_offset + offsetof(AutoVacOpts, log_min_duration= ), +=09=09=09=09=09=09=09 -1, -1, INT_MAX); + +=09optionsCatalogAddItemReal(catalog, "autovacuum_vacuum_scale_factor"= , +=09=09=09=09=09=09=09 "Number of tuple updates or deletes prior to va= cuum as a fraction of reltuples", +=09=09=09=09=09=09=09 ShareUpdateExclusiveLock, +=09=09=09=09 0, base_offset + offsetof(AutoVacOpts, vacuum_scale_facto= r), +=09=09=09=09=09=09=09 -1, 0.0, 100.0); + +=09optionsCatalogAddItemReal(catalog, "autovacuum_analyze_scale_factor= ", +=09=09=09=09=09=09=09 "Number of tuple inserts, updates or deletes pr= ior to analyze as a fraction of reltuples", +=09=09=09=09=09=09=09 ShareUpdateExclusiveLock, +=09=09=09=09=09=09=09 for_toast ? OPTION_DEFINITION_FLAG_REJECT : 0, +=09=09=09=09 base_offset + offsetof(AutoVacOpts, analyze_scale_facto= r), +=09=09=09=09=09=09=09 -1, 0.0, 100.0); } =20 =2D /* =2D * Option parser for anything that uses StdRdOptions. + * get_heap_relopt_catalog + *=09=09Returns an options catalog for heap relation. */ =2Dbytea * =2Ddefault_reloptions(Datum reloptions, bool validate, relopt_kind kind) =2D{ =2D=09relopt_value *options; =2D=09StdRdOptions *rdopts; =2D=09int=09=09=09numoptions; =2D=09static const relopt_parse_elt tab[] =3D { =2D=09=09{"fillfactor", RELOPT_TYPE_INT, offsetof(StdRdOptions, fillfacto= r)}, =2D=09=09{"autovacuum_enabled", RELOPT_TYPE_BOOL, =2D=09=09offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, enabl= ed)}, =2D=09=09{"autovacuum_vacuum_threshold", RELOPT_TYPE_INT, =2D=09=09offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuu= m_threshold)}, =2D=09=09{"autovacuum_analyze_threshold", RELOPT_TYPE_INT, =2D=09=09offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, analy= ze_threshold)}, =2D=09=09{"autovacuum_vacuum_cost_delay", RELOPT_TYPE_INT, =2D=09=09offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuu= m_cost_delay)}, =2D=09=09{"autovacuum_vacuum_cost_limit", RELOPT_TYPE_INT, =2D=09=09offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuu= m_cost_limit)}, =2D=09=09{"autovacuum_freeze_min_age", RELOPT_TYPE_INT, =2D=09=09offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freez= e_min_age)}, =2D=09=09{"autovacuum_freeze_max_age", RELOPT_TYPE_INT, =2D=09=09offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freez= e_max_age)}, =2D=09=09{"autovacuum_freeze_table_age", RELOPT_TYPE_INT, =2D=09=09offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freez= e_table_age)}, =2D=09=09{"autovacuum_multixact_freeze_min_age", RELOPT_TYPE_INT, =2D=09=09offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multi= xact_freeze_min_age)}, =2D=09=09{"autovacuum_multixact_freeze_max_age", RELOPT_TYPE_INT, =2D=09=09offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multi= xact_freeze_max_age)}, =2D=09=09{"autovacuum_multixact_freeze_table_age", RELOPT_TYPE_INT, =2D=09=09offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multi= xact_freeze_table_age)}, =2D=09=09{"log_autovacuum_min_duration", RELOPT_TYPE_INT, =2D=09=09offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, log_m= in_duration)}, =2D=09=09{"autovacuum_vacuum_scale_factor", RELOPT_TYPE_REAL, =2D=09=09offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuu= m_scale_factor)}, =2D=09=09{"autovacuum_analyze_scale_factor", RELOPT_TYPE_REAL, =2D=09=09offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, analy= ze_scale_factor)}, =2D=09=09{"user_catalog_table", RELOPT_TYPE_BOOL, =2D=09=09offsetof(StdRdOptions, user_catalog_table)}, =2D=09=09{"parallel_workers", RELOPT_TYPE_INT, =2D=09=09offsetof(StdRdOptions, parallel_workers)} =2D=09}; +static options_catalog *heap_relopt_catalog =3D NULL; =20 =2D=09options =3D parseRelOptions(reloptions, validate, kind, &numoptions= ); =2D =2D=09/* if none set, we're done */ =2D=09if (numoptions =3D=3D 0) =2D=09=09return NULL; =2D =2D=09rdopts =3D allocateReloptStruct(sizeof(StdRdOptions), options, numo= ptions); =2D =2D=09fillRelOptions((void *) rdopts, sizeof(StdRdOptions), options, numo= ptions, =2D=09=09=09=09 validate, tab, lengthof(tab)); =2D =2D=09pfree(options); =2D =2D=09return (bytea *) rdopts; +options_catalog * +get_heap_relopt_catalog(void) +{ +=09if (!heap_relopt_catalog) +=09{ +=09=09heap_relopt_catalog =3D allocateOptionsCatalog(NULL, sizeof(Heap= Options), +=09=09=09=09=09=09=09=09=09=09=09=09=09 14 + 4);=09/* 14 - for autovac= uum +=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09 * options, 4 - core +=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09 * heap options */ + +=09=09optionsCatalogAddItemInt(heap_relopt_catalog, "fillfactor", +=09=09=09=09=09=09=09=09 "Packs table pages only to this percentag", +=09=09=09=09=09=09=09=09 ShareUpdateExclusiveLock,=09=09/* since it ap= plies only +=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09 * to later inserts */= +=09=09=09=09=09=09=09=09 0, offsetof(HeapOptions, fillfactor), +=09=09=09=09=09=09 HEAP_DEFAULT_FILLFACTOR, HEAP_MIN_FILLFACTOR, 100)= ; + +=09=09add_autovacuum_options(heap_relopt_catalog, +=09=09=09=09=09=09=09 offsetof(HeapOptions, autovacuum), false); + +=09=09optionsCatalogAddItemBool(heap_relopt_catalog, "user_catalog_tab= le", +=09=09=09=09=09=09=09=09 "Declare a table as an additional catalog ta= ble, e.g. for the purpose of logical replication", +=09=09=09=09=09=09=09=09 AccessExclusiveLock, +=09=09=09=09=09=09=09=090, offsetof(HeapOptions, user_catalog_table), +=09=09=09=09=09=09=09=09 false); + +=09=09optionsCatalogAddItemInt(heap_relopt_catalog, "parallel_workers"= , +=09=09=09=09=09=09=09=09 "Number of parallel processes that can be use= d per executor node for this relation", +=09=09=09=09=09=09=09=09 ShareUpdateExclusiveLock, +=09=09=09=09=09=09=09=09 0, offsetof(HeapOptions, parallel_workers), +=09=09=09=09=09=09=09=09 -1, 0, 1024); + +=09=09/* +=09=09 * A WITH OID / WITHOUT OIDS expressions are converted by syntax= +=09=09 * parser into "oids" relation option, but this option is proces= sed by +=09=09 * interpretOidsOption(), that does not use core options code. T= hat is +=09=09 * why here we should just ignore this option as if it does not = exist. +=09=09 * Do it with OPTION_DEFINITION_FLAG_IGNORE flag +=09=09 */ +=09=09optionsCatalogAddItemBool(heap_relopt_catalog, "oids", +=09=09=09=09 "Option used for WITH OIDS expression. Processed elsewher= e", +=09=09=09=09=09=09=09=09 NoLock, +=09=09=09=09=09=09=09=09 OPTION_DEFINITION_FLAG_IGNORE, -1, +=09=09=09=09=09=09=09=09 false); +=09} +=09return heap_relopt_catalog; } =20 /* =2D * Option parser for views + * get_toast_relopt_catalog + *=09=09Returns an options catalog for toast relation. */ =2Dbytea * =2Dview_reloptions(Datum reloptions, bool validate) =2D{ =2D=09relopt_value *options; =2D=09ViewOptions *vopts; =2D=09int=09=09=09numoptions; =2D=09static const relopt_parse_elt tab[] =3D { =2D=09=09{"security_barrier", RELOPT_TYPE_BOOL, =2D=09=09offsetof(ViewOptions, security_barrier)}, =2D=09=09{"check_option", RELOPT_TYPE_STRING, =2D=09=09offsetof(ViewOptions, check_option_offset)} =2D=09}; =2D =2D=09options =3D parseRelOptions(reloptions, validate, RELOPT_KIND_VIEW,= &numoptions); =2D =2D=09/* if none set, we're done */ =2D=09if (numoptions =3D=3D 0) =2D=09=09return NULL; =20 =2D=09vopts =3D allocateReloptStruct(sizeof(ViewOptions), options, numopt= ions); +static options_catalog *toast_relopt_catalog =3D NULL; =20 =2D=09fillRelOptions((void *) vopts, sizeof(ViewOptions), options, numopt= ions, =2D=09=09=09=09 validate, tab, lengthof(tab)); =2D =2D=09pfree(options); =2D =2D=09return (bytea *) vopts; =2D} =2D =2D/* =2D * Parse options for heaps, views and toast tables. =2D */ =2Dbytea * =2Dheap_reloptions(char relkind, Datum reloptions, bool validate) +options_catalog * +get_toast_relopt_catalog(void) { =2D=09StdRdOptions *rdopts; =2D =2D=09switch (relkind) +=09if (!toast_relopt_catalog) =09{ =2D=09=09case RELKIND_TOASTVALUE: =2D=09=09=09rdopts =3D (StdRdOptions *) =2D=09=09=09=09default_reloptions(reloptions, validate, RELOPT_KIND_TOAST= ); =2D=09=09=09if (rdopts !=3D NULL) =2D=09=09=09{ =2D=09=09=09=09/* adjust default-only parameters for TOAST relations */ =2D=09=09=09=09rdopts->fillfactor =3D 100; =2D=09=09=09=09rdopts->autovacuum.analyze_threshold =3D -1; =2D=09=09=09=09rdopts->autovacuum.analyze_scale_factor =3D -1; =2D=09=09=09} =2D=09=09=09return (bytea *) rdopts; =2D=09=09case RELKIND_RELATION: =2D=09=09case RELKIND_MATVIEW: =2D=09=09=09return default_reloptions(reloptions, validate, RELOPT_KIND_H= EAP); =2D=09=09case RELKIND_PARTITIONED_TABLE: =2D=09=09=09return default_reloptions(reloptions, validate, =2D=09=09=09=09=09=09=09=09=09 RELOPT_KIND_PARTITIONED); =2D=09=09default: =2D=09=09=09/* other relkinds are not supported */ =2D=09=09=09return NULL; +=09=09toast_relopt_catalog =3D allocateOptionsCatalog("toast", sizeof(= ToastOptions), +=09=09=09=09=09=09=09=09=09=09=09=09=09 14);=09=09/* 14 - for autovac= uum +=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09 * options */ +=09=09add_autovacuum_options(toast_relopt_catalog, +=09=09=09=09=09=09=09 offsetof(ToastOptions, autovacuum), true); =09} +=09return toast_relopt_catalog; } =20 =2D /* =2D * Parse options for indexes. =2D * =2D *=09amoptions=09index AM's option parser function =2D *=09reloptions=09options as text[] datum =2D *=09validate=09error flag + * get_view_relopt_catalog + *=09=09Returns an options catalog for view relation. */ =2Dbytea * =2Dindex_reloptions(amoptions_function amoptions, Datum reloptions, bool = validate) +static options_catalog *view_relopt_catalog =3D NULL; + +options_catalog * +get_view_relopt_catalog(void) { =2D=09Assert(amoptions !=3D NULL); +=09static const char *enum_names[] =3D VIEW_OPTION_CHECK_OPTION_VALUE_= NAMES; =20 =2D=09/* Assume function is strict */ =2D=09if (!PointerIsValid(DatumGetPointer(reloptions))) =2D=09=09return NULL; +=09if (!view_relopt_catalog) +=09{ +=09=09view_relopt_catalog =3D allocateOptionsCatalog(NULL, +=09=09=09=09=09=09=09=09=09=09=09=09=09 sizeof(ViewOptions), 2); =20 =2D=09return amoptions(reloptions, validate); +=09=09optionsCatalogAddItemBool(view_relopt_catalog, "security_barrier= ", +=09=09=09=09=09=09=09=09 "View acts as a row security barrier", +=09=09=09=09=09=09=09=09 AccessExclusiveLock, +=09=09=09=09=09=09 0, offsetof(ViewOptions, security_barrier), false)= ; + +=09=09optionsCatalogAddItemEnum(view_relopt_catalog, "check_option", +=09=09=09=09=09"View has WITH CHECK OPTION defined (local or cascaded)= ", +=09=09=09=09=09=09=09=09 AccessExclusiveLock, 0, +=09=09=09=09=09=09=09=09 offsetof(ViewOptions, check_option), +=09=09=09=09=09=09=09=09 enum_names, +=09=09=09=09=09=09=09=09 VIEW_OPTION_CHECK_OPTION_NOT_SET); +=09} +=09return view_relopt_catalog; } =20 /* =2D * Option parser for attribute reloptions + * get_attribute_options_catalog + *=09=09Returns an options catalog for heap attributes */ =2Dbytea * =2Dattribute_reloptions(Datum reloptions, bool validate) =2D{ =2D=09relopt_value *options; =2D=09AttributeOpts *aopts; =2D=09int=09=09=09numoptions; =2D=09static const relopt_parse_elt tab[] =3D { =2D=09=09{"n_distinct", RELOPT_TYPE_REAL, offsetof(AttributeOpts, n_disti= nct)}, =2D=09=09{"n_distinct_inherited", RELOPT_TYPE_REAL, offsetof(AttributeOpt= s, n_distinct_inherited)} =2D=09}; =2D =2D=09options =3D parseRelOptions(reloptions, validate, RELOPT_KIND_ATTRI= BUTE, =2D=09=09=09=09=09=09=09 &numoptions); =2D =2D=09/* if none set, we're done */ =2D=09if (numoptions =3D=3D 0) =2D=09=09return NULL; =2D =2D=09aopts =3D allocateReloptStruct(sizeof(AttributeOpts), options, numo= ptions); +static options_catalog *attribute_options_catalog =3D NULL; =20 =2D=09fillRelOptions((void *) aopts, sizeof(AttributeOpts), options, numo= ptions, =2D=09=09=09=09 validate, tab, lengthof(tab)); +options_catalog * +get_attribute_options_catalog(void) +{ +=09if (!attribute_options_catalog) +=09{ +=09=09attribute_options_catalog =3D allocateOptionsCatalog(NULL, +=09=09=09=09=09=09=09=09=09=09=09=09 sizeof(AttributeOpts), 2); =20 =2D=09pfree(options); +=09=09optionsCatalogAddItemReal(attribute_options_catalog, "n_distinct= ", +=09=09=09=09=09=09=09=09 "Sets the planner's estimate of the number o= f distinct values appearing in a column (excluding child relations)", +=09=09=09=09=09=09=09=09 ShareUpdateExclusiveLock, +=09=09=09=09 0, offsetof(AttributeOpts, n_distinct), 0, -1.0, DBL_MA= X); =20 =2D=09return (bytea *) aopts; +=09=09optionsCatalogAddItemReal(attribute_options_catalog, +=09=09=09=09=09=09=09=09 "n_distinct_inherited", +=09=09=09=09=09=09=09=09 "Sets the planner's estimate of the number o= f distinct values appearing in a column (including child relations", +=09=09=09=09=09=09=09=09 ShareUpdateExclusiveLock, +=09=09 0, offsetof(AttributeOpts, n_distinct_inherited), 0, -1.0, DBL_= MAX); +=09} +=09return attribute_options_catalog; } =20 /* =2D * Option parser for tablespace reloptions + * get_tablespace_options_catalog + *=09=09Returns an options catalog for tablespaces */ =2Dbytea * =2Dtablespace_reloptions(Datum reloptions, bool validate) =2D{ =2D=09relopt_value *options; =2D=09TableSpaceOpts *tsopts; =2D=09int=09=09=09numoptions; =2D=09static const relopt_parse_elt tab[] =3D { =2D=09=09{"random_page_cost", RELOPT_TYPE_REAL, offsetof(TableSpaceOpts, = random_page_cost)}, =2D=09=09{"seq_page_cost", RELOPT_TYPE_REAL, offsetof(TableSpaceOpts, seq= _page_cost)}, =2D=09=09{"effective_io_concurrency", RELOPT_TYPE_INT, offsetof(TableSpac= eOpts, effective_io_concurrency)} =2D=09}; =2D =2D=09options =3D parseRelOptions(reloptions, validate, RELOPT_KIND_TABLE= SPACE, =2D=09=09=09=09=09=09=09 &numoptions); =2D =2D=09/* if none set, we're done */ =2D=09if (numoptions =3D=3D 0) =2D=09=09return NULL; =2D =2D=09tsopts =3D allocateReloptStruct(sizeof(TableSpaceOpts), options, nu= moptions); =2D =2D=09fillRelOptions((void *) tsopts, sizeof(TableSpaceOpts), options, nu= moptions, =2D=09=09=09=09 validate, tab, lengthof(tab)); =2D =2D=09pfree(options); +static options_catalog *tablespace_options_catalog =3D NULL; =20 =2D=09return (bytea *) tsopts; +options_catalog * +get_tablespace_options_catalog(void) +{ +=09if (!tablespace_options_catalog) +=09{ +=09=09tablespace_options_catalog =3D allocateOptionsCatalog(NULL, +=09=09=09=09=09=09=09=09=09=09=09=09 sizeof(TableSpaceOpts), 3); + +=09=09optionsCatalogAddItemReal(tablespace_options_catalog, +=09=09=09=09=09=09=09=09 "random_page_cost", +=09=09=09=09=09=09=09=09 "Sets the planner's estimate of the cost of = a nonsequentially fetched disk page", +=09=09=09=09=09=09=09=09 ShareUpdateExclusiveLock, +=09=09=090, offsetof(TableSpaceOpts, random_page_cost), -1, 0.0, DBL_M= AX); + +=09=09optionsCatalogAddItemReal(tablespace_options_catalog, "seq_page_= cost", +=09=09=09=09=09=09=09=09 "Sets the planner's estimate of the cost of = a sequentially fetched disk page", +=09=09=09=09=09=09=09=09 ShareUpdateExclusiveLock, +=09=09=09 0, offsetof(TableSpaceOpts, seq_page_cost), -1, 0.0, DBL_M= AX); + +=09=09optionsCatalogAddItemInt(tablespace_options_catalog, +=09=09=09=09=09=09=09=09 "effective_io_concurrency", +=09=09=09=09=09=09=09=09 "Number of simultaneous requests that can be = handled efficiently by the disk subsystem", +=09=09=09=09=09=09=09=09 ShareUpdateExclusiveLock, +=09=09=09=09=09 0, offsetof(TableSpaceOpts, effective_io_concurrency= ), +#ifdef USE_PREFETCH +=09=09=09=09=09=09=09=09 -1, 0, MAX_IO_CONCURRENCY +#else +=09=09=09=09=09=09=09=09 0, 0, 0 +#endif +=09=09=09); +=09} +=09return tablespace_options_catalog; } =20 /* @@ -1525,33 +390,56 @@ tablespace_reloptions(Datum reloptions, bool val= idate) * for a longer explanation of how this works. */ LOCKMODE =2DAlterTableGetRelOptionsLockLevel(List *defList) +AlterTableGetRelOptionsLockLevel(Relation rel, List *defList) { =09LOCKMODE=09lockmode =3D NoLock; =09ListCell *cell; +=09options_catalog *catalog =3D NULL; =20 =09if (defList =3D=3D NIL) =09=09return AccessExclusiveLock; =20 =2D=09if (need_initialization) =2D=09=09initialize_reloptions(); +=09switch (rel->rd_rel->relkind) +=09{ +=09=09case RELKIND_TOASTVALUE: +=09=09=09catalog =3D get_toast_relopt_catalog(); +=09=09=09break; +=09=09case RELKIND_RELATION: +=09=09case RELKIND_MATVIEW: +=09=09=09catalog =3D get_heap_relopt_catalog(); +=09=09=09break; +=09=09case RELKIND_INDEX: +=09=09=09catalog =3D rel->rd_amroutine->amrelopt_catalog(); +=09=09=09break; +=09=09case RELKIND_VIEW: +=09=09=09catalog =3D get_view_relopt_catalog(); +=09=09=09break; +=09=09case RELKIND_PARTITIONED_TABLE: +=09=09=09catalog =3D NULL; /* No options for parted table for now */ +=09=09=09break; +=09=09default: +=09=09=09Assert(false);=09=09/* can't get here */ +=09=09=09break; +=09} + +=09Assert(catalog);=09=09=09/* No catalog - no reloption change. Shoul= d +=09=09=09=09=09=09=09=09 * not get here */ =20 =09foreach(cell, defList) =09{ =09=09DefElem *def =3D (DefElem *) lfirst(cell); + =09=09int=09=09=09i; =20 =2D=09=09for (i =3D 0; relOpts[i]; i++) +=09=09for (i =3D 0; i < catalog->num; i++) =09=09{ =2D=09=09=09if (pg_strncasecmp(relOpts[i]->name, =2D=09=09=09=09=09=09=09 def->defname, =2D=09=09=09=09=09=09=09 relOpts[i]->namelen + 1) =3D=3D 0) =2D=09=09=09{ =2D=09=09=09=09if (lockmode < relOpts[i]->lockmode) =2D=09=09=09=09=09lockmode =3D relOpts[i]->lockmode; =2D=09=09=09} +=09=09=09option_definition_basic *gen =3D catalog->definitions[i]; + +=09=09=09if (pg_strcasecmp(gen->name, +=09=09=09=09=09=09=09 def->defname) =3D=3D 0) +=09=09=09=09if (lockmode < gen->lockmode) +=09=09=09=09=09lockmode =3D gen->lockmode; =09=09} =09} =2D =09return lockmode; } diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/= ginutil.c index 91e4a8c..0f2af51 100644 =2D-- 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" =2D#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" =2D +#include "utils/memutils.h" +#include "utils/guc.h" =20 /* * GIN handler function: return IndexAmRoutine with access method para= meters @@ -60,7 +61,6 @@ ginhandler(PG_FUNCTION_ARGS) =09amroutine->amvacuumcleanup =3D ginvacuumcleanup; =09amroutine->amcanreturn =3D NULL; =09amroutine->amcostestimate =3D gincostestimate; =2D=09amroutine->amoptions =3D ginoptions; =09amroutine->amproperty =3D NULL; =09amroutine->amvalidate =3D ginvalidate; =09amroutine->ambeginscan =3D ginbeginscan; @@ -70,6 +70,7 @@ ginhandler(PG_FUNCTION_ARGS) =09amroutine->amendscan =3D ginendscan; =09amroutine->ammarkpos =3D NULL; =09amroutine->amrestrpos =3D NULL; +=09amroutine->amrelopt_catalog =3D gingetreloptcatalog; =09amroutine->amestimateparallelscan =3D NULL; =09amroutine->aminitparallelscan =3D NULL; =09amroutine->amparallelrescan =3D NULL; @@ -593,35 +594,6 @@ ginExtractEntries(GinState *ginstate, OffsetNumber= attnum, =09return entries; } =20 =2Dbytea * =2Dginoptions(Datum reloptions, bool validate) =2D{ =2D=09relopt_value *options; =2D=09GinOptions *rdopts; =2D=09int=09=09=09numoptions; =2D=09static const relopt_parse_elt tab[] =3D { =2D=09=09{"fastupdate", RELOPT_TYPE_BOOL, offsetof(GinOptions, useFastUpd= ate)}, =2D=09=09{"gin_pending_list_limit", RELOPT_TYPE_INT, offsetof(GinOptions,= =2D=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09 pendingListCleanupSize)}= =2D=09}; =2D =2D=09options =3D parseRelOptions(reloptions, validate, RELOPT_KIND_GIN, =2D=09=09=09=09=09=09=09 &numoptions); =2D =2D=09/* if none set, we're done */ =2D=09if (numoptions =3D=3D 0) =2D=09=09return NULL; =2D =2D=09rdopts =3D allocateReloptStruct(sizeof(GinOptions), options, numopt= ions); =2D =2D=09fillRelOptions((void *) rdopts, sizeof(GinOptions), options, numopt= ions, =2D=09=09=09=09 validate, tab, lengthof(tab)); =2D =2D=09pfree(options); =2D =2D=09return (bytea *) rdopts; =2D} =2D /* * Fetch index's statistical data into *stats * @@ -698,3 +670,32 @@ ginUpdateStats(Relation index, const GinStatsData = *stats) =20 =09END_CRIT_SECTION(); } + + +static options_catalog *gin_relopt_catalog =3D NULL; + +void * +gingetreloptcatalog(void) +{ +=09if (!gin_relopt_catalog) +=09{ +=09=09gin_relopt_catalog =3D allocateOptionsCatalog(NULL, +=09=09=09=09=09=09=09=09=09=09=09=09=09sizeof(GinOptions), 2); + +=09=09optionsCatalogAddItemBool(gin_relopt_catalog, "fastupdate", +=09=09=09=09=09=09"Enables \"fast update\" feature for this GIN index"= , +=09=09=09=09=09=09=09=09 AccessExclusiveLock, +=09=09=09=09=09=09=09=09 0, +=09=09=09=09=09=09=09=09 offsetof(GinOptions, useFastUpdate), +=09=09=09=09=09=09=09=09 GIN_DEFAULT_USE_FASTUPDATE); + +=09=09optionsCatalogAddItemInt(gin_relopt_catalog, "gin_pending_list_l= imit", +=09=09 "Maximum size of the pending list for this GIN index, in kiloby= tes", +=09=09=09=09=09=09=09=09 AccessExclusiveLock, +=09=09=09=09=09=09=09=09 0, +=09=09=09=09=09=09=09=09 offsetof(GinOptions, pendingListCleanupSize),= +=09=09=09=09=09=09=09=09 -1, 64, MAX_KILOBYTES); + +=09} +=09return gin_relopt_catalog; +} diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/g= ist.c index 565525b..d44916f 100644 =2D-- a/src/backend/access/gist/gist.c +++ b/src/backend/access/gist/gist.c @@ -81,7 +81,6 @@ gisthandler(PG_FUNCTION_ARGS) =09amroutine->amvacuumcleanup =3D gistvacuumcleanup; =09amroutine->amcanreturn =3D gistcanreturn; =09amroutine->amcostestimate =3D gistcostestimate; =2D=09amroutine->amoptions =3D gistoptions; =09amroutine->amproperty =3D gistproperty; =09amroutine->amvalidate =3D gistvalidate; =09amroutine->ambeginscan =3D gistbeginscan; @@ -91,6 +90,7 @@ gisthandler(PG_FUNCTION_ARGS) =09amroutine->amendscan =3D gistendscan; =09amroutine->ammarkpos =3D NULL; =09amroutine->amrestrpos =3D NULL; +=09amroutine->amrelopt_catalog =3D gistgetreloptcatalog; =09amroutine->amestimateparallelscan =3D NULL; =09amroutine->aminitparallelscan =3D NULL; =09amroutine->amparallelrescan =3D NULL; diff --git a/src/backend/access/gist/gistbuild.c b/src/backend/access/g= ist/gistbuild.c index 4756a70..8d5c839 100644 =2D-- a/src/backend/access/gist/gistbuild.c +++ b/src/backend/access/gist/gistbuild.c @@ -126,11 +126,10 @@ gistbuild(Relation heap, Relation index, IndexInf= o *indexInfo) =09{ =09=09/* Get buffering mode from the options string */ =09=09GiSTOptions *options =3D (GiSTOptions *) index->rd_options; =2D=09=09char=09 *bufferingMode =3D (char *) options + options->bufferi= ngModeOffset; =20 =2D=09=09if (strcmp(bufferingMode, "on") =3D=3D 0) +=09=09if (options->buffering_mode =3D=3D GIST_OPTION_BUFFERING_ON) =09=09=09buildstate.bufferingMode =3D GIST_BUFFERING_STATS; =2D=09=09else if (strcmp(bufferingMode, "off") =3D=3D 0) +=09=09else if (options->buffering_mode =3D=3D GIST_OPTION_BUFFERING_OF= F) =09=09=09buildstate.bufferingMode =3D GIST_BUFFERING_DISABLED; =09=09else =09=09=09buildstate.bufferingMode =3D GIST_BUFFERING_AUTO; @@ -234,25 +233,6 @@ gistbuild(Relation heap, Relation index, IndexInfo= *indexInfo) } =20 /* =2D * Validator for "buffering" reloption on GiST indexes. Allows "on", "= off" =2D * and "auto" values. =2D */ =2Dvoid =2DgistValidateBufferingOption(char *value) =2D{ =2D=09if (value =3D=3D NULL || =2D=09=09(strcmp(value, "on") !=3D 0 && =2D=09=09 strcmp(value, "off") !=3D 0 && =2D=09=09 strcmp(value, "auto") !=3D 0)) =2D=09{ =2D=09=09ereport(ERROR, =2D=09=09=09=09(errcode(ERRCODE_INVALID_PARAMETER_VALUE), =2D=09=09=09=09 errmsg("invalid value for \"buffering\" option"), =2D=09=09=09=09 errdetail("Valid values are \"on\", \"off\", and \"auto\"= ."))); =2D=09} =2D} =2D =2D/* * Attempt to switch to buffering mode. * * If there is not enough memory for buffering build, sets bufferingMo= de diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gi= st/gistutil.c index b6ccc1a..5f46058 100644 =2D-- 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" =2D#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 =2D /* * Write itup vector to page, has no control of free space. */ @@ -812,34 +811,6 @@ gistNewBuffer(Relation r) =09return buffer; } =20 =2Dbytea * =2Dgistoptions(Datum reloptions, bool validate) =2D{ =2D=09relopt_value *options; =2D=09GiSTOptions *rdopts; =2D=09int=09=09=09numoptions; =2D=09static const relopt_parse_elt tab[] =3D { =2D=09=09{"fillfactor", RELOPT_TYPE_INT, offsetof(GiSTOptions, fillfactor= )}, =2D=09=09{"buffering", RELOPT_TYPE_STRING, offsetof(GiSTOptions, bufferin= gModeOffset)} =2D=09}; =2D =2D=09options =3D parseRelOptions(reloptions, validate, RELOPT_KIND_GIST,= =2D=09=09=09=09=09=09=09 &numoptions); =2D =2D=09/* if none set, we're done */ =2D=09if (numoptions =3D=3D 0) =2D=09=09return NULL; =2D =2D=09rdopts =3D allocateReloptStruct(sizeof(GiSTOptions), options, numop= tions); =2D =2D=09fillRelOptions((void *) rdopts, sizeof(GiSTOptions), options, numop= tions, =2D=09=09=09=09 validate, tab, lengthof(tab)); =2D =2D=09pfree(options); =2D =2D=09return (bytea *) rdopts; =2D} =2D /* *=09gistproperty() -- Check boolean properties of indexes. * @@ -965,3 +936,33 @@ gistGetFakeLSN(Relation rel) =09=09return GetFakeLSNForUnloggedRel(); =09} } + +static options_catalog *gist_relopt_catalog =3D NULL; + +void * +gistgetreloptcatalog(void) +{ +=09static const char *enum_names[] =3D GIST_OPTION_BUFFERING_VALUE_NAM= ES; + +=09if (!gist_relopt_catalog) +=09{ +=09=09gist_relopt_catalog =3D allocateOptionsCatalog(NULL, +=09=09=09=09=09=09=09=09=09=09=09=09=09 sizeof(GiSTOptions), 2); + +=09=09optionsCatalogAddItemInt(gist_relopt_catalog, "fillfactor", +=09=09=09=09=09=09=09"Packs gist index pages only to this percentage",= +=09=09=09=09=09=09=09=09 NoLock,=09=09/* No ALTER, no lock */ +=09=09=09=09=09=09=09=09 OPTION_DEFINITION_FLAG_FORBID_ALTER, +=09=09=09=09=09=09=09=09 offsetof(GiSTOptions, fillfactor), +=09=09=09=09=09=09=09=09 GIST_DEFAULT_FILLFACTOR, +=09=09=09=09=09=09=09=09 GIST_MIN_FILLFACTOR, 100); +=09=09optionsCatalogAddItemEnum(gist_relopt_catalog, "buffering", +=09=09=09=09=09=09=09 "Enables buffering build for this GiST index",= +=09=09=09=09=09=09=09=09 NoLock,=09=09/* No ALTER, no lock */ +=09=09=09=09=09=09=09=09 OPTION_DEFINITION_FLAG_FORBID_ALTER, +=09=09=09=09=09=09=09=09 offsetof(GiSTOptions, buffering_mode), +=09=09=09=09=09=09=09=09 enum_names, +=09=09=09=09=09=09=09=09 GIST_OPTION_BUFFERING_AUTO); +=09} +=09return gist_relopt_catalog; +} diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/h= ash.c index d89c192..6ef32cf 100644 =2D-- a/src/backend/access/hash/hash.c +++ b/src/backend/access/hash/hash.c @@ -79,7 +79,6 @@ hashhandler(PG_FUNCTION_ARGS) =09amroutine->amvacuumcleanup =3D hashvacuumcleanup; =09amroutine->amcanreturn =3D NULL; =09amroutine->amcostestimate =3D hashcostestimate; =2D=09amroutine->amoptions =3D hashoptions; =09amroutine->amproperty =3D NULL; =09amroutine->amvalidate =3D hashvalidate; =09amroutine->ambeginscan =3D hashbeginscan; @@ -89,6 +88,7 @@ hashhandler(PG_FUNCTION_ARGS) =09amroutine->amendscan =3D hashendscan; =09amroutine->ammarkpos =3D NULL; =09amroutine->amrestrpos =3D NULL; +=09amroutine->amrelopt_catalog =3D hashgetreloptcatalog; =09amroutine->amestimateparallelscan =3D NULL; =09amroutine->aminitparallelscan =3D NULL; =09amroutine->amparallelrescan =3D NULL; diff --git a/src/backend/access/hash/hashpage.c b/src/backend/access/ha= sh/hashpage.c index 1cb18a7..8330044 100644 =2D-- 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, ForkNum= ber forkNum) =09data_width =3D sizeof(uint32); =09item_width =3D MAXALIGN(sizeof(IndexTupleData)) + MAXALIGN(data_wid= th) + =09=09sizeof(ItemIdData);=09=09/* include the line pointer */ =2D=09ffactor =3D RelationGetTargetPageUsage(rel, HASH_DEFAULT_FILLFACTOR= ) / item_width; +=09ffactor =3D (BLCKSZ * HashGetFillFactor(rel) / 100) / item_width; =09/* keep to a sane range */ =09if (ffactor < 10) =09=09ffactor =3D 10; diff --git a/src/backend/access/hash/hashutil.c b/src/backend/access/ha= sh/hashutil.c index 9b803af..c5309b0 100644 =2D-- 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" =2D#include "access/reloptions.h" +#include "access/options.h" #include "access/relscan.h" #include "utils/lsyscache.h" #include "utils/rel.h" @@ -286,12 +286,6 @@ _hash_checkpage(Relation rel, Buffer buf, int flag= s) =09} } =20 =2Dbytea * =2Dhashoptions(Datum reloptions, bool validate) =2D{ =2D=09return default_reloptions(reloptions, validate, RELOPT_KIND_HASH); =2D} =2D /* * _hash_get_indextuple_hashkey - get the hash index tuple's hash key = value */ @@ -581,3 +575,28 @@ _hash_kill_items(IndexScanDesc scan) =09=09MarkBufferDirtyHint(so->hashso_curbuf, true); =09} } + +static options_catalog *hash_relopt_catalog =3D NULL; + +void * +hashgetreloptcatalog(void) +{ +=09if (!hash_relopt_catalog) +=09{ +=09=09hash_relopt_catalog =3D allocateOptionsCatalog(NULL, +=09=09=09=09=09=09=09=09=09=09=09=09 sizeof(HashRelOptions), 1); + +=09=09optionsCatalogAddItemInt(hash_relopt_catalog, "fillfactor", +=09=09=09=09=09=09=09"Packs hash index pages only to this percentage",= +=09=09=09=09=09=09=09=09 NoLock,=09=09/* No ALTER -- no lock */ +=09=09=09=09=09=09=09=09 OPTION_DEFINITION_FLAG_FORBID_ALTER,=09/* fil= lfactor is actualy +=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09 * stored in met= apage +=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09 * and should no= t be +=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09 * changed once = index is +=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09 * created */ +=09=09=09=09=09=09=09=09 offsetof(HashRelOptions, fillfactor), +=09=09=09=09=09=09=09=09 HASH_DEFAULT_FILLFACTOR, +=09=09=09=09=09=09=09=09 HASH_MIN_FILLFACTOR, 100); +=09} +=09return hash_relopt_catalog; +} diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap= /heapam.c index 9766723..6780ef8 100644 =2D-- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -2670,7 +2670,10 @@ heap_multi_insert(Relation relation, HeapTuple *= tuples, int ntuples, =09bool=09=09need_cids =3D RelationIsAccessibleInLogicalDecoding(relat= ion); =20 =09needwal =3D !(options & HEAP_INSERT_SKIP_WAL) && RelationNeedsWAL(r= elation); =2D=09saveFreeSpace =3D RelationGetTargetPageFreeSpace(relation, +=09if (IsToastRelation(relation)) +=09=09saveFreeSpace =3D ToastGetTargetPageFreeSpace(); +=09else +=09=09saveFreeSpace =3D HeapGetTargetPageFreeSpace(relation, =09=09=09=09=09=09=09=09=09=09=09=09 HEAP_DEFAULT_FILLFACTOR); =20 =09/* Toast and set header data in all the tuples */ diff --git a/src/backend/access/heap/hio.c b/src/backend/access/heap/hi= o.c index 13e3bdc..8c8f023 100644 =2D-- a/src/backend/access/heap/hio.c +++ b/src/backend/access/heap/hio.c @@ -323,7 +323,10 @@ RelationGetBufferForTuple(Relation relation, Size = len, =09=09=09=09=09=09len, MaxHeapTupleSize))); =20 =09/* Compute desired extra freespace due to fillfactor option */ =2D=09saveFreeSpace =3D RelationGetTargetPageFreeSpace(relation, +=09if (IsToastRelation(relation)) +=09=09saveFreeSpace =3D ToastGetTargetPageFreeSpace(); +=09else +=09=09saveFreeSpace =3D HeapGetTargetPageFreeSpace(relation, =09=09=09=09=09=09=09=09=09=09=09=09 HEAP_DEFAULT_FILLFACTOR); =20 =09if (otherBuffer !=3D InvalidBuffer) diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/h= eap/pruneheap.c index 52231ac..0686cf0 100644 =2D-- a/src/backend/access/heap/pruneheap.c +++ b/src/backend/access/heap/pruneheap.c @@ -130,7 +130,11 @@ heap_page_prune_opt(Relation relation, Buffer buff= er) =09 * important than sometimes getting a wrong answer in what is after= all =09 * just a heuristic estimate. =09 */ =2D=09minfree =3D RelationGetTargetPageFreeSpace(relation, + +=09if (IsToastRelation(relation)) +=09=09minfree =3D ToastGetTargetPageFreeSpace(); +=09else +=09=09minfree =3D HeapGetTargetPageFreeSpace(relation, =09=09=09=09=09=09=09=09=09=09=09 HEAP_DEFAULT_FILLFACTOR); =09minfree =3D Max(minfree, BLCKSZ / 10); =20 diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access= /heap/rewriteheap.c index bd560e4..b40abc9 100644 =2D-- a/src/backend/access/heap/rewriteheap.c +++ b/src/backend/access/heap/rewriteheap.c @@ -667,7 +667,10 @@ raw_heap_insert(RewriteState state, HeapTuple tup)= =09=09=09=09=09=09len, MaxHeapTupleSize))); =20 =09/* Compute desired extra freespace due to fillfactor option */ =2D=09saveFreeSpace =3D RelationGetTargetPageFreeSpace(state->rs_new_rel,= +=09if (IsToastRelation(state->rs_new_rel)) +=09=09saveFreeSpace =3D ToastGetTargetPageFreeSpace(); +=09else +=09=09saveFreeSpace =3D HeapGetTargetPageFreeSpace(state->rs_new_rel, =09=09=09=09=09=09=09=09=09=09=09=09 HEAP_DEFAULT_FILLFACTOR); =20 =09/* Now we can check to see if there's enough free space already. */= diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access= /nbtree/nbtinsert.c index 4aca7e4..f37054a 100644 =2D-- a/src/backend/access/nbtree/nbtinsert.c +++ b/src/backend/access/nbtree/nbtinsert.c @@ -1433,8 +1433,7 @@ _bt_findsplitloc(Relation rel, =09state.is_rightmost =3D P_RIGHTMOST(opaque); =09state.have_split =3D false; =09if (state.is_leaf) =2D=09=09state.fillfactor =3D RelationGetFillFactor(rel, =2D=09=09=09=09=09=09=09=09=09=09=09=09 BTREE_DEFAULT_FILLFACTOR); +=09=09state.fillfactor =3D BTGetFillFactor(rel); =09else =09=09state.fillfactor =3D BTREE_NONLEAF_FILLFACTOR; =09state.newitemonleft =3D false;=09/* these just to keep compiler qui= et */ diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nb= tree/nbtree.c index 3dbafdd..d164e6c 100644 =2D-- 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 =2D /* Working state for btbuild and its callback */ typedef struct { @@ -115,8 +115,7 @@ static void btvacuumscan(IndexVacuumInfo *info, Ind= exBulkDeleteResult *stats, =09=09=09 BTCycleId cycleid); static void btvacuumpage(BTVacState *vstate, BlockNumber blkno, =09=09=09 BlockNumber orig_blkno); =2D =2D +static void * btgetreloptcatalog (void); /* * Btree handler function: return IndexAmRoutine with access method pa= rameters * and callbacks. @@ -149,7 +148,6 @@ bthandler(PG_FUNCTION_ARGS) =09amroutine->amvacuumcleanup =3D btvacuumcleanup; =09amroutine->amcanreturn =3D btcanreturn; =09amroutine->amcostestimate =3D btcostestimate; =2D=09amroutine->amoptions =3D btoptions; =09amroutine->amproperty =3D btproperty; =09amroutine->amvalidate =3D btvalidate; =09amroutine->ambeginscan =3D btbeginscan; @@ -159,6 +157,7 @@ bthandler(PG_FUNCTION_ARGS) =09amroutine->amendscan =3D btendscan; =09amroutine->ammarkpos =3D btmarkpos; =09amroutine->amrestrpos =3D btrestrpos; +=09amroutine->amrelopt_catalog =3D btgetreloptcatalog; =09amroutine->amestimateparallelscan =3D btestimateparallelscan; =09amroutine->aminitparallelscan =3D btinitparallelscan; =09amroutine->amparallelrescan =3D btparallelrescan; @@ -1390,3 +1389,26 @@ btcanreturn(Relation index, int attno) { =09return true; } + + +static options_catalog *bt_relopt_catalog =3D NULL; + +static void * +btgetreloptcatalog(void) +{ +=09if (!bt_relopt_catalog) +=09{ +=09=09bt_relopt_catalog =3D allocateOptionsCatalog(NULL, +=09=09=09=09=09=09=09=09=09=09=09=09 sizeof(BTRelOptions), 1); + +=09=09optionsCatalogAddItemInt(bt_relopt_catalog, "fillfactor", +=09=09=09=09=09=09 "Packs btree index pages only to this percentage"= , +=09=09=09=09=09=09=09=09 ShareUpdateExclusiveLock,=09=09/* since it ap= plies only +=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09 * to later inserts */= +=09=09=09=09=09=09=09=09 0, +=09=09=09=09=09=09=09=09 offsetof(BTRelOptions, fillfactor), +=09=09=09=09=09=09=09=09 BTREE_DEFAULT_FILLFACTOR, +=09=09=09=09=09=09=09=09 BTREE_MIN_FILLFACTOR, 100); +=09} +=09return bt_relopt_catalog; +} diff --git a/src/backend/access/nbtree/nbtsort.c b/src/backend/access/n= btree/nbtsort.c index bf6c03c..5c6957a 100644 =2D-- a/src/backend/access/nbtree/nbtsort.c +++ b/src/backend/access/nbtree/nbtsort.c @@ -344,8 +344,8 @@ _bt_pagestate(BTWriteState *wstate, uint32 level) =09if (level > 0) =09=09state->btps_full =3D (BLCKSZ * (100 - BTREE_NONLEAF_FILLFACTOR) = / 100); =09else =2D=09=09state->btps_full =3D RelationGetTargetPageFreeSpace(wstate->inde= x, =2D=09=09=09=09=09=09=09=09=09=09=09=09=09=09 BTREE_DEFAULT_FILLFACTOR);= +=09=09state->btps_full =3D (BLCKSZ * (100 - BTGetFillFactor(wstate->i= ndex)) / 100); + =09/* no parent level, yet */ =09state->btps_next =3D NULL; =20 diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/= nbtree/nbtutils.c index dbfb775..b01d22e 100644 =2D-- a/src/backend/access/nbtree/nbtutils.c +++ b/src/backend/access/nbtree/nbtutils.c @@ -18,7 +18,7 @@ #include =20 #include "access/nbtree.h" =2D#include "access/reloptions.h" +#include "storage/lock.h" #include "access/relscan.h" #include "miscadmin.h" #include "utils/array.h" @@ -2038,12 +2038,6 @@ BTreeShmemInit(void) =09=09Assert(found); } =20 =2Dbytea * =2Dbtoptions(Datum reloptions, bool validate) =2D{ =2D=09return default_reloptions(reloptions, validate, RELOPT_KIND_BTREE);= =2D} =2D /* *=09btproperty() -- Check boolean properties of indexes. * diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/= spgist/spgutils.c index 8656af4..022e893 100644 =2D-- a/src/backend/access/spgist/spgutils.c +++ b/src/backend/access/spgist/spgutils.c @@ -15,7 +15,7 @@ =20 #include "postgres.h" =20 =2D#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) =09amroutine->amvacuumcleanup =3D spgvacuumcleanup; =09amroutine->amcanreturn =3D spgcanreturn; =09amroutine->amcostestimate =3D spgcostestimate; =2D=09amroutine->amoptions =3D spgoptions; =09amroutine->amproperty =3D NULL; =09amroutine->amvalidate =3D spgvalidate; =09amroutine->ambeginscan =3D spgbeginscan; @@ -69,6 +68,7 @@ spghandler(PG_FUNCTION_ARGS) =09amroutine->amendscan =3D spgendscan; =09amroutine->ammarkpos =3D NULL; =09amroutine->amrestrpos =3D NULL; +=09amroutine->amrelopt_catalog =3D spggetreloptcatalog; =09amroutine->amestimateparallelscan =3D NULL; =09amroutine->aminitparallelscan =3D NULL; =09amroutine->amparallelrescan =3D NULL; @@ -372,8 +372,8 @@ SpGistGetBuffer(Relation index, int flags, int need= Space, bool *isNew) =09 * related to the ones already on it. But fillfactor mustn't cause= an =09 * error for requests that would otherwise be legal. =09 */ =2D=09needSpace +=3D RelationGetTargetPageFreeSpace(index, =2D=09=09=09=09=09=09=09=09=09=09=09=09SPGIST_DEFAULT_FILLFACTOR); +=09needSpace +=3D (BLCKSZ * (100 - SpGistGetFillFactor(index)) / 100);= + =09needSpace =3D Min(needSpace, SPGIST_PAGE_CAPACITY); =20 =09/* Get the cache entry for this flags setting */ @@ -537,15 +537,6 @@ SpGistInitMetapage(Page page) } =20 /* =2D * reloptions processing for SPGiST =2D */ =2Dbytea * =2Dspgoptions(Datum reloptions, bool validate) =2D{ =2D=09return default_reloptions(reloptions, validate, RELOPT_KIND_SPGIST)= ; =2D} =2D =2D/* * Get the space needed to store a non-null datum of the indicated typ= e. * Note the result is already rounded up to a MAXALIGN boundary. * Also, we follow the SPGiST convention that pass-by-val types are @@ -911,3 +902,27 @@ SpGistPageAddNewItem(SpGistState *state, Page page= , Item item, Size size, =20 =09return offnum; } + + + +static options_catalog *spgist_relopt_catalog =3D NULL; + +void * +spggetreloptcatalog(void) +{ +=09if (!spgist_relopt_catalog) +=09{ +=09=09spgist_relopt_catalog =3D allocateOptionsCatalog(NULL, +=09=09=09=09=09=09=09=09=09=09=09=09sizeof(SpGistRelOptions), 1); + +=09=09optionsCatalogAddItemInt(spgist_relopt_catalog, "fillfactor", +=09=09=09=09=09=09 "Packs spgist index pages only to this percentage"= , +=09=09=09=09=09=09=09=09 ShareUpdateExclusiveLock,=09=09/* since it ap= plies only +=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09 * to later inserts */= +=09=09=09=09=09=09=09=09 0, +=09=09=09=09=09=09=09=09 offsetof(SpGistRelOptions, fillfactor), +=09=09=09=09=09=09=09=09 SPGIST_DEFAULT_FILLFACTOR, +=09=09=09=09=09=09=09=09 SPGIST_MIN_FILLFACTOR, 100); +=09} +=09return spgist_relopt_catalog; +} diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toast= ing.c index 29756eb..aaa9270 100644 =2D-- a/src/backend/catalog/toasting.c +++ b/src/backend/catalog/toasting.c @@ -36,7 +36,7 @@ /* Potentially set by pg_upgrade_support functions */ Oid=09=09=09binary_upgrade_next_toast_pg_type_oid =3D InvalidOid; =20 =2Dstatic void CheckAndCreateToastTable(Oid relOid, Datum reloptions, +static bool CheckAndCreateToastTable(Oid relOid, Datum reloptions, =09=09=09=09=09=09 LOCKMODE lockmode, bool check); static bool create_toast_table(Relation rel, Oid toastOid, Oid toastIn= dexOid, =09=09=09=09 Datum reloptions, LOCKMODE lockmode, bool check); @@ -67,23 +67,26 @@ NewHeapCreateToastTable(Oid relOid, Datum reloption= s, LOCKMODE lockmode) =09CheckAndCreateToastTable(relOid, reloptions, lockmode, false); } =20 =2Dvoid +bool NewRelationCreateToastTable(Oid relOid, Datum reloptions) { =2D=09CheckAndCreateToastTable(relOid, reloptions, AccessExclusiveLock, f= alse); +=09return CheckAndCreateToastTable(relOid, reloptions, +=09=09=09=09=09=09=09=09=09AccessExclusiveLock, false); } =20 =2Dstatic void +static bool CheckAndCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmo= de, bool check) { =09Relation=09rel; +=09bool=09=09success; =20 =09rel =3D heap_open(relOid, lockmode); =20 =09/* create_toast_table does all the work */ =2D=09(void) create_toast_table(rel, InvalidOid, InvalidOid, reloptions, = lockmode, check); +=09success =3D create_toast_table(rel, InvalidOid, InvalidOid, relopti= ons, lockmode, check); =20 =09heap_close(rel, NoLock); +=09return success; } =20 /* diff --git a/src/backend/commands/createas.c b/src/backend/commands/cre= ateas.c index 97f9c55..79c5147 100644 =2D-- a/src/backend/commands/createas.c +++ b/src/backend/commands/createas.c @@ -89,6 +89,8 @@ create_ctas_internal(List *attrList, IntoClause *into= ) =09Datum=09=09toast_options; =09static char *validnsps[] =3D HEAP_RELOPT_NAMESPACES; =09ObjectAddress intoRelationAddr; +=09List=09 *toastDefList; +=09bool=09=09toast_created; =20 =09/* This code supports both CREATE TABLE AS and CREATE MATERIALIZED = VIEW */ =09is_matview =3D (into->viewQuery !=3D NULL); @@ -122,15 +124,20 @@ create_ctas_internal(List *attrList, IntoClause *= into) =09CommandCounterIncrement(); =20 =09/* parse and validate reloptions for the toast table */ =2D=09toast_options =3D transformRelOptions((Datum) 0, =2D=09=09=09=09=09=09=09=09=09=09create->options, =2D=09=09=09=09=09=09=09=09=09=09"toast", =2D=09=09=09=09=09=09=09=09=09=09validnsps, =2D=09=09=09=09=09=09=09=09=09=09true, false); =20 =2D=09(void) heap_reloptions(RELKIND_TOASTVALUE, toast_options, true); =20 =2D=09NewRelationCreateToastTable(intoRelationAddr.objectId, toast_option= s); +=09optionsDefListValdateNamespaces(create->options, validnsps); +=09toastDefList =3D optionsDefListFilterNamespaces(create->options, "t= oast"); + +=09toast_options =3D transformOptions(get_toast_relopt_catalog(), (Dat= um) 0, +=09=09=09=09=09=09=09=09=09 toastDefList, 0); + +=09toast_created =3D NewRelationCreateToastTable(intoRelationAddr.obje= ctId, +=09=09=09=09=09=09=09=09=09=09=09=09toast_options); +=09if (!toast_created && toast_options) +=09=09ereport(ERROR, +=09=09=09=09(errcode(ERRCODE_INVALID_PARAMETER_VALUE), +=09=09=09=09 errmsg("no TOAST relation was created for a table. Can't = set toast.* storage parameters"))); =20 =09/* Create the "view" part of a materialized view. */ =09if (is_matview) diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/= foreigncmds.c index 9ad9915..6f78d7b 100644 =2D-- a/src/backend/commands/foreigncmds.c +++ b/src/backend/commands/foreigncmds.c @@ -111,7 +111,7 @@ transformGenericOptions(Oid catalogId, =09=09=09=09=09=09List *options, =09=09=09=09=09=09Oid fdwvalidator) { =2D=09List=09 *resultOptions =3D untransformRelOptions(oldOptions); +=09List=09 *resultOptions =3D optionsTextArrayToDefList(oldOptions);= =09ListCell *optcell; =09Datum=09=09result; =20 diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/in= dexcmds.c index 620704e..c3c8b21 100644 =2D-- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -329,7 +329,7 @@ DefineIndex(Oid relationId, =09Form_pg_am=09accessMethodForm; =09IndexAmRoutine *amRoutine; =09bool=09=09amcanorder; =2D=09amoptions_function amoptions; +=09amrelopt_catalog_function amoption_catalog; =09Datum=09=09reloptions; =09int16=09 *coloptions; =09IndexInfo *indexInfo; @@ -536,7 +536,7 @@ DefineIndex(Oid relationId, =09=09=09=09=09=09accessMethodName))); =20 =09amcanorder =3D amRoutine->amcanorder; =2D=09amoptions =3D amRoutine->amoptions; +=09amoption_catalog =3D amRoutine->amrelopt_catalog; =20 =09pfree(amRoutine); =09ReleaseSysCache(tuple); @@ -550,10 +550,18 @@ DefineIndex(Oid relationId, =09/* =09 * Parse AM-specific options, convert to text array form, validate.= =09 */ =2D=09reloptions =3D transformRelOptions((Datum) 0, stmt->options, =2D=09=09=09=09=09=09=09=09=09 NULL, NULL, false, false); =2D =2D=09(void) index_reloptions(amoptions, reloptions, true); +=09if (amoption_catalog) +=09{ +=09=09reloptions =3D transformOptions(amoption_catalog(), +=09=09=09=09=09=09=09=09=09 (Datum) 0, stmt->options, 0); +=09} +=09else +=09{ +=09=09ereport(ERROR, +=09=09=09=09(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), +=09=09=09=09 errmsg("access method %s does not support options", +=09=09=09=09=09=09accessMethodName))); +=09} =20 =09/* =09 * Prepare arguments for index_create, primarily an IndexInfo struc= ture. diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/ta= blecmds.c index 7d9c769..0847809 100644 =2D-- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -513,7 +513,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid = ownerId, =09Datum=09=09reloptions; =09ListCell *listptr; =09AttrNumber=09attnum; =2D=09static char *validnsps[] =3D HEAP_RELOPT_NAMESPACES; =09Oid=09=09=09ofTypeId; =09ObjectAddress address; =20 @@ -599,13 +598,23 @@ DefineRelation(CreateStmt *stmt, char relkind, Oi= d ownerId, =09/* =09 * Parse and validate reloptions, if any. =09 */ =2D=09reloptions =3D transformRelOptions((Datum) 0, stmt->options, NULL, = validnsps, =2D=09=09=09=09=09=09=09=09=09 true, false); =2D =09if (relkind =3D=3D RELKIND_VIEW) =2D=09=09(void) view_reloptions(reloptions, true); +=09{ +=09=09reloptions =3D transformOptions( +=09=09=09=09=09=09=09=09=09 get_view_relopt_catalog(), +=09=09=09=09=09=09=09=09=09 (Datum) 0, stmt->options, 0); +=09} =09else =2D=09=09(void) heap_reloptions(relkind, reloptions, true); +=09{ +=09=09/* If it is not view, then it if heap */ +=09=09char=09 *namespaces[] =3D HEAP_RELOPT_NAMESPACES; +=09=09List=09 *heapDefList; + +=09=09optionsDefListValdateNamespaces(stmt->options, namespaces); +=09=09heapDefList =3D optionsDefListFilterNamespaces(stmt->options, NU= LL); +=09=09reloptions =3D transformOptions(get_heap_relopt_catalog(), +=09=09=09=09=09=09=09=09=09 (Datum) 0, heapDefList, 0); +=09} =20 =09if (stmt->ofTypename) =09{ @@ -3113,7 +3122,8 @@ void AlterTableInternal(Oid relid, List *cmds, bool recurse) { =09Relation=09rel; =2D=09LOCKMODE=09lockmode =3D AlterTableGetLockLevel(cmds); + +=09LOCKMODE=09lockmode =3D AlterTableGetLockLevel(relid, cmds); =20 =09rel =3D relation_open(relid, lockmode); =20 @@ -3155,13 +3165,15 @@ AlterTableInternal(Oid relid, List *cmds, bool = recurse) * otherwise we might end up with an inconsistent dump that can't rest= ore. */ LOCKMODE =2DAlterTableGetLockLevel(List *cmds) + +AlterTableGetLockLevel(Oid relid, List *cmds) { =09/* =09 * This only works if we read catalog tables using MVCC snapshots. =09 */ =09ListCell *lcmd; =09LOCKMODE=09lockmode =3D ShareUpdateExclusiveLock; +=09Relation=09rel; =20 =09foreach(lcmd, cmds) =09{ @@ -3372,7 +3384,10 @@ AlterTableGetLockLevel(List *cmds) =09=09=09=09=09=09=09=09=09 * getTables() */ =09=09=09case AT_ResetRelOptions:=09/* Uses MVCC in getIndexes() and =09=09=09=09=09=09=09=09=09=09 * getTables() */ =2D=09=09=09=09cmd_lockmode =3D AlterTableGetRelOptionsLockLevel((List *)= cmd->def); +=09=09=09=09rel =3D relation_open(relid, NoLock); +=09=09=09=09cmd_lockmode =3D AlterTableGetRelOptionsLockLevel(rel, +=09=09=09=09=09=09=09=09=09=09=09=09=09castNode(List, cmd->def)); +=09=09=09=09relation_close(rel,NoLock); =09=09=09=09break; =20 =09=09=09case AT_AttachPartition: @@ -4147,7 +4162,7 @@ ATRewriteTables(AlterTableStmt *parsetree, List *= *wqueue, LOCKMODE lockmode) =09=09=09=09=09=09 errmsg("cannot rewrite system relation \"%s\"", =09=09=09=09=09=09=09=09RelationGetRelationName(OldHeap)))); =20 =2D=09=09=09if (RelationIsUsedAsCatalogTable(OldHeap)) +=09=09=09if (HeapIsUsedAsCatalogTable(OldHeap)) =09=09=09=09ereport(ERROR, =09=09=09=09=09=09(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), =09=09=09=09=09=09 errmsg("cannot rewrite table \"%s\" used as a catal= og table", @@ -6228,11 +6243,11 @@ ATExecSetOptions(Relation rel, const char *colN= ame, Node *options, =09/* Generate new proposed attoptions (text array) */ =09datum =3D SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attopti= ons, =09=09=09=09=09=09=09&isnull); =2D=09newOptions =3D transformRelOptions(isnull ? (Datum) 0 : datum, =2D=09=09=09=09=09=09=09=09=09 castNode(List, options), NULL, NULL, =2D=09=09=09=09=09=09=09=09=09 false, isReset); =2D=09/* Validate new options */ =2D=09(void) attribute_reloptions(newOptions, true); + +=09newOptions =3D transformOptions(get_attribute_options_catalog(), +=09=09=09=09=09=09=09=09 isnull ? (Datum) 0 : datum, +=09=09=09=09=09 castNode(List, options), OPTIONS_PARSE_MODE_FOR_ALTER= | +=09=09=09=09=09=09=09 (isReset ? OPTIONS_PARSE_MODE_FOR_RESET : 0));= =20 =09/* Build new tuple. */ =09memset(repl_null, false, sizeof(repl_null)); @@ -10256,7 +10271,7 @@ ATExecSetRelOptions(Relation rel, List *defList= , AlterTableType operation, =09Datum=09=09repl_val[Natts_pg_class]; =09bool=09=09repl_null[Natts_pg_class]; =09bool=09=09repl_repl[Natts_pg_class]; =2D=09static char *validnsps[] =3D HEAP_RELOPT_NAMESPACES; +=09options_parse_mode parse_mode; =20 =09if (defList =3D=3D NIL && operation !=3D AT_ReplaceRelOptions) =09=09return;=09=09=09=09=09/* nothing to do */ @@ -10285,25 +10300,52 @@ ATExecSetRelOptions(Relation rel, List *defLi= st, AlterTableType operation, =09=09=09=09=09=09=09=09&isnull); =09} =20 =2D=09/* Generate new proposed reloptions (text array) */ =2D=09newOptions =3D transformRelOptions(isnull ? (Datum) 0 : datum, =2D=09=09=09=09=09=09=09=09=09 defList, NULL, validnsps, false, =2D=09=09=09=09=09=09=09=09=09 operation =3D=3D AT_ResetRelOptions); =2D =09/* Validate */ +=09parse_mode =3D OPTIONS_PARSE_MODE_FOR_ALTER; +=09if (operation =3D=3D AT_ResetRelOptions) +=09=09parse_mode |=3D OPTIONS_PARSE_MODE_FOR_RESET; + =09switch (rel->rd_rel->relkind) =09{ =09=09case RELKIND_RELATION: =09=09case RELKIND_TOASTVALUE: =09=09case RELKIND_MATVIEW: =09=09case RELKIND_PARTITIONED_TABLE: =2D=09=09=09(void) heap_reloptions(rel->rd_rel->relkind, newOptions, true= ); +=09=09=09{ +=09=09=09=09char=09 *namespaces[] =3D HEAP_RELOPT_NAMESPACES; +=09=09=09=09List=09 *heapDefList; + +=09=09=09=09optionsDefListValdateNamespaces(defList, namespaces); +=09=09=09=09heapDefList =3D optionsDefListFilterNamespaces( +=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09 defList, NULL); +=09=09=09=09newOptions =3D transformOptions(get_heap_relopt_catalog(),= +=09=09=09=09=09=09=09=09=09=09=09 isnull ? (Datum) 0 : datum, +=09=09=09=09=09=09=09=09=09=09=09 heapDefList, parse_mode); +=09=09=09} =09=09=09break; =09=09case RELKIND_VIEW: =2D=09=09=09(void) view_reloptions(newOptions, true); +=09=09=09{ +=09=09=09=09newOptions =3D transformOptions( +=09=09=09=09=09=09=09=09=09=09=09 get_view_relopt_catalog(), +=09=09=09=09=09=09=09=09=09=09=09 isnull ? (Datum) 0 : datum, +=09=09=09=09=09=09=09=09=09=09=09 defList, parse_mode); +=09=09=09} =09=09=09break; =09=09case RELKIND_INDEX: =2D=09=09=09(void) index_reloptions(rel->rd_amroutine->amoptions, newOpti= ons, true); +=09=09=09if (rel->rd_amroutine->amrelopt_catalog) +=09=09=09{ +=09=09=09=09newOptions =3D transformOptions( +=09=09=09=09=09=09=09=09=09 rel->rd_amroutine->amrelopt_catalog(), +=09=09=09=09=09=09=09=09=09=09=09 isnull ? (Datum) 0 : datum, +=09=09=09=09=09=09=09=09=09=09=09 defList, parse_mode); +=09=09=09} +=09=09=09else +=09=09=09{ +=09=09=09=09ereport(ERROR, +=09=09=09=09=09=09(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), +=09=09=09=09=09=09 errmsg("index %s does not support options", +=09=09=09=09=09=09=09=09RelationGetRelationName(rel)))); +=09=09=09} =09=09=09break; =09=09default: =09=09=09ereport(ERROR, @@ -10317,7 +10359,7 @@ ATExecSetRelOptions(Relation rel, List *defList= , AlterTableType operation, =09if (rel->rd_rel->relkind =3D=3D RELKIND_VIEW) =09{ =09=09Query=09 *view_query =3D get_view_query(rel); =2D=09=09List=09 *view_options =3D untransformRelOptions(newOptions); +=09=09List=09 *view_options =3D optionsTextArrayToDefList(newOptions= ); =09=09ListCell *cell; =09=09bool=09=09check_option =3D false; =20 @@ -10377,6 +10419,8 @@ ATExecSetRelOptions(Relation rel, List *defList= , AlterTableType operation, =09{ =09=09Relation=09toastrel; =09=09Oid=09=09=09toastid =3D rel->rd_rel->reltoastrelid; +=09=09List=09 *toastDefList; +=09=09options_parse_mode parse_mode; =20 =09=09toastrel =3D heap_open(toastid, lockmode); =20 @@ -10400,12 +10444,15 @@ ATExecSetRelOptions(Relation rel, List *defLi= st, AlterTableType operation, =09=09=09datum =3D SysCacheGetAttr(RELOID, tuple, Anum_pg_class_relopt= ions, =09=09=09=09=09=09=09=09=09&isnull); =09=09} +=09=09parse_mode =3D OPTIONS_PARSE_MODE_FOR_ALTER; +=09=09if (operation =3D=3D AT_ResetRelOptions) +=09=09=09parse_mode |=3D OPTIONS_PARSE_MODE_FOR_RESET; =20 =2D=09=09newOptions =3D transformRelOptions(isnull ? (Datum) 0 : datum, =2D=09=09=09=09=09=09=09=09=09=09 defList, "toast", validnsps, false, =2D=09=09=09=09=09=09=09=09=09=09 operation =3D=3D AT_ResetRelOptions); +=09=09toastDefList =3D optionsDefListFilterNamespaces(defList, "toast"= ); =20 =2D=09=09(void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true); +=09=09newOptions =3D transformOptions(get_toast_relopt_catalog(), +=09=09=09=09=09=09=09=09=09 isnull ? (Datum) 0 : datum, +=09=09=09=09=09=09=09=09=09 toastDefList, parse_mode); =20 =09=09memset(repl_val, 0, sizeof(repl_val)); =09=09memset(repl_null, false, sizeof(repl_null)); @@ -10433,6 +10480,18 @@ ATExecSetRelOptions(Relation rel, List *defLis= t, AlterTableType operation, =20 =09=09heap_close(toastrel, NoLock); =09} +=09else +=09{ +=09=09List=09 *toastDefList; + +=09=09toastDefList =3D optionsDefListFilterNamespaces(defList, "toast"= ); +=09=09if (toastDefList) +=09=09{ +=09=09=09ereport(ERROR, +=09=09=09=09=09(errcode(ERRCODE_INVALID_PARAMETER_VALUE), +=09=09=09=09=09 errmsg("TOAST relation for this table does not exist. = Can't change toast.* storage parameters"))); +=09=09} +=09} =20 =09heap_close(pgclass, RowExclusiveLock); } diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/t= ablespace.c index 8559c3b..3498fef 100644 =2D-- a/src/backend/commands/tablespace.c +++ b/src/backend/commands/tablespace.c @@ -332,10 +332,11 @@ CreateTableSpace(CreateTableSpaceStmt *stmt) =09nulls[Anum_pg_tablespace_spcacl - 1] =3D true; =20 =09/* Generate new proposed spcoptions (text array) */ =2D=09newOptions =3D transformRelOptions((Datum) 0, =2D=09=09=09=09=09=09=09=09=09 stmt->options, =2D=09=09=09=09=09=09=09=09=09 NULL, NULL, false, false); =2D=09(void) tablespace_reloptions(newOptions, true); + +=09newOptions =3D transformOptions( +=09=09=09=09=09=09=09=09 get_tablespace_options_catalog(), +=09=09=09=09=09=09=09=09 (Datum) 0, stmt->options, 0); + =09if (newOptions !=3D (Datum) 0) =09=09values[Anum_pg_tablespace_spcoptions - 1] =3D newOptions; =09else @@ -1023,10 +1024,13 @@ AlterTableSpaceOptions(AlterTableSpaceOptionsSt= mt *stmt) =09/* Generate new proposed spcoptions (text array) */ =09datum =3D heap_getattr(tup, Anum_pg_tablespace_spcoptions, =09=09=09=09=09=09 RelationGetDescr(rel), &isnull); =2D=09newOptions =3D transformRelOptions(isnull ? (Datum) 0 : datum, =2D=09=09=09=09=09=09=09=09=09 stmt->options, NULL, NULL, false, =2D=09=09=09=09=09=09=09=09=09 stmt->isReset); =2D=09(void) tablespace_reloptions(newOptions, true); + + +=09newOptions =3D transformOptions(get_tablespace_options_catalog(), +=09=09=09=09=09=09=09=09 isnull ? (Datum) 0 : datum, +=09=09=09=09=09=09=09=09 stmt->options, +=09=09=09=09=09=09=09=09 OPTIONS_PARSE_MODE_FOR_ALTER | +=09=09=09=09=09=09 (stmt->isReset ? OPTIONS_PARSE_MODE_FOR_RESET : 0))= ; =20 =09/* Build new tuple. */ =09memset(repl_null, false, sizeof(repl_null)); diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreig= n.c index a113bf5..eb564fd 100644 =2D-- a/src/backend/foreign/foreign.c +++ b/src/backend/foreign/foreign.c @@ -62,7 +62,7 @@ GetForeignDataWrapper(Oid fdwid) =09if (isnull) =09=09fdw->options =3D NIL; =09else =2D=09=09fdw->options =3D untransformRelOptions(datum); +=09=09fdw->options =3D optionsTextArrayToDefList(datum); =20 =09ReleaseSysCache(tp); =20 @@ -133,7 +133,7 @@ GetForeignServer(Oid serverid) =09if (isnull) =09=09server->options =3D NIL; =09else =2D=09=09server->options =3D untransformRelOptions(datum); +=09=09server->options =3D optionsTextArrayToDefList(datum); =20 =09ReleaseSysCache(tp); =20 @@ -201,7 +201,7 @@ GetUserMapping(Oid userid, Oid serverid) =09if (isnull) =09=09um->options =3D NIL; =09else =2D=09=09um->options =3D untransformRelOptions(datum); +=09=09um->options =3D optionsTextArrayToDefList(datum); =20 =09ReleaseSysCache(tp); =20 @@ -238,7 +238,7 @@ GetForeignTable(Oid relid) =09if (isnull) =09=09ft->options =3D NIL; =09else =2D=09=09ft->options =3D untransformRelOptions(datum); +=09=09ft->options =3D optionsTextArrayToDefList(datum); =20 =09ReleaseSysCache(tp); =20 @@ -271,7 +271,7 @@ GetForeignColumnOptions(Oid relid, AttrNumber attnu= m) =09if (isnull) =09=09options =3D NIL; =09else =2D=09=09options =3D untransformRelOptions(datum); +=09=09options =3D optionsTextArrayToDefList(datum); =20 =09ReleaseSysCache(tp); =20 @@ -540,7 +540,7 @@ pg_options_to_table(PG_FUNCTION_ARGS) =09Datum=09=09array =3D PG_GETARG_DATUM(0); =20 =09deflist_to_tuplestore((ReturnSetInfo *) fcinfo->resultinfo, =2D=09=09=09=09=09=09 untransformRelOptions(array)); +=09=09=09=09=09=09 optionsTextArrayToDefList(array)); =20 =09return (Datum) 0; } @@ -611,7 +611,7 @@ is_conninfo_option(const char *option, Oid context)= Datum postgresql_fdw_validator(PG_FUNCTION_ARGS) { =2D=09List=09 *options_list =3D untransformRelOptions(PG_GETARG_DATUM(0= )); +=09List=09 *options_list =3D optionsTextArrayToDefList(PG_GETARG_DAT= UM(0)); =09Oid=09=09=09catalog =3D PG_GETARG_OID(1); =20 =09ListCell *cell; diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimiz= er/util/plancat.c index dc0b0b0..0bf8492 100644 =2D-- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -140,7 +140,7 @@ get_relation_info(PlannerInfo *root, Oid relationOb= jectId, bool inhparent, =09=09=09=09=09=09 &rel->pages, &rel->tuples, &rel->allvisfrac); =20 =09/* Retrieve the parallel_workers reloption, or -1 if not set. */ =2D=09rel->rel_parallel_workers =3D RelationGetParallelWorkers(relation, = =2D1); +=09rel->rel_parallel_workers =3D HeapGetParallelWorkers(relation, -1);= =20 =09/* =09 * 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/par= se_clause.c index 9ff80b8..35e8008 100644 =2D-- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -3148,7 +3148,7 @@ transformOnConflictArbiter(ParseState *pstate, =09=09=09=09=09=09=09=09=09exprLocation((Node *) onConflictClause))));= =20 =09/* Same applies to table used by logical decoding as catalog table = */ =2D=09if (RelationIsUsedAsCatalogTable(pstate->p_target_relation)) +=09if (HeapIsUsedAsCatalogTable(pstate->p_target_relation)) =09=09ereport(ERROR, =09=09=09=09(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), =09=09=09=09 errmsg("ON CONFLICT is not supported on table \"%s\" used= as a catalog table", diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/pa= rse_utilcmd.c index ee5f3a3..b528068 100644 =2D-- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -1520,7 +1520,7 @@ generateClonedIndexStmt(CreateStmtContext *cxt, R= elation source_idx, =09datum =3D SysCacheGetAttr(RELOID, ht_idxrel, =09=09=09=09=09=09=09Anum_pg_class_reloptions, &isnull); =09if (!isnull) =2D=09=09index->options =3D untransformRelOptions(datum); +=09=09index->options =3D optionsTextArrayToDefList(datum); =20 =09/* If it's a partial index, decompile and append the predicate */ =09datum =3D SysCacheGetAttr(INDEXRELID, ht_idx, diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmast= er/autovacuum.c index 00b1e82..506748d 100644 =2D-- a/src/backend/postmaster/autovacuum.c +++ b/src/backend/postmaster/autovacuum.c @@ -2736,6 +2736,7 @@ extract_autovac_opts(HeapTuple tup, TupleDesc pg_= class_desc) { =09bytea=09 *relopts; =09AutoVacOpts *av; +=09AutoVacOpts *src; =20 =09Assert(((Form_pg_class) GETSTRUCT(tup))->relkind =3D=3D RELKIND_REL= ATION || =09=09 ((Form_pg_class) GETSTRUCT(tup))->relkind =3D=3D RELKIND_MATV= IEW || @@ -2745,8 +2746,13 @@ extract_autovac_opts(HeapTuple tup, TupleDesc pg= _class_desc) =09if (relopts =3D=3D NULL) =09=09return NULL; =20 +=09if (((Form_pg_class) GETSTRUCT(tup))->relkind =3D=3D RELKIND_TOASTV= ALUE) +=09=09src =3D &(((ToastOptions *) relopts)->autovacuum); +=09else +=09=09src =3D &(((HeapOptions *) relopts)->autovacuum); + =09av =3D palloc(sizeof(AutoVacOpts)); =2D=09memcpy(av, &(((StdRdOptions *) relopts)->autovacuum), sizeof(AutoVa= cOpts)); +=09memcpy(av, src, sizeof(AutoVacOpts)); =09pfree(relopts); =20 =09return av; diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite= /rewriteHandler.c index 6225216..f822968 100644 =2D-- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -1531,7 +1531,7 @@ ApplyRetrieveRule(Query *parsetree, =20 =09rte->rtekind =3D RTE_SUBQUERY; =09rte->relid =3D InvalidOid; =2D=09rte->security_barrier =3D RelationIsSecurityView(relation); +=09rte->security_barrier =3D ViewIsSecurityView(relation); =09rte->subquery =3D rule_action; =09rte->inh =3D false;=09=09=09/* must not be set for a subquery */ =20 @@ -3035,7 +3035,7 @@ rewriteTargetView(Query *parsetree, Relation view= ) =20 =09=09ChangeVarNodes(viewqual, base_rt_index, new_rt_index, 0); =20 =2D=09=09if (RelationIsSecurityView(view)) +=09=09if (ViewIsSecurityView(view)) =09=09{ =09=09=09/* =09=09=09 * The view's quals go in front of existing barrier quals: th= ose @@ -3071,8 +3071,9 @@ rewriteTargetView(Query *parsetree, Relation view= ) =09 */ =09if (parsetree->commandType !=3D CMD_DELETE) =09{ =2D=09=09bool=09=09has_wco =3D RelationHasCheckOption(view); =2D=09=09bool=09=09cascaded =3D RelationHasCascadedCheckOption(view); + +=09=09bool=09=09has_wco =3D ViewHasCheckOption(view); +=09=09bool=09=09cascaded =3D ViewHasCascadedCheckOption(view); =20 =09=09/* =09=09 * If the parent view has a cascaded check option, treat this vi= ew as diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index ddacac8..19dcbb1 100644 =2D-- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -1003,6 +1003,8 @@ ProcessUtilitySlow(ParseState *pstate, =09=09=09=09=09=09{ =09=09=09=09=09=09=09Datum=09=09toast_options; =09=09=09=09=09=09=09static char *validnsps[] =3D HEAP_RELOPT_NAMESPAC= ES; +=09=09=09=09=09=09=09List=09 *toastDefList; +=09=09=09=09=09=09=09bool=09=09success; =20 =09=09=09=09=09=09=09/* Create the table itself */ =09=09=09=09=09=09=09address =3D DefineRelation((CreateStmt *) stmt, @@ -1023,18 +1025,24 @@ ProcessUtilitySlow(ParseState *pstate, =09=09=09=09=09=09=09 * parse and validate reloptions for the toast =09=09=09=09=09=09=09 * table =09=09=09=09=09=09=09 */ =2D=09=09=09=09=09=09=09toast_options =3D transformRelOptions((Datum) 0, =2D=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09((CreateStmt *) stmt)-= >options, =2D=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09"toast", =2D=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09validnsps, =2D=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09true, =2D=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09false); =2D=09=09=09=09=09=09=09(void) heap_reloptions(RELKIND_TOASTVALUE, =2D=09=09=09=09=09=09=09=09=09=09=09=09 toast_options, =2D=09=09=09=09=09=09=09=09=09=09=09=09 true); =20 =2D=09=09=09=09=09=09=09NewRelationCreateToastTable(address.objectId, =2D=09=09=09=09=09=09=09=09=09=09=09=09=09=09toast_options); +=09=09=09=09=09=09=09optionsDefListValdateNamespaces( +=09=09=09=09=09=09=09=09=09=09=09 ((CreateStmt *) stmt)->options, +=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09validnsps); + +=09=09=09=09=09=09=09toastDefList =3D optionsDefListFilterNamespaces( +=09=09=09=09=09=09=09=09=09((CreateStmt *) stmt)->options, "toast"); + +=09=09=09=09=09=09=09toast_options =3D transformOptions( +=09=09=09=09=09=09=09=09=09 get_toast_relopt_catalog(), (Datum) 0, +=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09 toastDefList, 0); + +=09=09=09=09=09=09=09success =3D NewRelationCreateToastTable( +=09=09=09=09=09=09=09=09=09=09=09address.objectId, toast_options); +=09=09=09=09=09=09=09if (!success && toast_options) +=09=09=09=09=09=09=09=09ereport(ERROR, +=09=09=09=09=09=09=09=09 (errcode(ERRCODE_INVALID_PARAMETER_VALUE), +=09=09=09=09=09=09=09=09=09errmsg("no TOAST relation was created for a= table. Can't set toast.* storage parameters"))); =09=09=09=09=09=09} =09=09=09=09=09=09else if (IsA(stmt, CreateForeignTableStmt)) =09=09=09=09=09=09{ @@ -1101,8 +1109,13 @@ ProcessUtilitySlow(ParseState *pstate, =09=09=09=09=09 * lock on (for example) a relation on which we have no= =09=09=09=09=09 * permissions. =09=09=09=09=09 */ =2D=09=09=09=09=09lockmode =3D AlterTableGetLockLevel(atstmt->cmds); =2D=09=09=09=09=09relid =3D AlterTableLookupRelation(atstmt, lockmode); + +=09=09=09=09=09relid =3D AlterTableLookupRelation(atstmt, NoLock); +=09=09=09=09=09if (OidIsValid(relid)) +=09=09=09=09=09{ +=09=09=09=09=09=09lockmode =3D AlterTableGetLockLevel(relid, atstmt->c= mds); +=09=09=09=09=09=09relid =3D AlterTableLookupRelation(atstmt, lockmode)= ; +=09=09=09=09=09} =20 =09=09=09=09=09if (OidIsValid(relid)) =09=09=09=09=09{ diff --git a/src/backend/utils/cache/attoptcache.c b/src/backend/utils/= cache/attoptcache.c index da8b42d..e8c4309 100644 =2D-- a/src/backend/utils/cache/attoptcache.c +++ b/src/backend/utils/cache/attoptcache.c @@ -149,7 +149,10 @@ get_attribute_options(Oid attrelid, int attnum) =09=09=09=09opts =3D NULL; =09=09=09else =09=09=09{ =2D=09=09=09=09bytea=09 *bytea_opts =3D attribute_reloptions(datum, fal= se); +=09=09=09=09bytea=09 *bytea_opts; + +=09=09=09=09bytea_opts =3D optionsTextArrayToBytea( +=09=09=09=09=09=09=09=09=09 get_attribute_options_catalog(), datum); =20 =09=09=09=09opts =3D MemoryContextAlloc(CacheMemoryContext, =09=09=09=09=09=09=09=09=09=09 VARSIZE(bytea_opts)); diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cac= he/relcache.c index 43238dd..50a2566 100644 =2D-- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -460,7 +460,8 @@ RelationParseRelOptions(Relation relation, HeapTupl= e tuple) =09options =3D extractRelOptions(tuple, =09=09=09=09=09=09=09=09GetPgClassDescriptor(), =09=09=09=09=09=09=09=09relation->rd_rel->relkind =3D=3D RELKIND_INDEX= ? =2D=09=09=09=09=09=09=09=09relation->rd_amroutine->amoptions : NULL); +=09=09=09=09=09=09=09 relation->rd_amroutine->amrelopt_catalog : NULL= +=09=09); =20 =09/* =09 * Copy parsed data into CacheMemoryContext. To guard against the diff --git a/src/backend/utils/cache/spccache.c b/src/backend/utils/cac= he/spccache.c index eafc00f..35db485 100644 =2D-- a/src/backend/utils/cache/spccache.c +++ b/src/backend/utils/cache/spccache.c @@ -149,7 +149,10 @@ get_tablespace(Oid spcid) =09=09=09opts =3D NULL; =09=09else =09=09{ =2D=09=09=09bytea=09 *bytea_opts =3D tablespace_reloptions(datum, false= ); +=09=09=09bytea=09 *bytea_opts; + +=09=09=09bytea_opts =3D optionsTextArrayToBytea( +=09=09=09=09=09=09=09=09=09get_tablespace_options_catalog(), datum); =20 =09=09=09opts =3D MemoryContextAlloc(CacheMemoryContext, VARSIZE(bytea= _opts)); =09=09=09memcpy(opts, bytea_opts, VARSIZE(bytea_opts)); @@ -194,7 +197,6 @@ get_tablespace_page_costs(Oid spcid, =09=09else =09=09=09*spc_random_page_cost =3D spc->opts->random_page_cost; =09} =2D =09if (spc_seq_page_cost) =09{ =09=09if (!spc->opts || spc->opts->seq_page_cost < 0) diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h index 0db4fc7..b226dc7 100644 =2D-- a/src/include/access/amapi.h +++ b/src/include/access/amapi.h @@ -98,10 +98,6 @@ typedef void (*amcostestimate_function) (struct Plan= nerInfo *root, =09=09=09=09=09=09=09=09=09=09 double *indexCorrelation, =09=09=09=09=09=09=09=09=09=09 double *indexPages); =20 =2D/* parse index reloptions */ =2Dtypedef bytea *(*amoptions_function) (Datum reloptions, =2D=09=09=09=09=09=09=09=09=09 bool validate); =2D /* report AM, index, or index column property */ typedef bool (*amproperty_function) (Oid index_oid, int attno, =09=09=09=09=09=09=09=09=09 IndexAMProperty prop, const char *propname= , @@ -139,6 +135,9 @@ typedef void (*ammarkpos_function) (IndexScanDesc s= can); /* 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 =09amvacuumcleanup_function amvacuumcleanup; =09amcanreturn_function amcanreturn;=09/* can be NULL */ =09amcostestimate_function amcostestimate; =2D=09amoptions_function amoptions; =09amproperty_function amproperty; /* can be NULL */ =09amvalidate_function amvalidate; =09ambeginscan_function ambeginscan; @@ -212,6 +210,7 @@ typedef struct IndexAmRoutine =09amendscan_function amendscan; =09ammarkpos_function ammarkpos;=09/* can be NULL */ =09amrestrpos_function amrestrpos; /* can be NULL */ +=09amrelopt_catalog_function amrelopt_catalog; /* can be NULL */ =20 =09/* interface functions to support parallel index scans */ =09amestimateparallelscan_function amestimateparallelscan; /* can be N= ULL */ diff --git a/src/include/access/brin.h b/src/include/access/brin.h index 61a3880..d65ad4e 100644 =2D-- a/src/include/access/brin.h +++ b/src/include/access/brin.h @@ -37,6 +37,9 @@ typedef struct BrinStatsData =20 =20 #define BRIN_DEFAULT_PAGES_PER_RANGE=09128 +#define BRIN_MIN_PAGES_PER_RANGE=09=091 +#define BRIN_MAX_PAGES_PER_RANGE=09=09131072 + #define BrinGetPagesPerRange(relation) \ =09((relation)->rd_options ? \ =09 ((BrinOptions *) (relation)->rd_options)->pagesPerRange : \ diff --git a/src/include/access/brin_internal.h b/src/include/access/br= in_internal.h index 3ed6743..00eccac 100644 =2D-- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -102,7 +102,6 @@ extern IndexBulkDeleteResult *brinbulkdelete(IndexV= acuumInfo *info, =09=09=09 void *callback_state); extern IndexBulkDeleteResult *brinvacuumcleanup(IndexVacuumInfo *info,= =09=09=09=09 IndexBulkDeleteResult *stats); =2Dextern 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_= private.h index adfdb0c..451b372 100644 =2D-- 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 */ =2Dextern bytea *ginoptions(Datum reloptions, bool validate); +extern Datum ginhandler(PG_FUNCTION_ARGS); extern void initGinState(GinState *state, Relation index); extern Buffer GinNewBuffer(Relation index); extern void GinInitBuffer(Buffer b, uint32 f); @@ -103,6 +103,7 @@ extern Datum *ginExtractEntries(GinState *ginstate,= OffsetNumber attnum, extern OffsetNumber gintuple_get_attrnum(GinState *ginstate, IndexTupl= e tuple); extern Datum gintuple_get_key(GinState *ginstate, IndexTuple tuple, =09=09=09=09 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/gis= t_private.h index bfef2df..6a9090e 100644 =2D-- a/src/include/access/gist_private.h +++ b/src/include/access/gist_private.h @@ -367,6 +367,24 @@ typedef struct GISTBuildBuffers =09int=09=09=09rootlevel; } 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 { \ +=09"on",=09=09=09=09=09=09=09=09=09\ +=09"off",=09=09=09=09=09=09=09=09=09\ +=09"auto",=09=09=09=09=09=09=09=09=09\ +=09(const char *) NULL=09=09=09=09=09=09\ +} +typedef enum gist_option_buffering_value_numbers +{ +=09GIST_OPTION_BUFFERING_ON =3D 0, +=09GIST_OPTION_BUFFERING_OFF =3D 1, +=09GIST_OPTION_BUFFERING_AUTO =3D 2, +}=09gist_option_buffering_value_numbers; + /* * Storage type for GiST's reloptions */ @@ -374,7 +392,7 @@ typedef struct GiSTOptions { =09int32=09=09vl_len_;=09=09/* varlena header (do not touch directly!)= */ =09int=09=09=09fillfactor;=09=09/* page fill factor in percent (0..100= ) */ =2D=09int=09=09=09bufferingModeOffset;=09/* use buffering build? */ +=09int=09=09=09buffering_mode; /* use buffering build? */ } GiSTOptions; =20 /* gist.c */ @@ -435,7 +453,6 @@ extern bool gistvalidate(Oid opclassoid); #define GIST_MIN_FILLFACTOR=09=09=0910 #define GIST_DEFAULT_FILLFACTOR=09=0990 =20 =2Dextern bytea *gistoptions(Datum reloptions, bool validate); extern bool gistproperty(Oid index_oid, int attno, =09=09=09 IndexAMProperty prop, const char *propname, =09=09=09 bool *res, bool *isnull); @@ -485,6 +502,7 @@ extern void gistMakeUnionKey(GISTSTATE *giststate, = int attno, =09=09=09=09 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, I= ndexTuple *itup, /* gistbuild.c */ extern IndexBuildResult *gistbuild(Relation heap, Relation index, =09=09 struct IndexInfo *indexInfo); =2Dextern void gistValidateBufferingOption(char *value); =20 /* gistbuildbuffers.c */ extern GISTBuildBuffers *gistInitBuildBuffers(int pagesPerBuffer, int = levelStep, diff --git a/src/include/access/hash.h b/src/include/access/hash.h index 7fa868b..4c3cc38 100644 =2D-- a/src/include/access/hash.h +++ b/src/include/access/hash.h @@ -224,6 +224,19 @@ typedef struct HashMetaPageData =20 typedef HashMetaPageData *HashMetaPage; =20 + +typedef struct HashRelOptions +{ +=09int32=09=09varlena_header_;=09/* varlena header (do not touch direc= tly!) */ +=09int=09=09=09fillfactor;=09=09/* page fill factor in percent (0..100= ) */ +}=09HashRelOptions; + + +#define HashGetFillFactor(relation) \ +=09((relation)->rd_options ? \ +=09=09((HashRelOptions *) (relation)->rd_options)->fillfactor : \ +=09=09HASH_DEFAULT_FILLFACTOR) + /* * Maximum size of a hash index item (it's okay to have only one per p= age) */ @@ -319,7 +332,6 @@ extern IndexBulkDeleteResult *hashbulkdelete(IndexV= acuumInfo *info, =09=09=09 void *callback_state); extern IndexBulkDeleteResult *hashvacuumcleanup(IndexVacuumInfo *info,= =09=09=09=09 IndexBulkDeleteResult *stats); =2Dextern bytea *hashoptions(Datum reloptions, bool validate); extern bool hashvalidate(Oid opclassoid); =20 extern Datum hash_any(register const unsigned char *k, register int ke= ylen); @@ -422,4 +434,6 @@ extern void hashbucketcleanup(Relation rel, Bucket = cur_bucket, =09=09=09=09 bool bucket_has_garbage, =09=09=09=09 IndexBulkDeleteCallback callback, void *callback_state);= =20 +extern void *hashgetreloptcatalog(void); + #endif=09=09=09=09=09=09=09/* HASH_H */ diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h index e6abbec..9bbf426 100644 =2D-- a/src/include/access/nbtree.h +++ b/src/include/access/nbtree.h @@ -427,6 +427,17 @@ typedef BTScanOpaqueData *BTScanOpaque; #define SK_BT_DESC=09=09=09(INDOPTION_DESC << SK_BT_INDOPTION_SHIFT) #define SK_BT_NULLS_FIRST=09(INDOPTION_NULLS_FIRST << SK_BT_INDOPTION_= SHIFT) =20 +typedef struct BTRelOptions +{ +=09int32=09=09varlena_header_;=09/* varlena header (do not touch direc= tly!) */ +=09int=09=09=09fillfactor;=09=09/* page fill factor in percent (0..100= ) */ +}=09BTRelOptions; + +#define BTGetFillFactor(relation) \ +=09((relation)->rd_options ? \ +=09=09((BTRelOptions *) (relation)->rd_options)->fillfactor : \ +=09=09BTREE_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); =2Dextern bytea *btoptions(Datum reloptions, bool validate); extern bool btproperty(Oid index_oid, int attno, =09=09 IndexAMProperty prop, const char *propname, =09=09 bool *res, bool *isnull); diff --git a/src/include/access/options.h b/src/include/access/options.= h new file mode 100644 index 0000000..8c1c03c =2D-- /dev/null +++ b/src/include/access/options.h @@ -0,0 +1,225 @@ +/*--------------------------------------------------------------------= =2D---- + * + * options.h + *=09 Core support for relation and tablespace options (pg_class.relo= ptions + *=09 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 Gro= up + * Portions Copyright (c) 1994, Regents of the University of Californi= a + * + * src/include/access/options.h + * + *--------------------------------------------------------------------= =2D---- + */ +#ifndef OPTIONS_H +#define OPTIONS_H + +#include "storage/lock.h" +#include "nodes/pg_list.h" + +/* types supported by reloptions */ +typedef enum option_type +{ +=09OPTION_TYPE_BOOL, +=09OPTION_TYPE_INT, +=09OPTION_TYPE_REAL, +=09OPTION_TYPE_ENUM, +=09OPTION_TYPE_STRING +}=09option_type; + +typedef enum option_value_status +{ +=09OPTION_VALUE_STATUS_EMPTY,=09/* Option was just initialized */ +=09OPTION_VALUE_STATUS_RAW,=09/* Option just came from syntax analyzer= in +=09=09=09=09=09=09=09=09 * has name, and raw (unparsed) value */ +=09OPTION_VALUE_STATUS_PARSED, /* Option was parsed and has link to ca= talog +=09=09=09=09=09=09=09=09 * entry and proper value */ +=09OPTION_VALUE_STATUS_FOR_RESET=09=09/* This option came from ALTER x= xx +=09=09=09=09=09=09=09=09=09=09 * RESET */ +}=09option_value_status; + +/* flags for reloptinon definition */ +typedef enum option_definition_flags +{ +=09OPTION_DEFINITION_FLAG_FORBID_ALTER =3D (1 << 0),=09=09/* Altering = this option +=09=09=09=09=09=09=09=09=09=09=09=09=09=09 * is forbidden */ +=09OPTION_DEFINITION_FLAG_IGNORE =3D (1 << 1),=09/* Skip this option w= hile +=09=09=09=09=09=09=09=09=09=09=09=09 * parsing. Used for WITH OIDS +=09=09=09=09=09=09=09=09=09=09=09=09 * special case */ +=09OPTION_DEFINITION_FLAG_REJECT =3D (1 << 2)=09/* Option will be reje= cted +=09=09=09=09=09=09=09=09=09=09=09=09 * when comes from syntax +=09=09=09=09=09=09=09=09=09=09=09=09 * analyzer, but still have +=09=09=09=09=09=09=09=09=09=09=09=09 * default value and offset */ +} option_definition_flags; + +/* flags that tells reloption parser how to parse*/ +typedef enum options_parse_mode +{ +=09OPTIONS_PARSE_MODE_VALIDATE =3D (1 << 0), +=09OPTIONS_PARSE_MODE_FOR_ALTER =3D (1 << 1), +=09OPTIONS_PARSE_MODE_FOR_RESET =3D (1 << 2) +} options_parse_mode; + + +/* generic struct to hold shared data */ +typedef struct option_definition_basic +{ +=09const char *name;=09=09=09/* must be first (used as list terminatio= n +=09=09=09=09=09=09=09=09 * marker) */ +=09const char *desc; +=09LOCKMODE=09lockmode; +=09option_definition_flags flags; +=09option_type type; +=09int=09=09=09struct_offset;=09/* offset of the value in Bytea repres= entation */ +}=09option_definition_basic; + +/* holds a parsed value */ +typedef struct option_value +{ +=09option_definition_basic *gen; +=09char=09 *namespace; +=09option_value_status status; +=09char=09 *raw_value;=09=09/* allocated separately */ +=09char=09 *raw_name; +=09union +=09{ +=09=09bool=09=09bool_val; +=09=09int=09=09=09int_val; +=09=09double=09=09real_val; +=09=09int=09=09=09enum_val; +=09=09char=09 *string_val; /* allocated separately */ +=09}=09=09=09values; +}=09option_value; + +/* reloptions records for specific variable types */ +typedef struct option_definition_bool +{ +=09option_definition_basic base; +=09bool=09=09default_val; +}=09option_definition_bool; + +typedef struct option_definition_int +{ +=09option_definition_basic base; +=09int=09=09=09default_val; +=09int=09=09=09min; +=09int=09=09=09max; +}=09option_definition_int; + +typedef struct option_definition_real +{ +=09option_definition_basic base; +=09double=09=09default_val; +=09double=09=09min; +=09double=09=09max; +}=09option_definition_real; + +typedef struct option_definition_enum +{ +=09option_definition_basic base; +=09const char **allowed_values;/* Null terminated array of allowed val= ues for +=09=09=09=09=09=09=09=09 * the option */ +=09int=09=09=09default_val;=09/* Number of item of allowed_values arra= y */ +}=09option_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 def= ault + * 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 relpt= ions + * can deal this case. For better readability it was defined as a cons= tant. + */ +#define OPTION_STRING_VALUE_NOT_SET_OFFSET -1 + +typedef struct option_definition_string +{ +=09option_definition_basic base; +=09validate_string_relopt validate_cb; +=09char=09 *default_val; +}=09option_definition_string; + +typedef void (*postprocess_bytea_options_function) (void *data, bool v= alidate); + +typedef struct options_catalog +{ +=09option_definition_basic **definitions; +=09int=09=09=09num;=09=09=09/* Number of catalog items in use */ +=09int=09=09=09num_allocated;=09/* Number of catalog items allocated *= / +=09bool=09=09forbid_realloc;=09/* If number of items of the catalog we= re +=09=09=09=09=09=09=09=09=09* strictly set to certain value +=09=09=09=09=09=09=09=09=09* do no allow adding more idems */ +=09Size=09=09struct_size;=09/* Size of a structure for options in bina= ry +=09=09=09=09=09=09=09=09 * representation */ +=09postprocess_bytea_options_function postprocess_fun; /* This functio= n is +=09=09=09=09=09=09=09=09=09=09=09=09=09=09 * called after options +=09=09=09=09=09=09=09=09=09=09=09=09=09=09 * were converted in +=09=09=09=09=09=09=09=09=09=09=09=09=09=09 * Bytea represenation. +=09=09=09=09=09=09=09=09=09=09=09=09=09=09 * Can be used for extra +=09=09=09=09=09=09=09=09=09=09=09=09=09=09 * validation and so on */ +=09char=09 *namespace;=09=09/* Catalog is used for options from this= +=09=09=09=09=09=09=09=09 * namespase */ +}=09options_catalog; + + +/* + * Options catalog related functions + */ +extern options_catalog *allocateOptionsCatalog(char *namespace, int si= ze_of_bytea, +=09=09=09int num_items_expected); +extern void optionsCatalogAddItemBool(options_catalog * catalog, char = *name, +=09=09=09=09char *desc, LOCKMODE lockmode, option_definition_flags fla= gs, +=09=09=09=09=09=09 int struct_offset, bool default_val); +extern void optionsCatalogAddItemInt(options_catalog * catalog, char *= name, +=09=09=09=09char *desc, LOCKMODE lockmode, option_definition_flags fla= gs, +=09=09=09 int struct_offset, int default_val, int min_val, int max_v= al); +extern void optionsCatalogAddItemReal(options_catalog * catalog, char = *name, +=09=09=09=09char *desc, LOCKMODE lockmode, option_definition_flags fla= gs, +=09 int struct_offset, double default_val, double min_val, double max= _val); +extern void optionsCatalogAddItemEnum(options_catalog * catalog, +=09char *name, char *desc, LOCKMODE lockmode, option_definition_flags = flags, +=09=09=09int struct_offset, const char **allowed_values, int default_v= al); +extern void optionsCatalogAddItemString(options_catalog * catalog, cha= r *name, +=09=09=09=09char *desc, LOCKMODE lockmode, option_definition_flags fla= gs, +=09 int struct_offset, char *default_val, validate_string_relopt valid= ator); + + +/* + * This macro allows to get string option value from bytea representat= ion. + * "optstruct" - is a structure that is stored in bytea options repres= entation + * "member" - member of this structure that has string option value + * (actually string values are stored in bytea after the structure, an= d + * and "member" will contain an offset to this value. This macro do al= l + * the math + */ +#define GET_STRING_OPTION(optstruct, member) \ +=09((optstruct)->member =3D=3D OPTION_STRING_VALUE_NOT_SET_OFFSET ? NU= LL : \ +=09 (char *)(optstruct) + (optstruct)->member) + +/* + * Functions related to option convertation, parsing, manipulation + * and validation + */ +extern void optionsDefListValdateNamespaces(List *defList, +=09=09=09=09=09=09=09=09char **allowed_namespaces); +extern List *optionsDefListFilterNamespaces(List *defList, char *names= pace); +extern List *optionsTextArrayToDefList(Datum options); + +/* + * Meta functions that uses functions above to get options for relatio= ns, + * tablespaces, views and so on + */ + +extern bytea *optionsTextArrayToBytea(options_catalog * catalog, Datum= data); +extern Datum transformOptions(options_catalog * catalog, Datum oldOpti= ons, +=09=09=09=09 List *defList, options_parse_mode parse_mode); + +#endif /* OPTIONS_H */ diff --git a/src/include/access/reloptions.h b/src/include/access/relop= tions.h index 5cdaa3b..396263f 100644 =2D-- a/src/include/access/reloptions.h +++ b/src/include/access/reloptions.h @@ -1,13 +1,9 @@ /*--------------------------------------------------------------------= =2D---- * * reloptions.h =2D *=09 Core support for relation and tablespace options (pg_class.relo= ptions + *=09 Support for relation and tablespace options (pg_class.reloption= s *=09 and pg_tablespace.spcoptions) * =2D * Note: the functions dealing with text-array reloptions values decla= re =2D * them as Datum, not ArrayType *, to avoid needing to include array.h= =2D * into a lot of low-level code. =2D * * * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Gro= up * Portions Copyright (c) 1994, Regents of the University of Californi= a @@ -20,264 +16,27 @@ #define RELOPTIONS_H =20 #include "access/amapi.h" =2D#include "access/htup.h" =2D#include "access/tupdesc.h" =2D#include "nodes/pg_list.h" =2D#include "storage/lock.h" =2D =2D/* types supported by reloptions */ =2Dtypedef enum relopt_type =2D{ =2D=09RELOPT_TYPE_BOOL, =2D=09RELOPT_TYPE_INT, =2D=09RELOPT_TYPE_REAL, =2D=09RELOPT_TYPE_STRING =2D} relopt_type; +#include "access/options.h" =20 =2D/* kinds supported by reloptions */ =2Dtypedef enum relopt_kind =2D{ =2D=09RELOPT_KIND_HEAP =3D (1 << 0), =2D=09RELOPT_KIND_TOAST =3D (1 << 1), =2D=09RELOPT_KIND_BTREE =3D (1 << 2), =2D=09RELOPT_KIND_HASH =3D (1 << 3), =2D=09RELOPT_KIND_GIN =3D (1 << 4), =2D=09RELOPT_KIND_GIST =3D (1 << 5), =2D=09RELOPT_KIND_ATTRIBUTE =3D (1 << 6), =2D=09RELOPT_KIND_TABLESPACE =3D (1 << 7), =2D=09RELOPT_KIND_SPGIST =3D (1 << 8), =2D=09RELOPT_KIND_VIEW =3D (1 << 9), =2D=09RELOPT_KIND_BRIN =3D (1 << 10), =2D=09RELOPT_KIND_PARTITIONED =3D (1 << 11), =2D=09/* if you add a new kind, make sure you update "last_default" too *= / =2D=09RELOPT_KIND_LAST_DEFAULT =3D RELOPT_KIND_PARTITIONED, =2D=09/* some compilers treat enums as signed ints, so we can't use 1 << = 31 */ =2D=09RELOPT_KIND_MAX =3D (1 << 30) =2D} relopt_kind; =20 /* reloption namespaces allowed for heaps -- currently only TOAST */ #define HEAP_RELOPT_NAMESPACES { "toast", NULL } =20 =2D/* generic struct to hold shared data */ =2Dtypedef struct relopt_gen =2D{ =2D=09const char *name;=09=09=09/* must be first (used as list terminatio= n =2D=09=09=09=09=09=09=09=09 * marker) */ =2D=09const char *desc; =2D=09bits32=09=09kinds; =2D=09LOCKMODE=09lockmode; =2D=09int=09=09=09namelen; =2D=09relopt_type type; =2D} relopt_gen; =2D =2D/* holds a parsed value */ =2Dtypedef struct relopt_value =2D{ =2D=09relopt_gen *gen; =2D=09bool=09=09isset; =2D=09union =2D=09{ =2D=09=09bool=09=09bool_val; =2D=09=09int=09=09=09int_val; =2D=09=09double=09=09real_val; =2D=09=09char=09 *string_val; /* allocated separately */ =2D=09}=09=09=09values; =2D} relopt_value; =2D =2D/* reloptions records for specific variable types */ =2Dtypedef struct relopt_bool =2D{ =2D=09relopt_gen=09gen; =2D=09bool=09=09default_val; =2D} relopt_bool; =2D =2Dtypedef struct relopt_int =2D{ =2D=09relopt_gen=09gen; =2D=09int=09=09=09default_val; =2D=09int=09=09=09min; =2D=09int=09=09=09max; =2D} relopt_int; =2D =2Dtypedef struct relopt_real =2D{ =2D=09relopt_gen=09gen; =2D=09double=09=09default_val; =2D=09double=09=09min; =2D=09double=09=09max; =2D} relopt_real; =2D =2D/* validation routines for strings */ =2Dtypedef void (*validate_string_relopt) (char *value); =2D =2Dtypedef struct relopt_string =2D{ =2D=09relopt_gen=09gen; =2D=09int=09=09=09default_len; =2D=09bool=09=09default_isnull; =2D=09validate_string_relopt validate_cb; =2D=09char=09 *default_val; =2D} relopt_string; =2D =2D/* This is the table datatype for fillRelOptions */ =2Dtypedef struct =2D{ =2D=09const char *optname;=09=09/* option's name */ =2D=09relopt_type opttype;=09=09/* option's datatype */ =2D=09int=09=09=09offset;=09=09=09/* offset of field in result struct */ =2D} relopt_parse_elt; =2D =2D =2D/* =2D * These macros exist for the convenience of amoptions writers (but co= nsider =2D * using fillRelOptions, which is a lot simpler). Beware of multiple =2D * evaluation of arguments! =2D * =2D * The last argument in the HANDLE_*_RELOPTION macros allows the calle= r to =2D * determine whether the option was set (true), or its value acquired = from =2D * defaults (false); it can be passed as (char *) NULL if the caller d= oes not =2D * need this information. =2D * =2D * optname is the option name (a string), var is the variable =2D * on which the value should be stored (e.g. StdRdOptions->fillfactor)= , and =2D * option is a relopt_value pointer. =2D * =2D * The normal way to use this is to loop on the relopt_value array ret= urned by =2D * parseRelOptions: =2D * for (i =3D 0; options[i].gen->name; i++) =2D * { =2D *=09=09if (HAVE_RELOPTION("fillfactor", options[i]) =2D *=09=09{ =2D *=09=09=09HANDLE_INT_RELOPTION("fillfactor", rdopts->fillfactor, opti= ons[i], &isset); =2D *=09=09=09continue; =2D *=09=09} =2D *=09=09if (HAVE_RELOPTION("default_row_acl", options[i]) =2D *=09=09{ =2D *=09=09=09... =2D *=09=09} =2D *=09=09... =2D *=09=09if (validate) =2D *=09=09=09ereport(ERROR, =2D *=09=09=09=09=09(errmsg("unknown option"))); =2D *=09} =2D * =2D *=09Note that this is more or less the same that fillRelOptions does,= so only =2D *=09use this if you need to do something non-standard within some opt= ion's =2D *=09code block. =2D */ =2D#define HAVE_RELOPTION(optname, option) \ =2D=09(pg_strncasecmp(option.gen->name, optname, option.gen->namelen + 1)= =3D=3D 0) =2D =2D#define HANDLE_INT_RELOPTION(optname, var, option, wasset)=09=09\ =2D=09do {=09=09=09=09=09=09=09=09=09=09=09=09=09=09\ =2D=09=09if (option.isset)=09=09=09=09=09=09=09=09=09=09\ =2D=09=09=09var =3D option.values.int_val;=09=09=09=09=09=09\ =2D=09=09else=09=09=09=09=09=09=09=09=09=09=09=09=09\ =2D=09=09=09var =3D ((relopt_int *) option.gen)->default_val;=09=09\ =2D=09=09(wasset) !=3D NULL ? *(wasset) =3D option.isset : (dummyret)NULL= ; \ =2D=09} while (0) =2D =2D#define HANDLE_BOOL_RELOPTION(optname, var, option, wasset)=09=09=09\ =2D=09do {=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09\ =2D=09=09if (option.isset)=09=09=09=09=09=09=09=09=09=09\ =2D=09=09=09var =3D option.values.bool_val;=09=09=09=09=09=09\ =2D=09=09else=09=09=09=09=09=09=09=09=09=09=09=09=09\ =2D=09=09=09var =3D ((relopt_bool *) option.gen)->default_val;=09\ =2D=09=09(wasset) !=3D NULL ? *(wasset) =3D option.isset : (dummyret) NUL= L; \ =2D=09} while (0) =20 =2D#define HANDLE_REAL_RELOPTION(optname, var, option, wasset)=09=09\ =2D=09do {=09=09=09=09=09=09=09=09=09=09=09=09=09=09\ =2D=09=09if (option.isset)=09=09=09=09=09=09=09=09=09=09\ =2D=09=09=09var =3D option.values.real_val;=09=09=09=09=09=09\ =2D=09=09else=09=09=09=09=09=09=09=09=09=09=09=09=09\ =2D=09=09=09var =3D ((relopt_real *) option.gen)->default_val;=09\ =2D=09=09(wasset) !=3D NULL ? *(wasset) =3D option.isset : (dummyret) NUL= L; \ =2D=09} while (0) =2D =2D/* =2D * Note that this assumes that the variable is already allocated at th= e tail of =2D * reloptions structure (StdRdOptions or equivalent). =2D * =2D * "base" is a pointer to the reloptions structure, and "offset" is an= integer =2D * variable that must be initialized to sizeof(reloptions structure). = This =2D * struct must have been allocated with enough space to hold any strin= g option =2D * present, including terminating \0 for every option. SET_VARSIZE() = must be =2D * called on the struct with this offset as the second argument, after= all the =2D * string options have been processed. =2D */ =2D#define HANDLE_STRING_RELOPTION(optname, var, option, base, offset, wa= sset) \ =2D=09do {=09=09=09=09=09=09=09=09=09=09=09=09=09=09\ =2D=09=09relopt_string *optstring =3D (relopt_string *) option.gen;\ =2D=09=09char *string_val;=09=09=09=09=09=09=09=09=09=09\ =2D=09=09if (option.isset)=09=09=09=09=09=09=09=09=09=09\ =2D=09=09=09string_val =3D option.values.string_val;=09=09=09=09\ =2D=09=09else if (!optstring->default_isnull)=09=09=09=09=09\ =2D=09=09=09string_val =3D optstring->default_val;=09=09=09=09\ =2D=09=09else=09=09=09=09=09=09=09=09=09=09=09=09=09\ =2D=09=09=09string_val =3D NULL;=09=09=09=09=09=09=09=09=09\ =2D=09=09(wasset) !=3D NULL ? *(wasset) =3D option.isset : (dummyret) NUL= L; \ =2D=09=09if (string_val =3D=3D NULL)=09=09=09=09=09=09=09=09=09\ =2D=09=09=09var =3D 0;=09=09=09=09=09=09=09=09=09=09=09\ =2D=09=09else=09=09=09=09=09=09=09=09=09=09=09=09=09\ =2D=09=09{=09=09=09=09=09=09=09=09=09=09=09=09=09=09\ =2D=09=09=09strcpy(((char *)(base)) + (offset), string_val);=09\ =2D=09=09=09var =3D (offset);=09=09=09=09=09=09=09=09=09=09\ =2D=09=09=09(offset) +=3D strlen(string_val) + 1;=09=09=09=09=09\ =2D=09=09}=09=09=09=09=09=09=09=09=09=09=09=09=09=09\ =2D=09} while (0) =2D =2D/* =2D * For use during amoptions: get the strlen of a string option =2D * (either default or the user defined value) =2D */ =2D#define GET_STRING_RELOPTION_LEN(option) \ =2D=09((option).isset ? strlen((option).values.string_val) : \ =2D=09 ((relopt_string *) (option).gen)->default_len) +extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, +=09=09=09=09 amrelopt_catalog_function catalog_fun); =20 /* =2D * For use by code reading options already parsed: get a pointer to th= e string =2D * value itself. "optstruct" is the StdRdOption struct or equivalent,= "member" =2D * is the struct member corresponding to the string option + * Functions that creates option catalog instances for heap, toast, vi= ew + * and other object types that are part of the postgres core */ =2D#define GET_STRING_RELOPTION(optstruct, member) \ =2D=09((optstruct)->member =3D=3D 0 ? NULL : \ =2D=09 (char *)(optstruct) + (optstruct)->member) =20 +extern options_catalog *get_heap_relopt_catalog(void); +extern options_catalog *get_toast_relopt_catalog(void); =20 =2Dextern relopt_kind add_reloption_kind(void); =2Dextern void add_bool_reloption(bits32 kinds, char *name, char *desc, =2D=09=09=09=09 bool default_val); =2Dextern void add_int_reloption(bits32 kinds, char *name, char *desc, =2D=09=09=09=09 int default_val, int min_val, int max_val); =2Dextern void add_real_reloption(bits32 kinds, char *name, char *desc, =2D=09=09=09=09 double default_val, double min_val, double max_val); =2Dextern void add_string_reloption(bits32 kinds, char *name, char *desc,= =2D=09=09=09=09=09 char *default_val, validate_string_relopt validator); =2D =2Dextern Datum transformRelOptions(Datum oldOptions, List *defList, =2D=09=09=09=09=09char *namspace, char *validnsps[], =2D=09=09=09=09=09bool ignoreOids, bool isReset); =2Dextern List *untransformRelOptions(Datum options); =2Dextern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, =2D=09=09=09=09 amoptions_function amoptions); =2Dextern relopt_value *parseRelOptions(Datum options, bool validate, =2D=09=09=09=09relopt_kind kind, int *numrelopts); =2Dextern void *allocateReloptStruct(Size base, relopt_value *options, =2D=09=09=09=09=09 int numoptions); =2Dextern void fillRelOptions(void *rdopts, Size basesize, =2D=09=09=09 relopt_value *options, int numoptions, =2D=09=09=09 bool validate, =2D=09=09=09 const relopt_parse_elt *elems, int nelems); =2D =2Dextern bytea *default_reloptions(Datum reloptions, bool validate, =2D=09=09=09=09 relopt_kind kind); =2Dextern bytea *heap_reloptions(char relkind, Datum reloptions, bool val= idate); =2Dextern bytea *view_reloptions(Datum reloptions, bool validate); =2Dextern bytea *index_reloptions(amoptions_function amoptions, Datum rel= options, =2D=09=09=09=09 bool validate); =2Dextern bytea *attribute_reloptions(Datum reloptions, bool validate); =2Dextern bytea *tablespace_reloptions(Datum reloptions, bool validate); =2Dextern 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 *d= efList); =20 #endif=09=09=09=09=09=09=09/* RELOPTIONS_H */ diff --git a/src/include/access/spgist.h b/src/include/access/spgist.h index d1bc396..780e0d7 100644 =2D-- a/src/include/access/spgist.h +++ b/src/include/access/spgist.h @@ -20,10 +20,6 @@ #include "lib/stringinfo.h" =20 =20 =2D/* reloption parameters */ =2D#define SPGIST_MIN_FILLFACTOR=09=09=0910 =2D#define SPGIST_DEFAULT_FILLFACTOR=09=0980 =2D /* SPGiST opclass support function numbers */ #define SPGIST_CONFIG_PROC=09=09=09=091 #define SPGIST_CHOOSE_PROC=09=09=09=092 @@ -181,9 +177,6 @@ typedef struct spgLeafConsistentOut } spgLeafConsistentOut; =20 =20 =2D/* spgutils.c */ =2Dextern bytea *spgoptions(Datum reloptions, bool validate); =2D /* spginsert.c */ extern IndexBuildResult *spgbuild(Relation heap, Relation index, =09=09 struct IndexInfo *indexInfo); diff --git a/src/include/access/spgist_private.h b/src/include/access/s= pgist_private.h index 1c4b321..f8c562a 100644 =2D-- 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 +{ +=09int32=09=09varlena_header_;=09/* varlena header (do not touch direc= tly!) */ +=09int=09=09=09fillfactor;=09=09/* page fill factor in percent (0..100= ) */ +}=09SpGistRelOptions; + + +#define SpGistGetFillFactor(relation) \ +=09((relation)->rd_options ? \ +=09=09((SpGistRelOptions *) (relation)->rd_options)->fillfactor : \ +=09=09SPGIST_DEFAULT_FILLFACTOR) + + /* Page numbers of fixed-location pages */ #define SPGIST_METAPAGE_BLKNO=09 (0)=09/* metapage */ #define SPGIST_ROOT_BLKNO=09=09 (1)=09/* root for normal entries */ @@ -380,6 +393,11 @@ typedef SpGistDeadTupleData *SpGistDeadTuple; #define GBUF_REQ_NULLS(flags)=09((flags) & GBUF_NULLS) =20 /* spgutils.c */ + +/* reloption parameters */ +#define SPGIST_MIN_FILLFACTOR=09=09=0910 +#define SPGIST_DEFAULT_FILLFACTOR=09=0980 + extern SpGistCache *spgGetCache(Relation index); extern void initSpGistState(SpGistState *state, Relation index); extern Buffer SpGistNewBuffer(Relation index); @@ -407,6 +425,7 @@ extern OffsetNumber SpGistPageAddNewItem(SpGistStat= e *state, Page page, =09=09=09=09=09 Item item, Size size, =09=09=09=09=09 OffsetNumber *startOffset, =09=09=09=09=09 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/toast= ing.h index f82a799..c649173 100644 =2D-- a/src/include/catalog/toasting.h +++ b/src/include/catalog/toasting.h @@ -19,7 +19,7 @@ /* * toasting.c prototypes */ =2Dextern void NewRelationCreateToastTable(Oid relOid, Datum reloptions);= +extern bool NewRelationCreateToastTable(Oid relOid, Datum reloptions);= extern void NewHeapCreateToastTable(Oid relOid, Datum reloptions, =09=09=09=09=09=09LOCKMODE lockmode); extern void AlterTableCreateToastTable(Oid relOid, Datum reloptions, diff --git a/src/include/commands/tablecmds.h b/src/include/commands/ta= blecmds.h index abd31b6..21e85ed 100644 =2D-- a/src/include/commands/tablecmds.h +++ b/src/include/commands/tablecmds.h @@ -31,7 +31,7 @@ extern Oid=09AlterTableLookupRelation(AlterTableStmt = *stmt, LOCKMODE lockmode); =20 extern void AlterTable(Oid relid, LOCKMODE lockmode, AlterTableStmt *s= tmt); =20 =2Dextern LOCKMODE AlterTableGetLockLevel(List *cmds); +extern LOCKMODE AlterTableGetLockLevel(Oid relid, List *cmds); =20 extern void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool re= cursing, LOCKMODE lockmode); =20 diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h index 4bc61e5..e276d8f 100644 =2D-- a/src/include/utils/rel.h +++ b/src/include/utils/rel.h @@ -273,114 +273,123 @@ typedef struct AutoVacOpts =09float8=09=09analyze_scale_factor; } AutoVacOpts; =20 =2Dtypedef struct StdRdOptions +typedef struct HeapOptions { =09int32=09=09vl_len_;=09=09/* varlena header (do not touch directly!)= */ =09int=09=09=09fillfactor;=09=09/* page fill factor in percent (0..100= ) */ =09AutoVacOpts autovacuum;=09=09/* autovacuum-related options */ =09bool=09=09user_catalog_table; /* use as an additional catalog relat= ion */ =09int=09=09=09parallel_workers;=09/* max number of parallel workers *= / =2D} StdRdOptions; +} HeapOptions; + +typedef struct ToastOptions +{ +=09int32=09=09vl_len_;=09=09/* varlena header (do not touch directly!)= */ +=09AutoVacOpts autovacuum;=09=09/* autovacuum-related options */ +}=09ToastOptions; =20 #define HEAP_MIN_FILLFACTOR=09=09=0910 #define HEAP_DEFAULT_FILLFACTOR=09=09100 =20 +#define TOAST_DEFAULT_FILLFACTOR=09100 /* Only default is actually use= d */ + /* =2D * RelationGetFillFactor + * HeapGetFillFactor *=09=09Returns the relation's fillfactor. Note multiple eval of argu= ment! */ =2D#define RelationGetFillFactor(relation, defaultff) \ +#define HeapGetFillFactor(relation, defaultff) \ =09((relation)->rd_options ? \ =2D=09 ((StdRdOptions *) (relation)->rd_options)->fillfactor : (defaultff= )) +=09 ((HeapOptions *) (relation)->rd_options)->fillfactor : (defaultff)= ) =20 /* =2D * RelationGetTargetPageUsage =2D *=09=09Returns the relation's desired space usage per page in bytes. =2D */ =2D#define RelationGetTargetPageUsage(relation, defaultff) \ =2D=09(BLCKSZ * RelationGetFillFactor(relation, defaultff) / 100) =2D =2D/* =2D * RelationGetTargetPageFreeSpace + * HeapGetTargetPageFreeSpace *=09=09Returns the relation's desired freespace per page in bytes. */ =2D#define RelationGetTargetPageFreeSpace(relation, defaultff) \ =2D=09(BLCKSZ * (100 - RelationGetFillFactor(relation, defaultff)) / 100)= +#define HeapGetTargetPageFreeSpace(relation, defaultff) \ +=09(BLCKSZ * (100 - HeapGetFillFactor(relation, defaultff)) / 100) =20 /* =2D * RelationIsUsedAsCatalogTable + * HeapIsUsedAsCatalogTable *=09=09Returns whether the relation should be treated as a catalog ta= ble *=09=09from the pov of logical decoding. Note multiple eval of argum= ent! */ =2D#define RelationIsUsedAsCatalogTable(relation)=09\ +#define HeapIsUsedAsCatalogTable(relation)=09\ =09((relation)->rd_options && \ =09 ((relation)->rd_rel->relkind =3D=3D RELKIND_RELATION || \ =09 (relation)->rd_rel->relkind =3D=3D RELKIND_MATVIEW) ? \ =2D=09 ((StdRdOptions *) (relation)->rd_options)->user_catalog_table : fa= lse) +=09 ((HeapOptions *) (relation)->rd_options)->user_catalog_table : fal= se) =20 /* =2D * RelationGetParallelWorkers + * HeapGetParallelWorkers *=09=09Returns the relation's parallel_workers reloption setting. *=09=09Note multiple eval of argument! */ =2D#define RelationGetParallelWorkers(relation, defaultpw) \ +#define HeapGetParallelWorkers(relation, defaultpw) \ =09((relation)->rd_options ? \ =2D=09 ((StdRdOptions *) (relation)->rd_options)->parallel_workers : (def= aultpw)) +=09 ((HeapOptions *) (relation)->rd_options)->parallel_workers : (defa= ultpw)) =20 +#define ToastGetTargetPageFreeSpace() \ +=09(BLCKSZ * (100 - TOAST_DEFAULT_FILLFACTOR) / 100) =20 /* * ViewOptions *=09=09Contents 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 {=09\ +=09"local",=09=09=09=09=09=09=09=09=09\ +=09"cascaded",=09=09=09=09=09=09=09=09=09\ +=09(const char *) NULL=09=09=09=09=09=09=09\ +} + +typedef enum view_option_check_option_value_numbers +{ +=09VIEW_OPTION_CHECK_OPTION_NOT_SET =3D -1, +=09VIEW_OPTION_CHECK_OPTION_LOCAL =3D 0, +=09VIEW_OPTION_CHECK_OPTION_CASCADED =3D 1, +}=09view_option_check_option_value_numbers; + typedef struct ViewOptions { =09int32=09=09vl_len_;=09=09/* varlena header (do not touch directly!)= */ =09bool=09=09security_barrier; =2D=09int=09=09=09check_option_offset; +=09int=09=09=09check_option; } ViewOptions; =20 /* =2D * RelationIsSecurityView =2D *=09=09Returns whether the relation is security view, or not. Note m= ultiple =2D *=09=09eval of argument! + * ViewIsSecurityView + *=09=09Returns whether the view is security view, or not. Note multip= le eval + *=09=09of argument! */ =2D#define RelationIsSecurityView(relation)=09\ +#define ViewIsSecurityView(relation)=09=09\ =09((relation)->rd_options ?=09=09=09=09\ =2D=09 ((ViewOptions *) (relation)->rd_options)->security_barrier : false= ) +=09=09((ViewOptions *) (relation)->rd_options)->security_barrier : fal= se) =20 /* =2D * RelationHasCheckOption =2D *=09=09Returns true if the relation is a view defined with either the= local =2D *=09=09or the cascaded check option. Note multiple eval of argument!= + * ViewHasCheckOption + *=09=09Returns true if view is defined with either the local or the c= ascaded + *=09=09check option. Note multiple eval of argument! */ =2D#define RelationHasCheckOption(relation)=09=09=09=09=09=09=09=09=09\ =2D=09((relation)->rd_options &&=09=09=09=09=09=09=09=09=09=09=09=09\ =2D=09 ((ViewOptions *) (relation)->rd_options)->check_option_offset !=3D= 0) =20 =2D/* =2D * RelationHasLocalCheckOption =2D *=09=09Returns true if the relation is a view defined with the local = check =2D *=09=09option. Note multiple eval of argument! =2D */ =2D#define RelationHasLocalCheckOption(relation)=09=09=09=09=09=09=09=09\= =2D=09((relation)->rd_options &&=09=09=09=09=09=09=09=09=09=09=09=09\ =2D=09 ((ViewOptions *) (relation)->rd_options)->check_option_offset !=3D= 0 ?=09\ =2D=09 strcmp((char *) (relation)->rd_options +=09=09=09=09=09=09=09=09\ =2D=09=09=09((ViewOptions *) (relation)->rd_options)->check_option_offset= ,=09\ =2D=09=09=09"local") =3D=3D 0 : false) +#define ViewHasCheckOption(relation)=09=09=09=09=09=09=09=09=09=09\ +((relation)->rd_options &&=09=09=09=09=09=09=09=09=09=09=09=09=09\ +=09((ViewOptions *) (relation)->rd_options)->check_option !=3D=09=09=09= =09\ +=09VIEW_OPTION_CHECK_OPTION_NOT_SET) =20 /* =2D * RelationHasCascadedCheckOption =2D *=09=09Returns true if the relation is a view defined with the cascad= ed check =2D *=09=09option. Note multiple eval of argument! + * ViewHasCascadedCheckOption + *=09=09Returns true if the view is defined with the cascaded check op= tion. + *=09=09Note multiple eval of argument! */ =2D#define RelationHasCascadedCheckOption(relation)=09=09=09=09=09=09=09\= =2D=09((relation)->rd_options &&=09=09=09=09=09=09=09=09=09=09=09=09\ =2D=09 ((ViewOptions *) (relation)->rd_options)->check_option_offset !=3D= 0 ?=09\ =2D=09 strcmp((char *) (relation)->rd_options +=09=09=09=09=09=09=09=09\ =2D=09=09=09((ViewOptions *) (relation)->rd_options)->check_option_offset= ,=09\ =2D=09=09=09"cascaded") =3D=3D 0 : false) =20 +#define ViewHasCascadedCheckOption(relation)=09=09=09=09=09=09=09=09\ +((relation)->rd_options &&=09=09=09=09=09=09=09=09=09=09=09=09=09\ +=09((ViewOptions *) (relation)->rd_options)->check_option =3D=3D=09=09= =09=09\ +=09VIEW_OPTION_CHECK_OPTION_CASCADED) =20 /* * RelationIsValid @@ -559,7 +568,7 @@ typedef struct ViewOptions #define RelationIsAccessibleInLogicalDecoding(relation) \ =09(XLogLogicalInfoActive() && \ =09 RelationNeedsWAL(relation) && \ =2D=09 (IsCatalogRelation(relation) || RelationIsUsedAsCatalogTable(relat= ion))) +=09 (IsCatalogRelation(relation) || HeapIsUsedAsCatalogTable(relation)= )) =20 /* * RelationIsLogicallyLogged diff --git a/src/test/regress/expected/alter_table.out b/src/test/regre= ss/expected/alter_table.out index 13d6a4b..c0bd149 100644 =2D-- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -2354,7 +2354,9 @@ ERROR: unrecognized parameter "autovacuum_enable= d" 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; @@ -3506,3 +3508,10 @@ create table parted_validate_test_1 partition of= parted_validate_test for values alter table parted_validate_test add constraint parted_validate_test_c= hka check (a > 0) not valid; alter table parted_validate_test validate constraint parted_validate_t= est_chka; drop table parted_validate_test; +-- test alter column options +CREATE TABLE tmp(i integer); +INSERT INTO tmp VALUES (1); +ALTER TABLE tmp ALTER COLUMN i SET (n_distinct =3D 1, n_distinct_inher= ited =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/expe= cted/brin.out index ca80f00..6e9c4a2 100644 =2D-- a/src/test/regress/expected/brin.out +++ b/src/test/regress/expected/brin.out @@ -507,3 +507,8 @@ EXPLAIN (COSTS OFF) SELECT * FROM brin_test WHERE b= =3D 1; Filter: (b =3D 1) (2 rows) =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/regr= ess/expected/create_index.out index 26cd059..7b8f805 100644 =2D-- 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=3D= 128 CREATE INDEX hash_i4_index ON hash_i4_heap USING hash (random int4_ops= ); CREATE INDEX hash_name_index ON hash_name_heap USING hash (random name= _ops); CREATE INDEX hash_txt_index ON hash_txt_heap USING hash (random text_o= ps); =2DCREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_o= ps); +CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_o= ps) WITH (fillfactor=3D60); CREATE UNLOGGED TABLE unlogged_hash_table (id int4); CREATE INDEX unlogged_hash_index ON unlogged_hash_table USING hash (id= int4_ops); DROP TABLE unlogged_hash_table; diff --git a/src/test/regress/expected/gist.out b/src/test/regress/expe= cted/gist.out index 91f9998..df52e46 100644 =2D-- 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 (buff= ering =3D on, fillfactor=3D50); +create index gist_pointidx3 on gist_point_tbl using gist(p) with (buff= ering =3D off); +create index gist_pointidx4 on gist_point_tbl using gist(p) with (buff= ering =3D auto); +--Test buffering and fillfactor for refising invalid values +create index gist_pointidx5 on gist_point_tbl using gist(p) with (buff= ering =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 (fill= factor=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 (fill= factor=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 current= ly -- 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/regres= s/expected/hash_index.out index 0bbaa2a..6d5cf0d 100644 =2D-- a/src/test/regress/expected/hash_index.out +++ b/src/test/regress/expected/hash_index.out @@ -229,3 +229,12 @@ CREATE TABLE hash_heap_float4 (x float4, y int); INSERT INTO hash_heap_float4 VALUES (1.1,1); CREATE INDEX hash_idx ON hash_heap_float4 USING hash (x); DROP TABLE hash_heap_float4 CASCADE; +-- Check reloptions min max values and that it is not allowed to ALTER= +CREATE INDEX hash_f8_index2 ON hash_f8_heap USING hash (random float8_= ops) WITH (fillfactor=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/regres= s/expected/reloptions.out new file mode 100644 index 0000000..835d72f =2D-- /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_e= nabled =3D false, autovacuum_analyze_scale_factor =3D 0.2); +SELECT reloptions FROM pg_class WHERE oid =3D 'reloptions_test'::regcl= ass; + reloptions = =20 +----------------------------------------------------------------------= =2D------- + {fillfactor=3D30,autovacuum_enabled=3Dfalse,autovacuum_analyze_scale_= factor=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'::regc= lass; + reloptions =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_fa= ctor =3D -10.0); +ERROR: value -10.0 out of bounds for option "autovacuum_analyze_scale= _factor" +DETAIL: Valid values are between "0.000000" and "100.000000". +CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_fa= ctor =3D 110.0); +ERROR: value 110.0 out of bounds for option "autovacuum_analyze_scale= _factor" +DETAIL: Valid values are between "0.000000" and "100.000000". +-- Fail when option and namespase do not exist +CREATE TABLE reloptions_test2(i INT) WITH (not_existing_option=3D2); +ERROR: unrecognized parameter "not_existing_option" +CREATE TABLE reloptions_test2(i INT) WITH (not_existing_namespace.fill= factor=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'strin= g'); +ERROR: invalid value for boolean option "autovacuum_enabled": string +CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_fa= ctor=3D'string'); +ERROR: invalid value for floating point option "autovacuum_analyze_sc= ale_factor": string +CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_fa= ctor=3Dtrue); +ERROR: invalid value for floating point option "autovacuum_analyze_sc= ale_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_s= cale_factor =3D 0.3); +SELECT reloptions FROM pg_class WHERE oid =3D 'reloptions_test'::regcl= ass; + reloptions = =20 +----------------------------------------------------------------------= =2D------- + {autovacuum_enabled=3Dfalse,fillfactor=3D31,autovacuum_analyze_scale_= factor=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'::regcl= ass; + reloptions = =20 +----------------------------------------------------------------------= =2D------ + {autovacuum_analyze_scale_factor=3D0.3,autovacuum_enabled=3Dtrue,fill= factor=3D32} +(1 row) + +-- Check that RESET works well +ALTER TABLE reloptions_test RESET (fillfactor); +SELECT reloptions FROM pg_class WHERE oid =3D 'reloptions_test'::regcl= ass; + reloptions =20 +--------------------------------------------------------------- + {autovacuum_analyze_scale_factor=3D0.3,autovacuum_enabled=3Dtrue} +(1 row) + +-- Check that RESETting all values make NULL reloptions record in pg_c= lass +ALTER TABLE reloptions_test RESET (autovacuum_enabled, autovacuum_anal= yze_scale_factor); +SELECT reloptions FROM pg_class WHERE oid =3D 'reloptions_test'::regcl= ass 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'::regcl= ass; + reloptions =20 +----------------- + {fillfactor=3D20} +(1 row) + +-- Now testing toast.* options +DROP TABLE reloptions_test; +CREATE TABLE reloptions_test (s VARCHAR) WITH (toast.autovacuum_vacuum= _cost_delay =3D 23 ); +SELECT reloptions FROM pg_class WHERE oid =3D (SELECT reltoastrelid FR= OM pg_class WHERE oid =3D 'reloptions_test'::regclass); + reloptions =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 FR= OM pg_class WHERE oid =3D 'reloptions_test'::regclass); + reloptions =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 FR= OM pg_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 rel= ation +CREATE TABLE reloptions_test2 (i int) WITH (toast.autovacuum_vacuum_co= st_delay =3D 23 ); +ERROR: no TOAST relation was created for a table. Can't set toast.* s= torage 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 toa= st.* storage parameters +ALTER TABLE reloptions_test RESET (toast.autovacuum_vacuum_cost_delay)= ; +ERROR: TOAST relation for this table does not exist. Can't change toa= st.* storage parameters +-- autovacuum_analyze_scale_factor and autovacuum_analyze_threshold sh= ould be +-- accepted by heap but rejected by toast (special case) +DROP TABLE reloptions_test; +CREATE TABLE reloptions_test (s VARCHAR) WITH (autovacuum_analyze_scal= e_factor=3D1, autovacuum_analyze_threshold=3D1); +CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analy= ze_scale_factor=3D1); +ERROR: unrecognized parameter "toast.autovacuum_analyze_scale_factor"= +CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analy= ze_threshold=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= _cost_delay =3D 23, autovacuum_vacuum_cost_delay =3D 24, fillfactor =3D= 40); +SELECT reloptions FROM pg_class WHERE oid =3D 'reloptions_test'::regcl= ass; + reloptions =20 +------------------------------------------------- + {autovacuum_vacuum_cost_delay=3D24,fillfactor=3D40} +(1 row) + +SELECT reloptions FROM pg_class WHERE oid =3D (SELECT reltoastrelid FR= OM pg_class WHERE oid =3D 'reloptions_test'::regclass); + reloptions =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 (fillfact= or=3D30); +SELECT reloptions FROM pg_class WHERE oid =3D 'reloptions_test_idx'::r= egclass; + reloptions =20 +----------------- + {fillfactor=3D30} +(1 row) + +-- Fail when option and namespase do not exist +CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_exis= ting_option=3D2); +ERROR: unrecognized parameter "not_existing_option" +CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_exis= ting_option.fillfactor=3D2); +ERROR: unrecognized parameter namespace "not_existing_option" +-- Check ranges +CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfac= tor=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 (fillfac= tor=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'::r= egclass; + reloptions =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'::= regclass; + reloptions =20 +----------------- + {fillfactor=3D40} +(1 row) + diff --git a/src/test/regress/expected/spgist.out b/src/test/regress/ex= pected/spgist.out index 0691e91..bb58fb7 100644 =2D-- 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); =2Dcreate index spgist_point_idx on spgist_point_tbl using spgist(p); +create index spgist_point_idx on spgist_point_tbl using spgist(p) with= (fillfactor =3D 75); -- Test vacuum-root operation. It gets invoked when the root is also a= leaf, -- i.e. the index is very small. insert into spgist_point_tbl (id, p) @@ -37,3 +37,12 @@ select g, 'baaaaaaaaaaaaaar' || g from generate_seri= es(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_serie= s(1, 100) g; +-- Check reloptions min, max values +create index spgist_point_idx2 on spgist_point_tbl using spgist(p) wit= h (fillfactor =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) wit= h (fillfactor =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/r= egress/expected/updatable_views.out index 971dddd..85d1daf 100644 =2D-- 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 =2DDETAIL: 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/regres= s/input/tablespace.source index 03a62bd..7f7934b 100644 =2D-- 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 =2DALTER TABLESPACE regress_tblspace SET (random_page_cost =3D 1.0); +ALTER TABLESPACE regress_tblspace SET (random_page_cost =3D 1.0, seq_p= age_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 =2DALTER TABLESPACE regress_tblspace RESET (random_page_cost, seq_page_co= st); -- ok +ALTER TABLESPACE regress_tblspace RESET (random_page_cost, effective_i= o_concurrency); -- ok =20 -- create a schema we can use CREATE SCHEMA testschema; diff --git a/src/test/regress/output/tablespace.source b/src/test/regre= ss/output/tablespace.source index aaedf5f..2443511 100644 =2D-- 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 =2DALTER TABLESPACE regress_tblspace SET (random_page_cost =3D 1.0); +ALTER TABLESPACE regress_tblspace SET (random_page_cost =3D 1.0, seq_p= age_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 =2DALTER TABLESPACE regress_tblspace RESET (random_page_cost, seq_page_co= st); -- ok +ALTER TABLESPACE regress_tblspace RESET (random_page_cost, effective_i= o_concurrency); -- ok -- create a schema we can use CREATE SCHEMA testschema; -- try a table diff --git a/src/test/regress/parallel_schedule b/src/test/regress/para= llel_schedule index 2369261..970025b 100644 =2D-- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -109,7 +109,7 @@ test: select_views portals_p2 foreign_key cluster d= ependency guc bitmapops combo # NB: temp.sql does a reconnect which transiently uses 2 connections, # so keep this parallel group to at most 19 tests # ---------- =2Dtest: plancache limit plpgsql copy2 temp domain rangefuncs prepare wit= hout_oid conversion truncate alter_table sequence polymorphism rowtypes= returning largeobject with xml +test: plancache limit plpgsql copy2 temp domain rangefuncs prepare wit= hout_oid conversion truncate alter_table sequence polymorphism rowtypes= returning largeobject with xml reloptions =20 # ---------- # Another group of parallel tests diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial= _schedule index 5e8b7e9..55d5deb 100644 =2D-- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -178,5 +178,6 @@ test: returning test: largeobject test: with test: xml +test: reloptions test: event_trigger test: stats diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sq= l/alter_table.sql index 5dd1402..4e84fb8 100644 =2D-- a/src/test/regress/sql/alter_table.sql +++ b/src/test/regress/sql/alter_table.sql @@ -2307,3 +2307,10 @@ create table parted_validate_test_1 partition of= parted_validate_test for values alter table parted_validate_test add constraint parted_validate_test_c= hka check (a > 0) not valid; alter table parted_validate_test validate constraint parted_validate_t= est_chka; drop table parted_validate_test; +-- test alter column options +CREATE TABLE tmp(i integer); +INSERT INTO tmp VALUES (1); +ALTER TABLE tmp ALTER COLUMN i SET (n_distinct =3D 1, n_distinct_inher= ited =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 11f8fe9..3559ef6 100644 =2D-- a/src/test/regress/sql/brin.sql +++ b/src/test/regress/sql/brin.sql @@ -460,3 +460,6 @@ VACUUM ANALYZE brin_test; EXPLAIN (COSTS OFF) SELECT * FROM brin_test WHERE a =3D 1; -- Ensure brin index is not used when values are not correlated EXPLAIN (COSTS OFF) SELECT * FROM brin_test WHERE b =3D 1; +-- 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/s= ql/create_index.sql index 1648072..1f8d101 100644 =2D-- 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 USIN= G hash (random name_ops); =20 CREATE INDEX hash_txt_index ON hash_txt_heap USING hash (random text_o= ps); =20 =2DCREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_o= ps); +CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_o= ps) WITH (fillfactor=3D60); =20 CREATE UNLOGGED TABLE unlogged_hash_table (id int4); CREATE INDEX unlogged_hash_index ON unlogged_hash_table USING hash (id= int4_ops); diff --git a/src/test/regress/sql/gist.sql b/src/test/regress/sql/gist.= sql index 49126fd..f65ce37 100644 =2D-- a/src/test/regress/sql/gist.sql +++ b/src/test/regress/sql/gist.sql @@ -17,6 +17,17 @@ select g+100000, point(g*10+1, g*10+1) from generate= _series(1, 10000) g; -- To test vacuum, delete some entries from all over the index. delete from gist_point_tbl where id % 2 =3D 1; =20 +-- Test buffering and fillfactor reloption takes valid values +create index gist_pointidx2 on gist_point_tbl using gist(p) with (buff= ering =3D on, fillfactor=3D50); +create index gist_pointidx3 on gist_point_tbl using gist(p) with (buff= ering =3D off); +create index gist_pointidx4 on gist_point_tbl using gist(p) with (buff= ering =3D auto); +--Test buffering and fillfactor for refising invalid values +create index gist_pointidx5 on gist_point_tbl using gist(p) with (buff= ering =3D invalid_value); +create index gist_pointidx5 on gist_point_tbl using gist(p) with (fill= factor=3D9); +create index gist_pointidx5 on gist_point_tbl using gist(p) with (fill= factor=3D101); + + + -- And also delete some concentration of values. (GiST doesn't current= ly -- attempt to delete pages even when they become empty, but if it did,= this -- would exercise it) diff --git a/src/test/regress/sql/hash_index.sql b/src/test/regress/sql= /hash_index.sql index 9af03d2..f245525 100644 =2D-- a/src/test/regress/sql/hash_index.sql +++ b/src/test/regress/sql/hash_index.sql @@ -192,3 +192,8 @@ CREATE TABLE hash_heap_float4 (x float4, y int); INSERT INTO hash_heap_float4 VALUES (1.1,1); CREATE INDEX hash_idx ON hash_heap_float4 USING hash (x); DROP TABLE hash_heap_float4 CASCADE; + +-- Check reloptions min max values and that it is not allowed to ALTER= +CREATE INDEX hash_f8_index2 ON hash_f8_heap USING hash (random float8_= ops) WITH (fillfactor=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= /reloptions.sql new file mode 100644 index 0000000..0bfec28 =2D-- /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_e= nabled =3D false, autovacuum_analyze_scale_factor =3D 0.2); + +SELECT reloptions FROM pg_class WHERE oid =3D 'reloptions_test'::regcl= ass; + +-- Test =D1=81ase insensitive +CREATE TABLE reloptions_test3(i INT) WITH (FiLlFaCtoR=3D40); + +SELECT reloptions FROM pg_class WHERE oid =3D 'reloptions_test3'::regc= lass; + +-- 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_fa= ctor =3D -10.0); + +CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_fa= ctor =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.fill= factor=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'strin= g'); + +CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_fa= ctor=3D'string'); + +CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_fa= ctor=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_s= cale_factor =3D 0.3); + +SELECT reloptions FROM pg_class WHERE oid =3D 'reloptions_test'::regcl= ass; + +-- 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'::regcl= ass; + +-- Check that RESET works well + +ALTER TABLE reloptions_test RESET (fillfactor); + +SELECT reloptions FROM pg_class WHERE oid =3D 'reloptions_test'::regcl= ass; + +-- Check that RESETting all values make NULL reloptions record in pg_c= lass +ALTER TABLE reloptions_test RESET (autovacuum_enabled, autovacuum_anal= yze_scale_factor); +SELECT reloptions FROM pg_class WHERE oid =3D 'reloptions_test'::regcl= ass 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'::regcl= ass; + +-- Now testing toast.* options +DROP TABLE reloptions_test; + +CREATE TABLE reloptions_test (s VARCHAR) WITH (toast.autovacuum_vacuum= _cost_delay =3D 23 ); + +SELECT reloptions FROM pg_class WHERE oid =3D (SELECT reltoastrelid FR= OM pg_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 FR= OM pg_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 FR= OM pg_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 rel= ation +CREATE TABLE reloptions_test2 (i int) WITH (toast.autovacuum_vacuum_co= st_delay =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 sh= ould be +-- accepted by heap but rejected by toast (special case) +DROP TABLE reloptions_test; +CREATE TABLE reloptions_test (s VARCHAR) WITH (autovacuum_analyze_scal= e_factor=3D1, autovacuum_analyze_threshold=3D1); + +CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analy= ze_scale_factor=3D1); +CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analy= ze_threshold=3D1); + +-- And now mixed toast + heap +DROP TABLE reloptions_test; + +CREATE TABLE reloptions_test (s VARCHAR) WITH (toast.autovacuum_vacuum= _cost_delay =3D 23, autovacuum_vacuum_cost_delay =3D 24, fillfactor =3D= 40); + +SELECT reloptions FROM pg_class WHERE oid =3D 'reloptions_test'::regcl= ass; +SELECT reloptions FROM pg_class WHERE oid =3D (SELECT reltoastrelid FR= OM pg_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 (fillfact= or=3D30); + +SELECT reloptions FROM pg_class WHERE oid =3D 'reloptions_test_idx'::r= egclass; + +-- Fail when option and namespase do not exist + +CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_exis= ting_option=3D2); + +CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_exis= ting_option.fillfactor=3D2); + +-- Check ranges + +CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfac= tor=3D1); + +CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfac= tor=3D130); + +-- Check alter + +ALTER INDEX reloptions_test_idx SET (fillfactor=3D40); + +SELECT reloptions FROM pg_class WHERE oid =3D 'reloptions_test_idx'::r= egclass; + +-- 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'::= regclass; + + diff --git a/src/test/regress/sql/spgist.sql b/src/test/regress/sql/spg= ist.sql index 5896b50..8d26e49 100644 =2D-- 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); =2Dcreate index spgist_point_idx on spgist_point_tbl using spgist(p); +create index spgist_point_idx on spgist_point_tbl using spgist(p) with= (fillfactor =3D 75); =20 -- Test vacuum-root operation. It gets invoked when the root is also a= leaf, -- i.e. the index is very small. @@ -48,3 +48,10 @@ select g, 'baaaaaaaaaaaaaar' || g from generate_seri= es(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_serie= s(1, 100) g; + +-- Check reloptions min, max values +create index spgist_point_idx2 on spgist_point_tbl using spgist(p) wit= h (fillfactor =3D 9); +create index spgist_point_idx2 on spgist_point_tbl using spgist(p) wit= h (fillfactor =3D 101); + +-- ALTER reloption should work +alter index spgist_point_idx set (fillfactor =3D 76); =2D-nextPart5742593.9NUVLNG8jH-- This is a multi-part message in MIME format. --nextPart5742593.9NUVLNG8jH Content-Transfer-Encoding: base64 Content-Type: text/plain; charset="utf-8" 0JIg0L/QuNGB0YzQvNC1INC+0YIgMTkg0LDQv9GA0LXQu9GPIDIwMTcgMjE6MzE6NDIg0L/QvtC7 0YzQt9C+0LLQsNGC0LXQu9GMIE5pa29sYXkgU2hhcGxvdiDQvdCw0L/QuNGB0LDQuzoKCkkgcmVi YXNlZCB0aGUgcGF0Y2ggYWdhaW4sIGFmdGVyIGNvZGUgcmVpbmRlbnQuCgpTdGlsbCB3YWl0aW5n IGZvciByZXZpZXcuCgotLSAKRG8gY29kZSBmb3IgZnVuLiBDYW4gZG8gaXQgZm9yIG1vbmV5IChQ ZXJsICYgQy9DKysgfjEwaC93ZWVrKQ== --nextPart5742593.9NUVLNG8jH Content-Disposition: attachment; filename="reloptions7c.diff" Content-Transfer-Encoding: quoted-printable Content-Type: text/x-patch; charset="UTF-8"; name="reloptions7c.diff" diff --git a/contrib/bloom/bloom.h b/contrib/bloom/bloom.h index f3df1af..9c27326 100644 --- a/contrib/bloom/bloom.h +++ b/contrib/bloom/bloom.h @@ -204,7 +204,6 @@ extern IndexBulkDeleteResult *blbulkdelete(IndexVac= uumInfo *info, =09=09=09 void *callback_state); extern IndexBulkDeleteResult *blvacuumcleanup(IndexVacuumInfo *info, =09=09=09=09IndexBulkDeleteResult *stats); -extern bytea *bloptions(Datum reloptions, bool validate); extern void blcostestimate(PlannerInfo *root, IndexPath *path, =09=09=09 double loop_count, Cost *indexStartupCost, =09=09=09 Cost *indexTotalCost, Selectivity *indexSelectivity, diff --git a/contrib/bloom/blutils.c b/contrib/bloom/blutils.c index f2eda67..e937fed 100644 --- a/contrib/bloom/blutils.c +++ b/contrib/bloom/blutils.c @@ -35,49 +35,13 @@ =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 op= tions. - * - * Note: keep this in sync with makeDefaultBloomOptions(). - */ -void -_PG_init(void) -{ -=09int=09=09=09i; -=09char=09=09buf[16]; - -=09bl_relopt_kind =3D add_reloption_kind(); - -=09/* Option for length of signature */ -=09add_int_reloption(bl_relopt_kind, "length", -=09=09=09=09=09 "Length of signature in bits", -=09=09=09=09=09 DEFAULT_BLOOM_LENGTH, 1, MAX_BLOOM_LENGTH); -=09bl_relopt_tab[0].optname =3D "length"; -=09bl_relopt_tab[0].opttype =3D RELOPT_TYPE_INT; -=09bl_relopt_tab[0].offset =3D offsetof(BloomOptions, bloomLength); - -=09/* Number of bits for each possible index column: col1, col2, ... *= / -=09for (i =3D 0; i < INDEX_MAX_KEYS; i++) -=09{ -=09=09snprintf(buf, sizeof(buf), "col%d", i + 1); -=09=09add_int_reloption(bl_relopt_kind, buf, -=09=09=09=09=09=09 "Number of bits generated for each index column", -=09=09=09=09=09=09 DEFAULT_BLOOM_BITS, 1, MAX_BLOOM_BITS); -=09=09bl_relopt_tab[i + 1].optname =3D MemoryContextStrdup(TopMemoryCo= ntext, -=09=09=09=09=09=09=09=09=09=09=09=09=09=09 buf); -=09=09bl_relopt_tab[i + 1].opttype =3D RELOPT_TYPE_INT; -=09=09bl_relopt_tab[i + 1].offset =3D offsetof(BloomOptions, bitSize[0= ]) + sizeof(int) * i; -=09} -} +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) =09amroutine->amvacuumcleanup =3D blvacuumcleanup; =09amroutine->amcanreturn =3D NULL; =09amroutine->amcostestimate =3D blcostestimate; -=09amroutine->amoptions =3D bloptions; +=09amroutine->amrelopt_catalog =3D blrelopt_catalog; =09amroutine->amproperty =3D NULL; =09amroutine->amvalidate =3D blvalidate; =09amroutine->ambeginscan =3D blbeginscan; @@ -146,6 +110,28 @@ blhandler(PG_FUNCTION_ARGS) =09PG_RETURN_POINTER(amroutine); } =20 +void +blReloptionPostprocess(void *data, bool validate) +{ +=09BloomOptions *opts =3D (BloomOptions *) data; +=09int=09=09=09i; + +=09if (validate) +=09=09for (i =3D 0; i < INDEX_MAX_KEYS; i++) +=09=09{ +=09=09=09if (opts->bitSize[i] >=3D opts->bloomLength) +=09=09=09{ +=09=09=09=09ereport(ERROR, +=09=09=09=09=09=09(errcode(ERRCODE_INVALID_PARAMETER_VALUE), +=09=09=09=09=09 errmsg("col%i should not be grater than length", i))= ); +=09=09=09} +=09=09} + +=09/* Convert signature length from # of bits to # to words, rounding = up */ +=09opts->bloomLength =3D (opts->bloomLength + SIGNWORDBITS - 1) / SIGN= WORDBITS; +} + + /* * Fill BloomState structure for particular index. */ @@ -466,24 +452,40 @@ BloomInitMetapage(Relation index) =09UnlockReleaseBuffer(metaBuffer); } =20 -/* - * Parse reloptions for bloom index, producing a BloomOptions struct. - */ -bytea * -bloptions(Datum reloptions, bool validate) +static void * +blrelopt_catalog(void) { -=09relopt_value *options; -=09int=09=09=09numoptions; -=09BloomOptions *rdopts; +=09if (!bl_relopt_catalog) +=09{ =20 -=09/* Parse the user-given reloptions */ -=09options =3D parseRelOptions(reloptions, validate, bl_relopt_kind, &= numoptions); -=09rdopts =3D allocateReloptStruct(sizeof(BloomOptions), options, numo= ptions); -=09fillRelOptions((void *) rdopts, sizeof(BloomOptions), options, numo= ptions, -=09=09=09=09 validate, bl_relopt_tab, lengthof(bl_relopt_tab)); +=09=09int=09=09=09i; +=09=09char=09=09buf[16]; =20 -=09/* Convert signature length from # of bits to # to words, rounding = up */ -=09rdopts->bloomLength =3D (rdopts->bloomLength + SIGNWORDBITS - 1) / = SIGNWORDBITS; +=09=09bl_relopt_catalog =3D allocateOptionsCatalog(NULL, +=09=09=09=09=09=09=09=09 sizeof(BloomOptions), INDEX_MAX_KEYS + 1); +=09=09bl_relopt_catalog->postprocess_fun =3D blReloptionPostprocess; + +=09=09optionsCatalogAddItemInt(bl_relopt_catalog, "length", +=09=09=09=09=09=09=09=09 "Length of signature in bits", +=09=09=09=09=09=09=09=09 NoLock,=09=09/* No lock as far as ALTER is +=09=09=09=09=09=09=09=09=09=09=09=09 * forbidden */ +=09=09=09=09=09=09=09=09 OPTION_DEFINITION_FLAG_FORBID_ALTER, +=09=09=09=09=09=09=09=09 offsetof(BloomOptions, bloomLength), +=09=09=09=09=09=09=09=09 DEFAULT_BLOOM_LENGTH, 1, MAX_BLOOM_LENGTH); =20 -=09return (bytea *) rdopts; +=09=09/* Number of bits for each possible index column: col1, col2, ..= . */ +=09=09for (i =3D 0; i < INDEX_MAX_KEYS; i++) +=09=09{ +=09=09=09snprintf(buf, 16, "col%d", i + 1); +=09=09=09optionsCatalogAddItemInt(bl_relopt_catalog, buf, +=09=09=09=09=09=09=09=09 "Number of bits for corresponding column", +=09=09=09=09=09=09=09=09=09 NoLock,=09/* No lock as far as ALTER is +=09=09=09=09=09=09=09=09=09=09=09=09 * forbidden */ +=09=09=09=09=09=09=09=09=09 OPTION_DEFINITION_FLAG_FORBID_ALTER, +=09=09=09=09=09=09=09=09=09 offsetof(BloomOptions, bitSize[i]), +=09=09=09=09=09=09=09=09=09 DEFAULT_BLOOM_BITS, 1, MAX_BLOOM_BITS); +=09=09} + +=09} +=09return (void *) bl_relopt_catalog; } diff --git a/contrib/bloom/expected/bloom.out b/contrib/bloom/expected/= bloom.out index cbc50f7..7e3bbc0 100644 --- a/contrib/bloom/expected/bloom.out +++ b/contrib/bloom/expected/bloom.out @@ -210,3 +210,37 @@ ORDER BY 1; text_ops | t (2 rows) =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 +------------------- + {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; =09amroutine->amcanreturn =3D NULL; =09amroutine->amcostestimate =3D brincostestimate; -=09amroutine->amoptions =3D brinoptions; =09amroutine->amproperty =3D NULL; =09amroutine->amvalidate =3D brinvalidate; =09amroutine->ambeginscan =3D brinbeginscan; @@ -116,6 +116,7 @@ brinhandler(PG_FUNCTION_ARGS) =09amroutine->amendscan =3D brinendscan; =09amroutine->ammarkpos =3D NULL; =09amroutine->amrestrpos =3D NULL; +=09amroutine->amrelopt_catalog =3D bringetreloptcatalog; =09amroutine->amestimateparallelscan =3D NULL; =09amroutine->aminitparallelscan =3D NULL; =09amroutine->amparallelrescan =3D NULL; @@ -798,37 +799,6 @@ brinvacuumcleanup(IndexVacuumInfo *info, IndexBulk= DeleteResult *stats) } =20 /* - * reloptions processor for BRIN indexes - */ -bytea * -brinoptions(Datum reloptions, bool validate) -{ -=09relopt_value *options; -=09BrinOptions *rdopts; -=09int=09=09=09numoptions; -=09static const relopt_parse_elt tab[] =3D { -=09=09{"pages_per_range", RELOPT_TYPE_INT, offsetof(BrinOptions, pages= PerRange)}, -=09=09{"autosummarize", RELOPT_TYPE_BOOL, offsetof(BrinOptions, autosu= mmarize)} -=09}; - -=09options =3D parseRelOptions(reloptions, validate, RELOPT_KIND_BRIN,= -=09=09=09=09=09=09=09 &numoptions); - -=09/* if none set, we're done */ -=09if (numoptions =3D=3D 0) -=09=09return NULL; - -=09rdopts =3D allocateReloptStruct(sizeof(BrinOptions), options, numop= tions); - -=09fillRelOptions((void *) rdopts, sizeof(BrinOptions), options, numop= tions, -=09=09=09=09 validate, tab, lengthof(tab)); - -=09pfree(options); - -=09return (bytea *) rdopts; -} - -/* * SQL-callable function to scan through an index and summarize all ra= nges * that are not currently summarized. */ @@ -1426,3 +1396,33 @@ brin_vacuum_scan(Relation idxrel, BufferAccessSt= rategy strategy) =09if (vacuum_fsm) =09=09FreeSpaceMapVacuum(idxrel); } + +static options_catalog *brin_relopt_catalog =3D NULL; + +static void * +bringetreloptcatalog(void) +{ +=09if (!brin_relopt_catalog) +=09{ +=09=09brin_relopt_catalog =3D allocateOptionsCatalog(NULL, +=09=09=09=09=09=09=09=09=09=09=09=09=09 sizeof(BrinOptions), 2); + +=09=09optionsCatalogAddItemInt(brin_relopt_catalog, "pages_per_range",= +=09=09=09 "Number of pages that each page range covers in a BRIN ind= ex", +=09=09=09=09=09=09=09=09 NoLock,=09=09/* since ALTER is not allowed +=09=09=09=09=09=09=09=09=09=09=09=09 * no lock needed */ +=09=09=09=09=09=09=09=09 OPTION_DEFINITION_FLAG_FORBID_ALTER, +=09=09=09=09=09=09=09=09 offsetof(BrinOptions, pagesPerRange), +=09=09=09=09=09=09=09=09 BRIN_DEFAULT_PAGES_PER_RANGE, +=09=09=09=09=09=09=09=09 BRIN_MIN_PAGES_PER_RANGE, +=09=09=09=09=09=09=09=09 BRIN_MAX_PAGES_PER_RANGE); + +=09=09optionsCatalogAddItemBool(brin_relopt_catalog, "autosummarize", +=09=09=09=09=09=09"Enables automatic summarization on this BRIN index"= , +=09=09=09=09=09=09=09=09 AccessExclusiveLock, +=09=09=09=09=09=09=09=09 0, +=09=09=09=09=09=09=09=09 offsetof(BrinOptions, autosummarize), +=09=09=09=09=09=09=09=09 false); +=09} +=09return brin_relopt_catalog; +} diff --git a/src/backend/access/common/Makefile b/src/backend/access/co= mmon/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 pr= inttup.o \ =09reloptions.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/c= ommon/options.c new file mode 100644 index 0000000..4250173 --- /dev/null +++ b/src/backend/access/common/options.c @@ -0,0 +1,1445 @@ +/*--------------------------------------------------------------------= ----- + * + * options.c + *=09 Core support for options used for relotions (pg_class.reloption= s), + *=09 attoptions (pg_attribute.attoptions), and can be used for other= + *=09 kinds of options. + * + *=09 Here you can find functions that allow to define available opti= ons + *=09 for certain object (certain retaion type, attribute type etc), = and + *=09 functions that allows to convert options from one represenation= to + *=09 another and validate them. + * + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Gro= up + * Portions Copyright (c) 1994, Regents of the University of Californi= a + * + * + * IDENTIFICATION + *=09 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 cr= eate an + * options catalog using allocateOptionsCatalog function, fill it wit= h + * optionsCatalogAddItemXxxx functions with definition of available op= tions. + * Then instance of this object can take option values from syntax ana= lyzer + * when instance in created using SQL command or from attribute of + * pg_catalog.yyyy table, when object that already exists is loaded fo= rm + * pg_catalog. + * + * Now options are mainly used for all kind or relations (pg_class.rel= option) + * 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 an= d 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, cha= r *name, +=09=09=09=09=09=09 char *desc, LOCKMODE lockmode, +=09=09=09=09=09=09 option_definition_flags flags, int struct_offset); + +static void parse_one_option(option_value * option, char *text_str, +=09=09=09=09 int text_len, bool validate); +static void *optionsAllocateBytea(options_catalog * catalog, List *opt= ions); + + +static List * +optionsDefListToRawValues(List *defList, options_parse_mode +=09=09=09=09=09=09 parse_mode); +static Datum optionsValuesToTextArray(List *options_values); +static List *optionsTextArrayToRawValues(Datum array_datum); +static List *optionsMergeOptionValues(List *old_options, List *new_opt= ions); +static List *optionsParseRawValues(List *raw_values, options_catalog *= catalog, +=09=09=09=09=09 options_parse_mode mode); +static bytea *optionsValuesToBytea(List *options, options_catalog * ca= talog); + + +/* + * Options catalog functions + */ + +/* + * Options catalog describes options available for certain object. Cat= alog has + * all necessary information for parsing transforming and validating o= ptions + * for an object. All parsing/validation/transformation functions shou= ld not + * know any details of option implementation for certain object, all t= his + * information should be stored in catalog instead and interpreted by + * pars/valid/transf functions blindly. + * + * The heart of the option catalog is an array of option definitions. = Options + * definition specifies name of option, type, range of acceptable valu= es, and + * default value. + * + * Options values can be one of the following types: bool, int, real, = enum, + * string. For more info see "option_type" and "optionsCatalogAddItemY= yyy" + * functions. + * + * Option definition flags allows to define parser behavior for specia= l (or not + * so special) cases. See option_definition_flags for more info. + * + * Options and Lock levels: + * + * The default choice for any new option should be AccessExclusiveLock= . + * In some cases the lock level can be reduced from there, but the loc= k + * level chosen should always conflict with itself to ensure that mult= iple + * 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 chan= ge it. + * Safety first. Existing choices are documented here, and elsewhere i= n + * backend code where the parameters are used. + * + * In general, anything that affects the results obtained from a SELEC= T must be + * protected by AccessExclusiveLock. + * + * Autovacuum related parameters can be set at ShareUpdateExclusiveLoc= k + * since they are only used by the AV procs and don't change anything + * currently executing. + * + * Fillfactor can be set because it applies only to subsequent changes= made to + * data blocks, as documented in heapio.c + * + * n_distinct options can be set at ShareUpdateExclusiveLock because t= hey + * 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 stronge= r lock. + * + * Planner-related parameters can be set with ShareUpdateExclusiveLock= because + * they only affect planning and not the correctness of the execution.= Plans + * cannot be changed in mid-flight, so changes here could not easily r= esult in + * new improved plans in any case. So we allow existing queries to con= tinue + * and existing plans to survive, a small price to pay for allowing be= tter + * 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 d= oesn't + * affect existing plans or queries. +*/ + +/* + * allocateOptionsCatalog + *=09=09Allocate memory for a new catalog and initializes structure me= mbers. + * + * namespace - name of a namespace in which all items of the catalog + * exists (E.g. namespace.option=3Dvalue). For now postgres uses only = toast. + * namespace for tables. + * + * size_of_bytea - size of bytea-packed C-structure where all option v= alues + * would be finally mapped, so it can be fast and easily used by pg co= re + * + * num_items_expected - expected number of items in options catalog. I= f it is + * set to positive value, proper chunk of memory would be allocated, a= nd postgres + * will assert if you try to add more options to the catalog. If it is= set to -1, then + * you can add any number of options to catalog, memory for entries wo= uld be dynamically + * reallocated while adding. This feature was left for backward compat= ibility and is + * not encouraged for production usage. + */ + +options_catalog * +allocateOptionsCatalog(char *namespace, int size_of_bytea, int num_ite= ms_expected) +{ +=09MemoryContext oldcxt; +=09options_catalog *catalog; + +=09oldcxt =3D MemoryContextSwitchTo(TopMemoryContext); +=09catalog =3D palloc(sizeof(options_catalog)); +=09if (namespace) +=09{ +=09=09catalog->namespace =3D palloc(strlen(namespace) + 1); +=09=09strcpy(catalog->namespace, namespace); +=09} +=09else +=09=09catalog->namespace =3D NULL; +=09if (num_items_expected > 0) +=09{ +=09=09catalog->num_allocated =3D num_items_expected; +=09=09catalog->forbid_realloc =3D true; +=09=09catalog->definitions =3D palloc( +=09=09=09=09 catalog->num_allocated * sizeof(option_definition_basic *= )); +=09} +=09else +=09{ +=09=09catalog->num_allocated =3D 0; +=09=09catalog->forbid_realloc =3D false; +=09=09catalog->definitions =3D NULL; +=09} +=09catalog->num =3D 0; +=09catalog->struct_size =3D size_of_bytea; +=09catalog->postprocess_fun =3D NULL; +=09MemoryContextSwitchTo(oldcxt); +=09return catalog; +} + +/* + * optionCatalogAddItem + *=09=09Add an already-created option definition to the catalog + */ +static void +optionCatalogAddItem(option_definition_basic * newoption, +=09=09=09=09=09 options_catalog * catalog) +{ +=09if (catalog->num >=3D catalog->num_allocated) +=09{ +=09=09MemoryContext oldcxt; + +=09=09Assert(!catalog->forbid_realloc); +=09=09oldcxt =3D MemoryContextSwitchTo(TopMemoryContext); + +=09=09if (catalog->num_allocated =3D=3D 0) +=09=09{ +=09=09=09catalog->num_allocated =3D 8; +=09=09=09catalog->definitions =3D palloc( +=09=09=09=09 catalog->num_allocated * sizeof(option_definition_basic *= )); +=09=09} +=09=09else +=09=09{ +=09=09=09catalog->num_allocated *=3D 2; +=09=09=09catalog->definitions =3D repalloc(catalog->definitions, +=09=09=09=09 catalog->num_allocated * sizeof(option_definition_basic *= )); +=09=09} +=09=09MemoryContextSwitchTo(oldcxt); +=09} +=09catalog->definitions[catalog->num] =3D newoption; +=09catalog->num++; +} + +/* + * allocateOptionDefinition + *=09=09Allocate a new option definition and initialize the type-agnos= tic + *=09=09fields + */ +static option_definition_basic * +allocateOptionDefinition(int type, char *name, char *desc, LOCKMODE lo= ckmode, +=09=09=09=09=09=09 option_definition_flags flags, int struct_offset) +{ +=09MemoryContext oldcxt; +=09size_t=09=09size; +=09option_definition_basic *newoption; + +=09oldcxt =3D MemoryContextSwitchTo(TopMemoryContext); + +=09switch (type) +=09{ +=09=09case OPTION_TYPE_BOOL: +=09=09=09size =3D sizeof(option_definition_bool); +=09=09=09break; +=09=09case OPTION_TYPE_INT: +=09=09=09size =3D sizeof(option_definition_int); +=09=09=09break; +=09=09case OPTION_TYPE_REAL: +=09=09=09size =3D sizeof(option_definition_real); +=09=09=09break; +=09=09case OPTION_TYPE_ENUM: +=09=09=09size =3D sizeof(option_definition_enum); +=09=09=09break; +=09=09case OPTION_TYPE_STRING: +=09=09=09size =3D sizeof(option_definition_string); +=09=09=09break; +=09=09default: +=09=09=09elog(ERROR, "unsupported reloption type %d", type); +=09=09=09return NULL;=09=09/* keep compiler quiet */ +=09} + +=09newoption =3D palloc(size); + +=09newoption->name =3D pstrdup(name); +=09if (desc) +=09=09newoption->desc =3D pstrdup(desc); +=09else +=09=09newoption->desc =3D NULL; +=09newoption->type =3D type; +=09newoption->lockmode =3D lockmode; +=09newoption->flags =3D flags; +=09newoption->struct_offset =3D struct_offset; + +=09MemoryContextSwitchTo(oldcxt); + +=09return newoption; +} + +/* + * optionsCatalogAddItemBool + *=09=09Add a new boolean option definition to the catalog + */ +void +optionsCatalogAddItemBool(options_catalog * catalog, char *name, char = *desc, +=09=09=09=09=09=09 LOCKMODE lockmode, option_definition_flags flags, +=09=09=09=09=09=09 int struct_offset, bool default_val) +{ +=09option_definition_bool *catalog_item; + +=09catalog_item =3D (option_definition_bool *) +=09=09allocateOptionDefinition(OPTION_TYPE_BOOL, name, desc, lockmode,= +=09=09=09=09=09=09=09=09 flags, struct_offset); + +=09catalog_item->default_val =3D default_val; + +=09optionCatalogAddItem((option_definition_basic *) catalog_item, cata= log); +} + +/* + * optionsCatalogAddItemInt + *=09=09Add a new integer option definition to the catalog + */ +void +optionsCatalogAddItemInt(options_catalog * catalog, char *name, +=09=09=09=09char *desc, LOCKMODE lockmode, option_definition_flags fla= gs, +=09=09=09=09int struct_offset, int default_val, int min_val, int max_v= al) +{ +=09option_definition_int *catalog_item; + +=09catalog_item =3D (option_definition_int *) +=09=09allocateOptionDefinition(OPTION_TYPE_INT, name, desc, lockmode, +=09=09=09=09=09=09=09=09 flags, struct_offset); + +=09catalog_item->default_val =3D default_val; +=09catalog_item->min =3D min_val; +=09catalog_item->max =3D max_val; + +=09optionCatalogAddItem((option_definition_basic *) catalog_item, cata= log); +} + +/* + * optionsCatalogAddItemReal + *=09=09Add a new float option to the catalog + */ +void +optionsCatalogAddItemReal(options_catalog * catalog, char *name, char = *desc, +=09=09 LOCKMODE lockmode, option_definition_flags flags, int struct_of= fset, +=09=09=09=09=09=09 double default_val, double min_val, double max_val= ) +{ +=09option_definition_real *catalog_item; + +=09catalog_item =3D (option_definition_real *) +=09=09allocateOptionDefinition(OPTION_TYPE_REAL, name, desc, lockmode,= +=09=09=09=09=09=09=09=09 flags, struct_offset); + +=09catalog_item->default_val =3D default_val; +=09catalog_item->min =3D min_val; +=09catalog_item->max =3D max_val; + +=09optionCatalogAddItem((option_definition_basic *) catalog_item, cata= log); +} + +/* + * optionsCatalogAddItemEnum + *=09=09Add a new enum option to the catalog + * + * "allowed_values" is a pointer to a NULL-terminated char* array (las= t item of + * an array should be null). This array contains a list of acceptable = values + * for the option. + * + * "default_val" is a number of item in allowed_values array that shou= ld be + * set as default value. If you want to handle "option was not set" sp= ecial + * case, you can set default_val to -1 + */ +void +optionsCatalogAddItemEnum(options_catalog * catalog, char *name, char = *desc, +=09=09 LOCKMODE lockmode, option_definition_flags flags, int struct_of= fset, +=09=09=09=09=09=09 const char **allowed_values, int default_val) +{ +=09option_definition_enum *catalog_item; + +=09catalog_item =3D (option_definition_enum *) +=09=09allocateOptionDefinition(OPTION_TYPE_ENUM, name, desc, lockmode,= +=09=09=09=09=09=09=09=09 flags, struct_offset); + +=09catalog_item->default_val =3D default_val; +=09catalog_item->allowed_values =3D allowed_values; + +=09optionCatalogAddItem((option_definition_basic *) catalog_item, cata= log); +} + +/* + * optionsCatalogAddItemString + *=09=09Add a new string option definition to the catalog + * + * "validator" is an optional function pointer that can be used to tes= t the + * validity of the values. It must elog(ERROR) when the argument stri= ng is + * not acceptable for the variable. Note that the default value must = pass + * the validation. + */ +void +optionsCatalogAddItemString(options_catalog * catalog, char *name, cha= r *desc, +=09=09 LOCKMODE lockmode, option_definition_flags flags, int struct_of= fset, +=09=09=09=09=09=09 char *default_val, validate_string_relopt validator= ) +{ +=09option_definition_string *catalog_item; + +=09/* make sure the validator/default combination is sane */ +=09if (validator) +=09=09(validator) (default_val); + +=09catalog_item =3D (option_definition_string *) +=09=09allocateOptionDefinition(OPTION_TYPE_STRING, name, desc, lockmod= e, +=09=09=09=09=09=09=09=09 flags, struct_offset); +=09catalog_item->validate_cb =3D validator; + +=09if (default_val) +=09=09catalog_item->default_val =3D MemoryContextStrdup(TopMemoryConte= xt, +=09=09=09=09=09=09=09=09=09=09=09=09=09=09default_val); +=09else +=09=09catalog_item->default_val =3D NULL; +=09optionCatalogAddItem((option_definition_basic *) catalog_item, cata= log); +} + + +/* + * Options transform functions + */ + +/* + * Option values exists in five representations: DefList, TextArray, V= alues and + * Bytea: + * + * DefList: Is a List of DefElem structures, that comes from syntax an= alyzer. + * It can be transformed to Values representation for further parsing = and + * validating + * + * Values: A List of option_value structures. Is divided into two subc= lasses: + * RawValues, when values are already transformed from DefList or Text= Array, + * but not parsed yet. (In this case you should use raw_name and raw_v= alue + * structure members to see option content). ParsedValues (or just sim= ple + * Values) is crated after finding a definition for this option in a c= atalog + * and after parsing of the raw value. For ParsedValues content is sto= red in + * values structure member, and name can be taken from option definiti= on in gen + * structure member. Actually Value list can have both Raw and Parsed= values, + * as we do not validate options that came from database, and db optio= n that + * does not exist in catalog is just ignored, and kept as RawValues + * + * TextArray: The representation in which options for existing object= comes + * and goes from/to database; for example from pg_class.reloptions. It= is a + * plain TEXT[] db object with name=3Dvalue text inside. This represen= tation can + * be transformed into Values for further processing, using options ca= talog. + * + * Bytea: Is a binary representation of options. Each object that has = code that + * uses options, should create a C-structure for this options, with va= rlen + * 4-byte header in front of the data; all items of options catalog sh= ould have + * an offset of a corresponding binary data in this structure, so tran= sform + * function can put this data in the correct place. One can transform = options + * data from values representation into Bytea, using catalog data, and= then use + * it as a usual Datum object, when needed. This Datum should be cache= d + * somewhere (for example in rel->rd_options for relations) when objec= t that + * has option is loaded from db. + */ + + +/* optionsDefListToRawValues + *=09=09Converts option values that came from syntax analyzer (DefList= ) into + *=09=09Values List. + * + * No parsing is done here except for checking that RESET syntax is co= rrect + * (syntax analyzer do not see difference between SET and RESET cases,= we + * should treat it here manually + */ +static List * +optionsDefListToRawValues(List *defList, options_parse_mode parse_mode= ) +{ +=09ListCell *cell; +=09List=09 *result =3D NIL; + +=09foreach(cell, defList) +=09{ +=09=09option_value *option_dst; +=09=09DefElem *def =3D (DefElem *) lfirst(cell); +=09=09char=09 *value; + +=09=09option_dst =3D palloc(sizeof(option_value)); + +=09=09if (def->defnamespace) +=09=09{ +=09=09=09option_dst->namespace =3D palloc(strlen(def->defnamespace) + = 1); +=09=09=09strcpy(option_dst->namespace, def->defnamespace); +=09=09} +=09=09else +=09=09{ +=09=09=09option_dst->namespace =3D NULL; +=09=09} +=09=09option_dst->raw_name =3D palloc(strlen(def->defname) + 1); +=09=09strcpy(option_dst->raw_name, def->defname); + +=09=09if (parse_mode & OPTIONS_PARSE_MODE_FOR_RESET) +=09=09{ +=09=09=09/* +=09=09=09 * If this option came from RESET statement we should throw e= rror +=09=09=09 * it it brings us name=3Dvalue data, as syntax analyzer do n= ot +=09=09=09 * prevent it +=09=09=09 */ +=09=09=09if (def->arg !=3D NULL) +=09=09=09=09ereport(ERROR, +=09=09=09=09=09=09(errcode(ERRCODE_SYNTAX_ERROR), +=09=09=09=09=09errmsg("RESET must not include values for parameters"))= ); + +=09=09=09option_dst->status =3D OPTION_VALUE_STATUS_FOR_RESET; +=09=09} +=09=09else +=09=09{ +=09=09=09/* +=09=09=09 * For SET statement we should treat (name) expression as if = it is +=09=09=09 * actually (name=3Dtrue) so do it here manually. In other ca= ses +=09=09=09 * just use value as we should use it +=09=09=09 */ +=09=09=09option_dst->status =3D OPTION_VALUE_STATUS_RAW; +=09=09=09if (def->arg !=3D NULL) +=09=09=09=09value =3D defGetString(def); +=09=09=09else +=09=09=09=09value =3D "true"; +=09=09=09option_dst->raw_value =3D palloc(strlen(value) + 1); +=09=09=09strcpy(option_dst->raw_value, value); +=09=09} + +=09=09result =3D lappend(result, option_dst); +=09} +=09return result; +} + +/* + * optionsValuesToTextArray + *=09=09Converts List of option_values into TextArray + * + *=09Convertation is made to put options into database (e.g. in + *=09pg_class.reloptions for all relation options) + */ + +Datum +optionsValuesToTextArray(List *options_values) +{ +=09ArrayBuildState *astate =3D NULL; +=09ListCell *cell; +=09Datum=09=09result; + +=09foreach(cell, options_values) +=09{ +=09=09option_value *option =3D (option_value *) lfirst(cell); +=09=09const char *name; +=09=09char=09 *value; +=09=09text=09 *t; +=09=09int=09=09=09len; + +=09=09/* +=09=09 * Raw value were not cleared while parsing, so instead of conve= rting +=09=09 * it back, just use it to store value as text +=09=09 */ +=09=09value =3D option->raw_value; + +=09=09Assert(option->status !=3D OPTION_VALUE_STATUS_EMPTY); + +=09=09/* +=09=09 * Name will be taken from option definition, if option were par= sed or +=09=09 * from raw_name if option were not parsed for some reason +=09=09 */ +=09=09if (option->status =3D=3D OPTION_VALUE_STATUS_PARSED) +=09=09=09name =3D option->gen->name; +=09=09else +=09=09=09name =3D option->raw_name; + +=09=09/* +=09=09 * Now build "name=3Dvalue" string and append it to the array +=09=09 */ +=09=09len =3D VARHDRSZ + strlen(name) + strlen(value) + 1; +=09=09t =3D (text *) palloc(len + 1); +=09=09SET_VARSIZE(t, len); +=09=09sprintf(VARDATA(t), "%s=3D%s", name, value); +=09=09astate =3D accumArrayResult(astate, PointerGetDatum(t), false, +=09=09=09=09=09=09=09=09 TEXTOID, CurrentMemoryContext); +=09} +=09if (astate) +=09=09result =3D makeArrayResult(astate, CurrentMemoryContext); +=09else +=09=09result =3D (Datum) 0; + +=09return result; +} + +/* + * optionsTextArrayToRawValues + *=09=09Converts options from TextArray format into RawValues list. + * + *=09This function is used to convert options data that comes from dat= abase to + *=09List of option_values, for further parsing, and, in the case of A= LTER + *=09command, for merging with new option values. + */ +static List * +optionsTextArrayToRawValues(Datum array_datum) +{ +=09List=09 *result =3D NIL; + +=09if (PointerIsValid(DatumGetPointer(array_datum))) +=09{ +=09=09ArrayType *array =3D DatumGetArrayTypeP(array_datum); +=09=09Datum=09 *options; +=09=09int=09=09=09noptions; +=09=09int=09=09=09i; + +=09=09deconstruct_array(array, TEXTOID, -1, false, 'i', +=09=09=09=09=09=09 &options, NULL, &noptions); + +=09=09for (i =3D 0; i < noptions; i++) +=09=09{ +=09=09=09option_value *option_dst; +=09=09=09char=09 *text_str =3D VARDATA(options[i]); +=09=09=09int=09=09=09text_len =3D VARSIZE(options[i]) - VARHDRSZ; +=09=09=09int=09=09=09i; +=09=09=09int=09=09=09name_len =3D -1; +=09=09=09char=09 *name; +=09=09=09int=09=09=09raw_value_len; +=09=09=09char=09 *raw_value; + +=09=09=09/* +=09=09=09 * Find position of '=3D' sign and treat id as a separator be= tween +=09=09=09 * name and value in "name=3Dvalue" item +=09=09=09 */ +=09=09=09for (i =3D 0; i < text_len; i =3D i + pg_mblen(text_str)) +=09=09=09{ +=09=09=09=09if (text_str[i] =3D=3D '=3D') +=09=09=09=09{ +=09=09=09=09=09name_len =3D i; +=09=09=09=09=09break; +=09=09=09=09} +=09=09=09} +=09=09=09Assert(name_len >=3D 1);=09=09/* Just in case */ + +=09=09=09raw_value_len =3D text_len - name_len - 1; + +=09=09=09/* +=09=09=09 * Copy name from src +=09=09=09 */ +=09=09=09name =3D palloc(name_len + 1); +=09=09=09memcpy(name, text_str, name_len); +=09=09=09name[name_len] =3D '\0'; + +=09=09=09/* +=09=09=09 * Copy value from src +=09=09=09 */ +=09=09=09raw_value =3D palloc(raw_value_len + 1); +=09=09=09memcpy(raw_value, text_str + name_len + 1, raw_value_len); +=09=09=09raw_value[raw_value_len] =3D '\0'; + +=09=09=09/* +=09=09=09 * Create new option_value item +=09=09=09 */ +=09=09=09option_dst =3D palloc(sizeof(option_value)); +=09=09=09option_dst->status =3D OPTION_VALUE_STATUS_RAW; +=09=09=09option_dst->raw_name =3D name; +=09=09=09option_dst->raw_value =3D raw_value; + +=09=09=09result =3D lappend(result, option_dst); +=09=09} +=09} +=09return result; +} + +/* + * optionsMergeOptionValues + *=09=09Merges 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 w= ith new + * options list. This function also process RESET variant of ALTER com= mand. It + * merges two lists as usual, and then removes all items with RESET fl= ag on. + * + * Both incoming lists will be destroyed while merging + */ +static List * +optionsMergeOptionValues(List *old_options, List *new_options) +{ +=09List=09 *result =3D NIL; +=09ListCell *old_cell; +=09ListCell *old_prev; +=09ListCell *old_next; +=09ListCell *new_cell; +=09ListCell *new_prev; +=09ListCell *new_next; + +=09old_prev =3D NULL; + +=09/* +=09 * First try to remove from old options list all values that exists= in a +=09 * new options list +=09 */ +=09for (old_cell =3D list_head(old_options); old_cell; old_cell =3D ol= d_next) +=09{ +=09=09bool=09=09found; +=09=09const char *old_name; +=09=09option_value *old_option; + +=09=09old_next =3D lnext(old_cell); +=09=09old_option =3D (option_value *) lfirst(old_cell); +=09=09if (old_option->status =3D=3D OPTION_VALUE_STATUS_PARSED) +=09=09=09old_name =3D old_option->gen->name; +=09=09else +=09=09=09old_name =3D old_option->raw_name; + +=09=09/* +=09=09 * Try to find old_name option among new option +=09=09 */ +=09=09found =3D false; +=09=09foreach(new_cell, new_options) +=09=09{ +=09=09=09option_value *new_option; +=09=09=09const char *new_name; + +=09=09=09new_option =3D (option_value *) lfirst(new_cell); +=09=09=09if (new_option->status =3D=3D OPTION_VALUE_STATUS_PARSED) +=09=09=09=09new_name =3D new_option->gen->name; +=09=09=09else +=09=09=09=09new_name =3D new_option->raw_name; +=09=09=09if (pg_strcasecmp(new_name, old_name) =3D=3D 0) +=09=09=09{ +=09=09=09=09found =3D true; +=09=09=09=09break; +=09=09=09} +=09=09} + +=09=09/* +=09=09 * If found, delete old option from the list +=09=09 */ +=09=09if (found) +=09=09{ +=09=09=09old_options =3D list_delete_cell(old_options, old_cell, old_p= rev); +=09=09} +=09=09else +=09=09{ +=09=09=09old_prev =3D old_cell; +=09=09} +=09} + +=09/* +=09 * Remove from new_options all options that are for RESET. In old l= ist all +=09 * this options were already removed in previous block, so all RESE= T +=09 * options are cleaned now +=09 */ +=09new_prev =3D NULL; +=09for (new_cell =3D list_head(new_options); new_cell; new_cell =3D ne= w_next) +=09{ +=09=09option_value *new_option =3D (option_value *) lfirst(new_cell); + +=09=09new_next =3D lnext(new_cell); + +=09=09if (new_option->status =3D=3D OPTION_VALUE_STATUS_FOR_RESET) +=09=09=09new_options =3D list_delete_cell(new_options, new_cell, new_p= rev); +=09=09else +=09=09=09new_prev =3D new_cell; +=09} + +=09/* +=09 * Now merge what remained of both lists +=09 */ +=09result =3D list_concat(old_options, new_options); +=09return result; +} + +/* + * optionsDefListValdateNamespaces + *=09=09Function checks that all options represented as DefList has no= + *=09=09namespaces or have namespaces only from allowed list + * + * Function accept options as DefList and NULL terminated list of allo= wed + * namespaces. It throws an error if not proper namespace was found. + * + * This function actually used only for tables with it's toast. namesp= ace + */ +void +optionsDefListValdateNamespaces(List *defList, char **allowed_namespac= es) +{ +=09ListCell *cell; + +=09foreach(cell, defList) +=09{ +=09=09DefElem *def =3D (DefElem *) lfirst(cell); + +=09=09/* +=09=09 * Checking namespace only for options that have namespaces. Opt= ions +=09=09 * with no namespaces are always accepted +=09=09 */ +=09=09if (def->defnamespace) +=09=09{ +=09=09=09bool=09=09found =3D false; +=09=09=09int=09=09=09i =3D 0; + +=09=09=09while (allowed_namespaces[i]) +=09=09=09{ +=09=09=09=09if (pg_strcasecmp(def->defnamespace, +=09=09=09=09=09=09=09=09 allowed_namespaces[i]) =3D=3D 0) +=09=09=09=09{ +=09=09=09=09=09found =3D true; +=09=09=09=09=09break; +=09=09=09=09} +=09=09=09=09i++; +=09=09=09} +=09=09=09if (!found) +=09=09=09=09ereport(ERROR, +=09=09=09=09=09=09(errcode(ERRCODE_INVALID_PARAMETER_VALUE), +=09=09=09=09=09=09 errmsg("unrecognized parameter namespace \"%s\"", +=09=09=09=09=09=09=09=09def->defnamespace))); +=09=09} +=09} +} + +/* + * optionsDefListFilterNamespaces + *=09=09Iterates over DefList, choose items with specified namespace a= nd adds + *=09=09them to a result List + * + * This function does not destroy source DefList but does not create c= opies + * of List nodes. + * It is actually used only for tables, in order to split toast and he= ap + * reloptions, so each one can be stored in on it's own pg_class recor= d + */ +List * +optionsDefListFilterNamespaces(List *defList, char *namespace) +{ +=09ListCell *cell; +=09List=09 *result =3D NIL; + +=09foreach(cell, defList) +=09{ +=09=09DefElem *def =3D (DefElem *) lfirst(cell); + +=09=09if ((!namespace && !def->defnamespace) || +=09=09=09(namespace && def->defnamespace && +=09=09=09 pg_strcasecmp(namespace, def->defnamespace) =3D=3D 0)) +=09=09{ +=09=09=09result =3D lappend(result, def); +=09=09} +=09} +=09return result; +} + +/* + * optionsTextArrayToDefList + *=09=09Convert the text-array format of reloptions into a List of Def= Elem. + */ +List * +optionsTextArrayToDefList(Datum options) +{ +=09List=09 *result =3D NIL; +=09ArrayType *array; +=09Datum=09 *optiondatums; +=09int=09=09=09noptions; +=09int=09=09=09i; + +=09/* Nothing to do if no options */ +=09if (!PointerIsValid(DatumGetPointer(options))) +=09=09return result; + +=09array =3D DatumGetArrayTypeP(options); + +=09deconstruct_array(array, TEXTOID, -1, false, 'i', +=09=09=09=09=09 &optiondatums, NULL, &noptions); + +=09for (i =3D 0; i < noptions; i++) +=09{ +=09=09char=09 *s; +=09=09char=09 *p; +=09=09Node=09 *val =3D NULL; + +=09=09s =3D TextDatumGetCString(optiondatums[i]); +=09=09p =3D strchr(s, '=3D'); +=09=09if (p) +=09=09{ +=09=09=09*p++ =3D '\0'; +=09=09=09val =3D (Node *) makeString(pstrdup(p)); +=09=09} +=09=09result =3D lappend(result, makeDefElem(pstrdup(s), val, -1)); +=09} + +=09return result; +} + +/* + * optionsParseRawValues + *=09=09Parses and vlaidates (if proper flag is set) option_values. As= a result + *=09=09caller will get the list of parsed (or partly parsed) option_v= alues + * + * This function is used in cases when caller gets raw values from db = or + * syntax and want to parse them. + * This function uses option_catalog to get information about how each= option + * should be parsed. + * If validate mode is off, function found an option that do not have = proper + * option_catalog entry, this option kept unparsed (if some garbage ca= me from + * the DB, we should put it back there) + * + * This function destroys incoming list. + */ +static List * +optionsParseRawValues(List *raw_values, options_catalog * catalog, +=09=09=09=09=09 options_parse_mode mode) +{ +=09ListCell *cell; +=09List=09 *result =3D NIL; +=09bool=09 *is_set; +=09int=09=09=09i; +=09bool=09=09validate =3D mode & OPTIONS_PARSE_MODE_VALIDATE; +=09bool=09=09for_alter =3D mode & OPTIONS_PARSE_MODE_FOR_ALTER; + + +=09is_set =3D palloc0(sizeof(bool) * catalog->num); +=09foreach(cell, raw_values) +=09{ +=09=09option_value *option =3D (option_value *) lfirst(cell); +=09=09bool=09=09found =3D false; +=09=09bool=09=09skip =3D false; + + +=09=09if (option->status =3D=3D OPTION_VALUE_STATUS_PARSED) +=09=09{ +=09=09=09/* +=09=09=09 * This can happen while ALTER, when new values were already +=09=09=09 * parsed, but old values merged from DB are still raw +=09=09=09 */ +=09=09=09result =3D lappend(result, option); +=09=09=09continue; +=09=09} +=09=09if (validate && option->namespace && (!catalog->namespace || +=09=09=09=09 pg_strcasecmp(catalog->namespace, option->namespace) !=3D= 0)) +=09=09{ +=09=09=09ereport(ERROR, +=09=09=09=09=09(errcode(ERRCODE_INVALID_PARAMETER_VALUE), +=09=09=09=09=09 errmsg("unrecognized parameter namespace \"%s\"", +=09=09=09=09=09=09=09option->namespace))); +=09=09} + +=09=09for (i =3D 0; i < catalog->num; i++) +=09=09{ +=09=09=09option_definition_basic *definition =3D catalog->definitions[= i]; + +=09=09=09if (pg_strcasecmp(option->raw_name, +=09=09=09=09=09=09=09 definition->name) =3D=3D 0) +=09=09=09{ +=09=09=09=09/* +=09=09=09=09 * Skip option with "ignore" flag, as it is processed +=09=09=09=09 * somewhere else. (WITH OIDS special case) +=09=09=09=09 */ +=09=09=09=09if (definition->flags & OPTION_DEFINITION_FLAG_IGNORE) +=09=09=09=09{ +=09=09=09=09=09found =3D true; +=09=09=09=09=09skip =3D true; +=09=09=09=09=09break; +=09=09=09=09} + +=09=09=09=09/* +=09=09=09=09 * Reject option as if it was not in catalog. Needed for c= ases +=09=09=09=09 * when option should have default value, but should not b= e +=09=09=09=09 * changed +=09=09=09=09 */ +=09=09=09=09if (definition->flags & OPTION_DEFINITION_FLAG_REJECT) +=09=09=09=09{ +=09=09=09=09=09found =3D false; +=09=09=09=09=09break; +=09=09=09=09} + +=09=09=09=09if (validate && is_set[i]) +=09=09=09=09{ +=09=09=09=09=09ereport(ERROR, +=09=09=09=09=09=09=09(errcode(ERRCODE_INVALID_PARAMETER_VALUE), +=09=09=09=09=09=09 errmsg("parameter \"%s\" specified more than once"= , +=09=09=09=09=09=09=09=09 option->raw_name))); +=09=09=09=09} +=09=09=09=09if ((for_alter) && +=09=09=09=09=09(definition->flags & OPTION_DEFINITION_FLAG_FORBID_ALTE= R)) +=09=09=09=09{ +=09=09=09=09=09ereport(ERROR, +=09=09=09=09=09=09=09(errcode(ERRCODE_INVALID_PARAMETER_VALUE), +=09=09=09=09=09=09 errmsg("changing parameter \"%s\" is not allowed"= , +=09=09=09=09=09=09=09=09 definition->name))); +=09=09=09=09} +=09=09=09=09if (option->status =3D=3D OPTION_VALUE_STATUS_FOR_RESET) +=09=09=09=09{ +=09=09=09=09=09/* +=09=09=09=09=09 * For RESET options do not need further processing so +=09=09=09=09=09 * mark it found and stop searching +=09=09=09=09=09 */ +=09=09=09=09=09found =3D true; +=09=09=09=09=09break; +=09=09=09=09} +=09=09=09=09pfree(option->raw_name); +=09=09=09=09option->raw_name =3D NULL; +=09=09=09=09option->gen =3D definition; +=09=09=09=09parse_one_option(option, NULL, -1, validate); +=09=09=09=09is_set[i] =3D true; +=09=09=09=09found =3D true; +=09=09=09=09break; +=09=09=09} +=09=09} +=09=09if (!found) +=09=09{ +=09=09=09if (validate) +=09=09=09{ +=09=09=09=09if (option->namespace) +=09=09=09=09=09ereport(ERROR, +=09=09=09=09=09=09=09(errcode(ERRCODE_INVALID_PARAMETER_VALUE), +=09=09=09=09=09=09=09 errmsg("unrecognized parameter \"%s.%s\"", +=09=09=09=09=09=09=09=09=09option->namespace, option->raw_name))); +=09=09=09=09else +=09=09=09=09=09ereport(ERROR, +=09=09=09=09=09=09=09(errcode(ERRCODE_INVALID_PARAMETER_VALUE), +=09=09=09=09=09=09=09 errmsg("unrecognized parameter \"%s\"", +=09=09=09=09=09=09=09=09=09option->raw_name))); +=09=09=09} + +=09=09=09/* +=09=09=09 * If we are parsing not in validate mode, then we should kee= p +=09=09=09 * unknown node, because non-validate mode is for data that i= s +=09=09=09 * already in the DB and should not be changed after altering= +=09=09=09 * another entries +=09=09=09 */ +=09=09} +=09=09if (!skip) +=09=09=09result =3D lappend(result, option); +=09} +=09return result; +} + +/* + * parse_one_option + * + *=09=09Subroutine for optionsParseRawValues, to parse and validate a + *=09=09single option's value + */ +static void +parse_one_option(option_value * option, char *text_str, int text_len, +=09=09=09=09 bool validate) +{ +=09char=09 *value; +=09bool=09=09parsed; + +=09value =3D option->raw_value; + +=09switch (option->gen->type) +=09{ +=09=09case OPTION_TYPE_BOOL: +=09=09=09{ +=09=09=09=09parsed =3D parse_bool(value, &option->values.bool_val); +=09=09=09=09if (validate && !parsed) +=09=09=09=09=09ereport(ERROR, +=09=09=09=09=09=09=09(errcode(ERRCODE_INVALID_PARAMETER_VALUE), +=09=09=09=09=09=09errmsg("invalid value for boolean option \"%s\": %s"= , +=09=09=09=09=09=09=09 option->gen->name, value))); +=09=09=09} +=09=09=09break; +=09=09case OPTION_TYPE_INT: +=09=09=09{ +=09=09=09=09option_definition_int *optint =3D +=09=09=09=09(option_definition_int *) option->gen; + +=09=09=09=09parsed =3D parse_int(value, &option->values.int_val, 0, NU= LL); +=09=09=09=09if (validate && !parsed) +=09=09=09=09=09ereport(ERROR, +=09=09=09=09=09=09=09(errcode(ERRCODE_INVALID_PARAMETER_VALUE), +=09=09=09=09=09=09errmsg("invalid value for integer option \"%s\": %s"= , +=09=09=09=09=09=09=09 option->gen->name, value))); +=09=09=09=09if (validate && (option->values.int_val < optint->min || +=09=09=09=09=09=09=09=09 option->values.int_val > optint->max)) +=09=09=09=09=09ereport(ERROR, +=09=09=09=09=09=09=09(errcode(ERRCODE_INVALID_PARAMETER_VALUE), +=09=09=09=09=09=09 errmsg("value %s out of bounds for option \"%s\""= , +=09=09=09=09=09=09=09=09 value, option->gen->name), +=09=09=09=09=09 errdetail("Valid values are between \"%d\" and \"%d\".= ", +=09=09=09=09=09=09=09 optint->min, optint->max))); +=09=09=09} +=09=09=09break; +=09=09case OPTION_TYPE_REAL: +=09=09=09{ +=09=09=09=09option_definition_real *optreal =3D +=09=09=09=09(option_definition_real *) option->gen; + +=09=09=09=09parsed =3D parse_real(value, &option->values.real_val); +=09=09=09=09if (validate && !parsed) +=09=09=09=09=09ereport(ERROR, +=09=09=09=09=09=09=09(errcode(ERRCODE_INVALID_PARAMETER_VALUE), +=09=09=09=09=09=09=09 errmsg("invalid value for floating point option = \"%s\": %s", +=09=09=09=09=09=09=09=09=09option->gen->name, value))); +=09=09=09=09if (validate && (option->values.real_val < optreal->min ||= +=09=09=09=09=09=09=09=09 option->values.real_val > optreal->max)) +=09=09=09=09=09ereport(ERROR, +=09=09=09=09=09=09=09(errcode(ERRCODE_INVALID_PARAMETER_VALUE), +=09=09=09=09=09=09 errmsg("value %s out of bounds for option \"%s\""= , +=09=09=09=09=09=09=09=09 value, option->gen->name), +=09=09=09=09=09 errdetail("Valid values are between \"%f\" and \"%f\".= ", +=09=09=09=09=09=09=09 optreal->min, optreal->max))); +=09=09=09} +=09=09=09break; +=09=09case OPTION_TYPE_ENUM: +=09=09=09{ +=09=09=09=09option_definition_enum *opt_enum =3D +=09=09=09=09(option_definition_enum *) option->gen; +=09=09=09=09int=09=09=09i =3D 0; + +=09=09=09=09parsed =3D false; +=09=09=09=09while (opt_enum->allowed_values[i]) +=09=09=09=09{ +=09=09=09=09=09if (pg_strcasecmp(value, opt_enum->allowed_values[i]) =3D= =3D 0) +=09=09=09=09=09{ +=09=09=09=09=09=09option->values.enum_val =3D i; +=09=09=09=09=09=09parsed =3D true; +=09=09=09=09=09=09break; +=09=09=09=09=09} +=09=09=09=09=09i++; +=09=09=09=09} +=09=09=09=09if (!parsed) +=09=09=09=09{ +=09=09=09=09=09int=09=09=09length =3D 0; +=09=09=09=09=09char=09 *str; +=09=09=09=09=09char=09 *ptr; + +=09=09=09=09=09/* +=09=09=09=09=09 * Generating list of allowed values: "value1", "value2= ", +=09=09=09=09=09 * ... "valueN" +=09=09=09=09=09 */ +=09=09=09=09=09i =3D 0; +=09=09=09=09=09while (opt_enum->allowed_values[i]) +=09=09=09=09=09{ +=09=09=09=09=09=09length +=3D strlen(opt_enum->allowed_values[i]) + 4;= +=09=09=09=09=09=09/* +4: two quotes, one comma, one space */ +=09=09=09=09=09=09i++; +=09=09=09=09=09} + +=09=09=09=09=09/* +=09=09=09=09=09 * one byte not used for comma after the last item will= be +=09=09=09=09=09 * used for \0; for another byte will do -1 +=09=09=09=09=09 */ +=09=09=09=09=09str =3D palloc((length - 1) * sizeof(char)); +=09=09=09=09=09i =3D 0; +=09=09=09=09=09ptr =3D str; +=09=09=09=09=09while (opt_enum->allowed_values[i]) +=09=09=09=09=09{ +=09=09=09=09=09=09if (i !=3D 0) +=09=09=09=09=09=09{ +=09=09=09=09=09=09=09ptr[0] =3D ','; +=09=09=09=09=09=09=09ptr[1] =3D ' '; +=09=09=09=09=09=09=09ptr +=3D 2; +=09=09=09=09=09=09} +=09=09=09=09=09=09ptr[0] =3D '"'; +=09=09=09=09=09=09ptr++; +=09=09=09=09=09=09sprintf(ptr, "%s", opt_enum->allowed_values[i]); +=09=09=09=09=09=09ptr +=3D strlen(ptr); +=09=09=09=09=09=09ptr[0] =3D '"'; +=09=09=09=09=09=09ptr++; +=09=09=09=09=09=09i++; +=09=09=09=09=09} +=09=09=09=09=09*ptr =3D '\0'; + +=09=09=09=09=09ereport(ERROR, +=09=09=09=09=09=09=09(errcode(ERRCODE_INVALID_PARAMETER_VALUE), +=09=09=09=09=09=09=09 errmsg("invalid value for \"%s\" option", +=09=09=09=09=09=09=09=09=09option->gen->name), +=09=09=09=09=09=09=09 errdetail("Valid values are %s.", str))); +=09=09=09=09} +=09=09=09} +=09=09=09break; +=09=09case OPTION_TYPE_STRING: +=09=09=09{ +=09=09=09=09option_definition_string *optstring =3D +=09=09=09=09(option_definition_string *) option->gen; + +=09=09=09=09option->values.string_val =3D value; +=09=09=09=09if (validate && optstring->validate_cb) +=09=09=09=09=09(optstring->validate_cb) (value); +=09=09=09=09parsed =3D true; +=09=09=09} +=09=09=09break; +=09=09default: +=09=09=09elog(ERROR, "unsupported reloption type %d", option->gen->typ= e); +=09=09=09parsed =3D true;=09=09/* quiet compiler */ +=09=09=09break; +=09} + +=09if (parsed) +=09=09option->status =3D OPTION_VALUE_STATUS_PARSED; + +} + +/* + * optionsAllocateBytea + *=09=09Allocates memory for bytea options representation + * + * Function allocates memory for byrea structure of an option, plus ad= ds space + * for values of string options. We should keep all data including str= ing + * values in the same memory chunk, because Cache code copies bytea op= tion + * data from one MemoryConext to another without knowing about it's in= ternal + * structure, so it would not be able to copy string values if they ar= e outside + * of bytea memory chunk. + */ +static void * +optionsAllocateBytea(options_catalog * catalog, List *options) +{ +=09Size=09=09size; +=09int=09=09=09i; +=09ListCell *cell; +=09int=09=09=09length; +=09void=09 *res; + +=09size =3D catalog->struct_size; + +=09/* Calculate size needed to store all string values for this option= */ +=09for (i =3D 0; i < catalog->num; i++) +=09{ +=09=09option_definition_basic *definition =3D catalog->definitions[i];= +=09=09bool=09=09found =3D false; +=09=09option_value *option; + +=09=09/* Not interested in non-string options, skipping */ +=09=09if (definition->type !=3D OPTION_TYPE_STRING) +=09=09=09continue; + +=09=09/* +=09=09 * Trying to find option_value that references definition catalo= g +=09=09 * entry +=09=09 */ +=09=09foreach(cell, options) +=09=09{ +=09=09=09option =3D (option_value *) lfirst(cell); +=09=09=09if (option->status =3D=3D OPTION_VALUE_STATUS_PARSED && +=09=09=09=09pg_strcasecmp(option->gen->name, definition->name) =3D=3D = 0) +=09=09=09{ +=09=09=09=09found =3D true; +=09=09=09=09break; +=09=09=09} +=09=09} +=09=09if (found) +=09=09=09/* If found, it'value will be stored */ +=09=09=09length =3D strlen(option->values.string_val) + 1; +=09=09else +=09=09=09/* If not found, then there would be default value there */ +=09=09if (((option_definition_string *) definition)->default_val) +=09=09=09length =3D strlen( +=09=09=09=09 ((option_definition_string *) definition)->default_val) += 1; +=09=09else +=09=09=09length =3D 0; +=09=09/* Add total length of all string values to basic size */ +=09=09size +=3D length; +=09} + +=09res =3D palloc0(size); +=09SET_VARSIZE(res, size); +=09return res; +} + +/* + * optionsValuesToBytea + *=09=09Converts options from List of option_values to binary bytea st= ructure + * + * 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 a= llocated + * by optionsAllocateBytea which adds some more space for string value= s to + * the size of original structure. All string values are copied there = and + * inside the bytea structure an offset to that value is kept. + * + */ +static bytea * +optionsValuesToBytea(List *options, options_catalog * catalog) +{ +=09char=09 *data; +=09char=09 *string_values_buffer; +=09int=09=09=09i; + +=09data =3D optionsAllocateBytea(catalog, options); + +=09/* place for string data starts right after original structure */ +=09string_values_buffer =3D data + catalog->struct_size; + +=09for (i =3D 0; i < catalog->num; i++) +=09{ +=09=09option_value *found =3D NULL; +=09=09ListCell *cell; +=09=09char=09 *item_pos; +=09=09option_definition_basic *definition =3D catalog->definitions[i];= + +=09=09if (definition->flags & OPTION_DEFINITION_FLAG_IGNORE) +=09=09=09continue; + +=09=09/* Calculate the position of the item inside the structure */ +=09=09item_pos =3D data + definition->struct_offset; + +=09=09/* Looking for the corresponding option from options list */ +=09=09foreach(cell, options) +=09=09{ +=09=09=09option_value *option =3D (option_value *) lfirst(cell); + +=09=09=09if (option->status =3D=3D OPTION_VALUE_STATUS_RAW) +=09=09=09=09continue;=09=09/* raw can come from db. Just ignore them t= hen */ +=09=09=09Assert(option->status !=3D OPTION_VALUE_STATUS_EMPTY); + +=09=09=09if (pg_strcasecmp(definition->name, option->gen->name) =3D=3D= 0) +=09=09=09{ +=09=09=09=09found =3D option; +=09=09=09=09break; +=09=09=09} +=09=09} +=09=09/* writing to the proper position either option value or default= val */ +=09=09switch (definition->type) +=09=09{ +=09=09=09case OPTION_TYPE_BOOL: +=09=09=09=09*(bool *) item_pos =3D found ? +=09=09=09=09=09found->values.bool_val : +=09=09=09=09=09((option_definition_bool *) definition)->default_val; +=09=09=09=09break; +=09=09=09case OPTION_TYPE_INT: +=09=09=09=09*(int *) item_pos =3D found ? +=09=09=09=09=09found->values.int_val : +=09=09=09=09=09((option_definition_int *) definition)->default_val; +=09=09=09=09break; +=09=09=09case OPTION_TYPE_REAL: +=09=09=09=09*(double *) item_pos =3D found ? +=09=09=09=09=09found->values.real_val : +=09=09=09=09=09((option_definition_real *) definition)->default_val; +=09=09=09=09break; +=09=09=09case OPTION_TYPE_ENUM: +=09=09=09=09*(int *) item_pos =3D found ? +=09=09=09=09=09found->values.enum_val : +=09=09=09=09=09((option_definition_enum *) definition)->default_val; +=09=09=09=09break; + +=09=09=09case OPTION_TYPE_STRING: +=09=09=09=09{ +=09=09=09=09=09/* +=09=09=09=09=09 * For string options: writing string value at the stri= ng +=09=09=09=09=09 * buffer after the structure, and storing and offset t= o +=09=09=09=09=09 * that value +=09=09=09=09=09 */ +=09=09=09=09=09char=09 *value =3D NULL; + +=09=09=09=09=09if (found) +=09=09=09=09=09=09value =3D found->values.string_val; +=09=09=09=09=09else +=09=09=09=09=09=09value =3D ((option_definition_string *) definition) +=09=09=09=09=09=09=09->default_val; +=09=09=09=09=09*(int *) item_pos =3D value ? +=09=09=09=09=09=09string_values_buffer - data : +=09=09=09=09=09=09OPTION_STRING_VALUE_NOT_SET_OFFSET; +=09=09=09=09=09if (value) +=09=09=09=09=09{ +=09=09=09=09=09=09strcpy(string_values_buffer, value); +=09=09=09=09=09=09string_values_buffer +=3D strlen(value) + 1; +=09=09=09=09=09} +=09=09=09=09} +=09=09=09=09break; +=09=09=09default: +=09=09=09=09elog(ERROR, "unsupported reloption type %d", +=09=09=09=09=09 definition->type); +=09=09=09=09break; +=09=09} +=09} +=09return (void *) data; +} + + +/* + * transformOptions + *=09=09This function is used by src/backend/commands/Xxxx in order to= process + *=09=09new option values, merge them with existing values (in the cas= e of + *=09=09ALTER command) and prepare to put them [back] into DB + */ + +Datum +transformOptions(options_catalog * catalog, Datum oldOptions, +=09=09=09=09 List *defList, options_parse_mode parse_mode) +{ +=09Datum=09=09result; +=09List=09 *new_values; +=09List=09 *old_values; +=09List=09 *merged_values; + +=09/* +=09 * Parse and validate New values +=09 */ +=09new_values =3D optionsDefListToRawValues(defList, parse_mode); +=09new_values =3D optionsParseRawValues(new_values, catalog, +=09=09=09=09=09=09=09=09 parse_mode | OPTIONS_PARSE_MODE_VALIDATE); + +=09/* +=09 * Old values exists in case of ALTER commands. Transform them to r= aw +=09 * values and merge them with new_values, and parse it. +=09 */ +=09if (PointerIsValid(DatumGetPointer(oldOptions))) +=09{ +=09=09old_values =3D optionsTextArrayToRawValues(oldOptions); +=09=09merged_values =3D optionsMergeOptionValues(old_values, new_value= s); + +=09=09/* +=09=09 * Parse options only after merging in order not to parse option= s that +=09=09 * would be removed by merging later +=09=09 */ +=09=09merged_values =3D optionsParseRawValues(merged_values, catalog, = 0); +=09} +=09else +=09{ +=09=09merged_values =3D new_values; +=09} + +=09/* +=09 * If we have postprocess_fun function defined in catalog, then the= re +=09 * might be some custom options checks there, with error throwing. = So we +=09 * should do it here to throw these errors while CREATing or ALTERi= ng +=09 * options +=09 */ +=09if (catalog->postprocess_fun) +=09{ +=09=09bytea=09 *data =3D optionsValuesToBytea(merged_values, catalog= ); + +=09=09catalog->postprocess_fun(data, true); +=09=09pfree(data); +=09} + +=09/* +=09 * Convert options to TextArray format so caller can store them int= o +=09 * database +=09 */ +=09result =3D optionsValuesToTextArray(merged_values); +=09return result; +} + + +/* + * optionsTextArrayToBytea + *=09=09A meta-function that transforms options stored as TextArray in= to binary + *=09=09(bytea) representation. + * + *=09This function runs other transform functions that leads to the de= sired + *=09result in no-validation mode. This function is used by cache mech= anism, + *=09in order to load and cache options when object itself is loaded a= nd cached + */ +bytea * +optionsTextArrayToBytea(options_catalog * catalog, Datum data) +{ +=09List=09 *values; +=09bytea=09 *options; + +=09values =3D optionsTextArrayToRawValues(data); +=09values =3D optionsParseRawValues(values, catalog, 0); +=09options =3D optionsValuesToBytea(values, catalog); + +=09if (catalog->postprocess_fun) +=09{ +=09=09catalog->postprocess_fun(options, false); +=09} +=09return options; +} diff --git a/src/backend/access/common/reloptions.c b/src/backend/acces= s/common/reloptions.c index ec10762..19816d5 100644 --- a/src/backend/access/common/reloptions.c +++ b/src/backend/access/common/reloptions.c @@ -1,7 +1,10 @@ /*--------------------------------------------------------------------= ----- * * reloptions.c - *=09 Core support for relation options (pg_class.reloptions) + *=09 Support for options relotions (pg_class.reloptions). + *=09 Reloptions for non-Access Metod relations are defined here. + *=09 There is also extractRelOptions function to extract reloptions + *=09 from pg_class table for all kind of relations. * * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Gro= up * Portions Copyright (c) 1994, Regents of the University of Californi= a @@ -17,928 +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 v= alue, - * upper and lower bounds (if applicable); for strings, consider a val= idation - * routine. - * (ii) add a record below (or use add__reloption). - * (iii) add it to the appropriate options struct (perhaps StdRdOption= s) - * (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 b= y - * interpretOidsOption(). - * - * The default choice for any new option should be AccessExclusiveLock= . - * In some cases the lock level can be reduced from there, but the loc= k - * level chosen should always conflict with itself to ensure that mult= iple - * 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 chan= ge it. - * Safety first. Existing choices are documented here, and elsewhere i= n - * backend code where the parameters are used. - * - * In general, anything that affects the results obtained from a SELEC= T must be - * protected by AccessExclusiveLock. - * - * Autovacuum related parameters can be set at ShareUpdateExclusiveLoc= k - * since they are only used by the AV procs and don't change anything - * currently executing. - * - * Fillfactor can be set because it applies only to subsequent changes= made to - * data blocks, as documented in heapio.c - * - * n_distinct options can be set at ShareUpdateExclusiveLock because t= hey - * 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 stronge= r lock. - * - * Planner-related parameters can be set with ShareUpdateExclusiveLock= because - * they only affect planning and not the correctness of the execution.= Plans - * cannot be changed in mid-flight, so changes here could not easily r= esult in - * new improved plans in any case. So we allow existing queries to con= tinue - * and existing plans to survive, a small price to pay for allowing be= tter - * 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 d= oesn't - * affect existing plans or queries. - */ - -static relopt_bool boolRelOpts[] =3D -{ -=09{ -=09=09{ -=09=09=09"autosummarize", -=09=09=09"Enables automatic summarization on this BRIN index", -=09=09=09RELOPT_KIND_BRIN, -=09=09=09AccessExclusiveLock -=09=09}, -=09=09false -=09}, -=09{ -=09=09{ -=09=09=09"autovacuum_enabled", -=09=09=09"Enables autovacuum in this relation", -=09=09=09RELOPT_KIND_HEAP | RELOPT_KIND_TOAST, -=09=09=09ShareUpdateExclusiveLock -=09=09}, -=09=09true -=09}, -=09{ -=09=09{ -=09=09=09"user_catalog_table", -=09=09=09"Declare a table as an additional catalog table, e.g. for the= purpose of logical replication", -=09=09=09RELOPT_KIND_HEAP, -=09=09=09AccessExclusiveLock -=09=09}, -=09=09false -=09}, -=09{ -=09=09{ -=09=09=09"fastupdate", -=09=09=09"Enables \"fast update\" feature for this GIN index", -=09=09=09RELOPT_KIND_GIN, -=09=09=09AccessExclusiveLock -=09=09}, -=09=09true -=09}, -=09{ -=09=09{ -=09=09=09"security_barrier", -=09=09=09"View acts as a row security barrier", -=09=09=09RELOPT_KIND_VIEW, -=09=09=09AccessExclusiveLock -=09=09}, -=09=09false -=09}, -=09/* list terminator */ -=09{{NULL}} -}; - -static relopt_int intRelOpts[] =3D -{ -=09{ -=09=09{ -=09=09=09"fillfactor", -=09=09=09"Packs table pages only to this percentage", -=09=09=09RELOPT_KIND_HEAP, -=09=09=09ShareUpdateExclusiveLock=09/* since it applies only to later -=09=09=09=09=09=09=09=09=09=09 * inserts */ -=09=09}, -=09=09HEAP_DEFAULT_FILLFACTOR, HEAP_MIN_FILLFACTOR, 100 -=09}, -=09{ -=09=09{ -=09=09=09"fillfactor", -=09=09=09"Packs btree index pages only to this percentage", -=09=09=09RELOPT_KIND_BTREE, -=09=09=09ShareUpdateExclusiveLock=09/* since it applies only to later -=09=09=09=09=09=09=09=09=09=09 * inserts */ -=09=09}, -=09=09BTREE_DEFAULT_FILLFACTOR, BTREE_MIN_FILLFACTOR, 100 -=09}, -=09{ -=09=09{ -=09=09=09"fillfactor", -=09=09=09"Packs hash index pages only to this percentage", -=09=09=09RELOPT_KIND_HASH, -=09=09=09ShareUpdateExclusiveLock=09/* since it applies only to later -=09=09=09=09=09=09=09=09=09=09 * inserts */ -=09=09}, -=09=09HASH_DEFAULT_FILLFACTOR, HASH_MIN_FILLFACTOR, 100 -=09}, -=09{ -=09=09{ -=09=09=09"fillfactor", -=09=09=09"Packs gist index pages only to this percentage", -=09=09=09RELOPT_KIND_GIST, -=09=09=09ShareUpdateExclusiveLock=09/* since it applies only to later -=09=09=09=09=09=09=09=09=09=09 * inserts */ -=09=09}, -=09=09GIST_DEFAULT_FILLFACTOR, GIST_MIN_FILLFACTOR, 100 -=09}, -=09{ -=09=09{ -=09=09=09"fillfactor", -=09=09=09"Packs spgist index pages only to this percentage", -=09=09=09RELOPT_KIND_SPGIST, -=09=09=09ShareUpdateExclusiveLock=09/* since it applies only to later -=09=09=09=09=09=09=09=09=09=09 * inserts */ -=09=09}, -=09=09SPGIST_DEFAULT_FILLFACTOR, SPGIST_MIN_FILLFACTOR, 100 -=09}, -=09{ -=09=09{ -=09=09=09"autovacuum_vacuum_threshold", -=09=09=09"Minimum number of tuple updates or deletes prior to vacuum",= -=09=09=09RELOPT_KIND_HEAP | RELOPT_KIND_TOAST, -=09=09=09ShareUpdateExclusiveLock -=09=09}, -=09=09-1, 0, INT_MAX -=09}, -=09{ -=09=09{ -=09=09=09"autovacuum_analyze_threshold", -=09=09=09"Minimum number of tuple inserts, updates or deletes prior to= analyze", -=09=09=09RELOPT_KIND_HEAP, -=09=09=09ShareUpdateExclusiveLock -=09=09}, -=09=09-1, 0, INT_MAX -=09}, -=09{ -=09=09{ -=09=09=09"autovacuum_vacuum_cost_delay", -=09=09=09"Vacuum cost delay in milliseconds, for autovacuum", -=09=09=09RELOPT_KIND_HEAP | RELOPT_KIND_TOAST, -=09=09=09ShareUpdateExclusiveLock -=09=09}, -=09=09-1, 0, 100 -=09}, -=09{ -=09=09{ -=09=09=09"autovacuum_vacuum_cost_limit", -=09=09=09"Vacuum cost amount available before napping, for autovacuum"= , -=09=09=09RELOPT_KIND_HEAP | RELOPT_KIND_TOAST, -=09=09=09ShareUpdateExclusiveLock -=09=09}, -=09=09-1, 1, 10000 -=09}, -=09{ -=09=09{ -=09=09=09"autovacuum_freeze_min_age", -=09=09=09"Minimum age at which VACUUM should freeze a table row, for a= utovacuum", -=09=09=09RELOPT_KIND_HEAP | RELOPT_KIND_TOAST, -=09=09=09ShareUpdateExclusiveLock -=09=09}, -=09=09-1, 0, 1000000000 -=09}, -=09{ -=09=09{ -=09=09=09"autovacuum_multixact_freeze_min_age", -=09=09=09"Minimum multixact age at which VACUUM should freeze a row mu= ltixact's, for autovacuum", -=09=09=09RELOPT_KIND_HEAP | RELOPT_KIND_TOAST, -=09=09=09ShareUpdateExclusiveLock -=09=09}, -=09=09-1, 0, 1000000000 -=09}, -=09{ -=09=09{ -=09=09=09"autovacuum_freeze_max_age", -=09=09=09"Age at which to autovacuum a table to prevent transaction ID= wraparound", -=09=09=09RELOPT_KIND_HEAP | RELOPT_KIND_TOAST, -=09=09=09ShareUpdateExclusiveLock -=09=09}, -=09=09-1, 100000, 2000000000 -=09}, -=09{ -=09=09{ -=09=09=09"autovacuum_multixact_freeze_max_age", -=09=09=09"Multixact age at which to autovacuum a table to prevent mult= ixact wraparound", -=09=09=09RELOPT_KIND_HEAP | RELOPT_KIND_TOAST, -=09=09=09ShareUpdateExclusiveLock -=09=09}, -=09=09-1, 10000, 2000000000 -=09}, -=09{ -=09=09{ -=09=09=09"autovacuum_freeze_table_age", -=09=09=09"Age at which VACUUM should perform a full table sweep to fre= eze row versions", -=09=09=09RELOPT_KIND_HEAP | RELOPT_KIND_TOAST, -=09=09=09ShareUpdateExclusiveLock -=09=09}, -1, 0, 2000000000 -=09}, -=09{ -=09=09{ -=09=09=09"autovacuum_multixact_freeze_table_age", -=09=09=09"Age of multixact at which VACUUM should perform a full table= sweep to freeze row versions", -=09=09=09RELOPT_KIND_HEAP | RELOPT_KIND_TOAST, -=09=09=09ShareUpdateExclusiveLock -=09=09}, -1, 0, 2000000000 -=09}, -=09{ -=09=09{ -=09=09=09"log_autovacuum_min_duration", -=09=09=09"Sets the minimum execution time above which autovacuum actio= ns will be logged", -=09=09=09RELOPT_KIND_HEAP | RELOPT_KIND_TOAST, -=09=09=09ShareUpdateExclusiveLock -=09=09}, -=09=09-1, -1, INT_MAX -=09}, -=09{ -=09=09{ -=09=09=09"pages_per_range", -=09=09=09"Number of pages that each page range covers in a BRIN index"= , -=09=09=09RELOPT_KIND_BRIN, -=09=09=09AccessExclusiveLock -=09=09}, 128, 1, 131072 -=09}, -=09{ -=09=09{ -=09=09=09"gin_pending_list_limit", -=09=09=09"Maximum size of the pending list for this GIN index, in kilo= bytes.", -=09=09=09RELOPT_KIND_GIN, -=09=09=09AccessExclusiveLock -=09=09}, -=09=09-1, 64, MAX_KILOBYTES -=09}, -=09{ -=09=09{ -=09=09=09"effective_io_concurrency", -=09=09=09"Number of simultaneous requests that can be handled efficien= tly by the disk subsystem.", -=09=09=09RELOPT_KIND_TABLESPACE, -=09=09=09ShareUpdateExclusiveLock -=09=09}, -#ifdef USE_PREFETCH -=09=09-1, 0, MAX_IO_CONCURRENCY -#else -=09=090, 0, 0 -#endif -=09}, -=09{ -=09=09{ -=09=09=09"parallel_workers", -=09=09=09"Number of parallel processes that can be used per executor n= ode for this relation.", -=09=09=09RELOPT_KIND_HEAP, -=09=09=09ShareUpdateExclusiveLock -=09=09}, -=09=09-1, 0, 1024 -=09}, - -=09/* list terminator */ -=09{{NULL}} -}; - -static relopt_real realRelOpts[] =3D -{ -=09{ -=09=09{ -=09=09=09"autovacuum_vacuum_scale_factor", -=09=09=09"Number of tuple updates or deletes prior to vacuum as a frac= tion of reltuples", -=09=09=09RELOPT_KIND_HEAP | RELOPT_KIND_TOAST, -=09=09=09ShareUpdateExclusiveLock -=09=09}, -=09=09-1, 0.0, 100.0 -=09}, -=09{ -=09=09{ -=09=09=09"autovacuum_analyze_scale_factor", -=09=09=09"Number of tuple inserts, updates or deletes prior to analyze= as a fraction of reltuples", -=09=09=09RELOPT_KIND_HEAP, -=09=09=09ShareUpdateExclusiveLock -=09=09}, -=09=09-1, 0.0, 100.0 -=09}, -=09{ -=09=09{ -=09=09=09"seq_page_cost", -=09=09=09"Sets the planner's estimate of the cost of a sequentially fe= tched disk page.", -=09=09=09RELOPT_KIND_TABLESPACE, -=09=09=09ShareUpdateExclusiveLock -=09=09}, -=09=09-1, 0.0, DBL_MAX -=09}, -=09{ -=09=09{ -=09=09=09"random_page_cost", -=09=09=09"Sets the planner's estimate of the cost of a nonsequentially= fetched disk page.", -=09=09=09RELOPT_KIND_TABLESPACE, -=09=09=09ShareUpdateExclusiveLock -=09=09}, -=09=09-1, 0.0, DBL_MAX -=09}, -=09{ -=09=09{ -=09=09=09"n_distinct", -=09=09=09"Sets the planner's estimate of the number of distinct values= appearing in a column (excluding child relations).", -=09=09=09RELOPT_KIND_ATTRIBUTE, -=09=09=09ShareUpdateExclusiveLock -=09=09}, -=09=090, -1.0, DBL_MAX -=09}, -=09{ -=09=09{ -=09=09=09"n_distinct_inherited", -=09=09=09"Sets the planner's estimate of the number of distinct values= appearing in a column (including child relations).", -=09=09=09RELOPT_KIND_ATTRIBUTE, -=09=09=09ShareUpdateExclusiveLock -=09=09}, -=09=090, -1.0, DBL_MAX -=09}, -=09/* list terminator */ -=09{{NULL}} -}; - -static relopt_string stringRelOpts[] =3D -{ -=09{ -=09=09{ -=09=09=09"buffering", -=09=09=09"Enables buffering build for this GiST index", -=09=09=09RELOPT_KIND_GIST, -=09=09=09AccessExclusiveLock -=09=09}, -=09=094, -=09=09false, -=09=09gistValidateBufferingOption, -=09=09"auto" -=09}, -=09{ -=09=09{ -=09=09=09"check_option", -=09=09=09"View has WITH CHECK OPTION defined (local or cascaded).", -=09=09=09RELOPT_KIND_VIEW, -=09=09=09AccessExclusiveLock -=09=09}, -=09=090, -=09=09true, -=09=09validateWithCheckOption, -=09=09NULL -=09}, -=09/* list terminator */ -=09{{NULL}} -}; - -static relopt_gen **relOpts =3D NULL; -static bits32 last_assigned_kind =3D RELOPT_KIND_LAST_DEFAULT; - -static int=09num_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, -=09=09=09=09=09int text_len, bool validate); - -/* - * initialize_reloptions - *=09=09initialization routine, must be called before parsing - * - * Initialize the relOpts array and fill each variable's type and name= length. - */ -static void -initialize_reloptions(void) -{ -=09int=09=09=09i; -=09int=09=09=09j; - -=09j =3D 0; -=09for (i =3D 0; boolRelOpts[i].gen.name; i++) -=09{ -=09=09Assert(DoLockModesConflict(boolRelOpts[i].gen.lockmode, -=09=09=09=09=09=09=09=09 boolRelOpts[i].gen.lockmode)); -=09=09j++; -=09} -=09for (i =3D 0; intRelOpts[i].gen.name; i++) -=09{ -=09=09Assert(DoLockModesConflict(intRelOpts[i].gen.lockmode, -=09=09=09=09=09=09=09=09 intRelOpts[i].gen.lockmode)); -=09=09j++; -=09} -=09for (i =3D 0; realRelOpts[i].gen.name; i++) -=09{ -=09=09Assert(DoLockModesConflict(realRelOpts[i].gen.lockmode, -=09=09=09=09=09=09=09=09 realRelOpts[i].gen.lockmode)); -=09=09j++; -=09} -=09for (i =3D 0; stringRelOpts[i].gen.name; i++) -=09{ -=09=09Assert(DoLockModesConflict(stringRelOpts[i].gen.lockmode, -=09=09=09=09=09=09=09=09 stringRelOpts[i].gen.lockmode)); -=09=09j++; -=09} -=09j +=3D num_custom_options; - -=09if (relOpts) -=09=09pfree(relOpts); -=09relOpts =3D MemoryContextAlloc(TopMemoryContext, -=09=09=09=09=09=09=09=09 (j + 1) * sizeof(relopt_gen *)); - -=09j =3D 0; -=09for (i =3D 0; boolRelOpts[i].gen.name; i++) -=09{ -=09=09relOpts[j] =3D &boolRelOpts[i].gen; -=09=09relOpts[j]->type =3D RELOPT_TYPE_BOOL; -=09=09relOpts[j]->namelen =3D strlen(relOpts[j]->name); -=09=09j++; -=09} - -=09for (i =3D 0; intRelOpts[i].gen.name; i++) -=09{ -=09=09relOpts[j] =3D &intRelOpts[i].gen; -=09=09relOpts[j]->type =3D RELOPT_TYPE_INT; -=09=09relOpts[j]->namelen =3D strlen(relOpts[j]->name); -=09=09j++; -=09} - -=09for (i =3D 0; realRelOpts[i].gen.name; i++) -=09{ -=09=09relOpts[j] =3D &realRelOpts[i].gen; -=09=09relOpts[j]->type =3D RELOPT_TYPE_REAL; -=09=09relOpts[j]->namelen =3D strlen(relOpts[j]->name); -=09=09j++; -=09} - -=09for (i =3D 0; stringRelOpts[i].gen.name; i++) -=09{ -=09=09relOpts[j] =3D &stringRelOpts[i].gen; -=09=09relOpts[j]->type =3D RELOPT_TYPE_STRING; -=09=09relOpts[j]->namelen =3D strlen(relOpts[j]->name); -=09=09j++; -=09} - -=09for (i =3D 0; i < num_custom_options; i++) -=09{ -=09=09relOpts[j] =3D custom_options[i]; -=09=09j++; -=09} - -=09/* add a list terminator */ -=09relOpts[j] =3D NULL; - -=09/* flag the work is complete */ -=09need_initialization =3D false; -} - -/* - * add_reloption_kind - *=09=09Create a new relopt_kind value, to be used in custom reloption= s by - *=09=09user-defined AMs. - */ -relopt_kind -add_reloption_kind(void) -{ -=09/* don't hand out the last bit so that the enum's behavior is porta= ble */ -=09if (last_assigned_kind >=3D RELOPT_KIND_MAX) -=09=09ereport(ERROR, -=09=09=09=09(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), -=09=09=09=09 errmsg("user-defined relation parameter types limit excee= ded"))); -=09last_assigned_kind <<=3D 1; -=09return (relopt_kind) last_assigned_kind; -} - -/* - * add_reloption - *=09=09Add an already-created custom reloption to the list, and recom= pute the - *=09=09main parser table. - */ -static void -add_reloption(relopt_gen *newoption) -{ -=09static int=09max_custom_options =3D 0; - -=09if (num_custom_options >=3D max_custom_options) -=09{ -=09=09MemoryContext oldcxt; - -=09=09oldcxt =3D MemoryContextSwitchTo(TopMemoryContext); - -=09=09if (max_custom_options =3D=3D 0) -=09=09{ -=09=09=09max_custom_options =3D 8; -=09=09=09custom_options =3D palloc(max_custom_options * sizeof(relopt_= gen *)); -=09=09} -=09=09else -=09=09{ -=09=09=09max_custom_options *=3D 2; -=09=09=09custom_options =3D repalloc(custom_options, -=09=09=09=09=09=09=09=09=09 max_custom_options * sizeof(relopt_gen *)= ); -=09=09} -=09=09MemoryContextSwitchTo(oldcxt); -=09} -=09custom_options[num_custom_options++] =3D newoption; - -=09need_initialization =3D true; -} - -/* - * allocate_reloption - *=09=09Allocate a new reloption and initialize the type-agnostic fiel= ds - *=09=09(for types other than string) - */ -static relopt_gen * -allocate_reloption(bits32 kinds, int type, char *name, char *desc) -{ -=09MemoryContext oldcxt; -=09size_t=09=09size; -=09relopt_gen *newoption; - -=09oldcxt =3D MemoryContextSwitchTo(TopMemoryContext); - -=09switch (type) -=09{ -=09=09case RELOPT_TYPE_BOOL: -=09=09=09size =3D sizeof(relopt_bool); -=09=09=09break; -=09=09case RELOPT_TYPE_INT: -=09=09=09size =3D sizeof(relopt_int); -=09=09=09break; -=09=09case RELOPT_TYPE_REAL: -=09=09=09size =3D sizeof(relopt_real); -=09=09=09break; -=09=09case RELOPT_TYPE_STRING: -=09=09=09size =3D sizeof(relopt_string); -=09=09=09break; -=09=09default: -=09=09=09elog(ERROR, "unsupported reloption type %d", type); -=09=09=09return NULL;=09=09/* keep compiler quiet */ -=09} - -=09newoption =3D palloc(size); - -=09newoption->name =3D pstrdup(name); -=09if (desc) -=09=09newoption->desc =3D pstrdup(desc); -=09else -=09=09newoption->desc =3D NULL; -=09newoption->kinds =3D kinds; -=09newoption->namelen =3D strlen(name); -=09newoption->type =3D type; - -=09MemoryContextSwitchTo(oldcxt); - -=09return newoption; -} - -/* - * add_bool_reloption - *=09=09Add a new boolean reloption - */ -void -add_bool_reloption(bits32 kinds, char *name, char *desc, bool default_= val) -{ -=09relopt_bool *newoption; - -=09newoption =3D (relopt_bool *) allocate_reloption(kinds, RELOPT_TYPE= _BOOL, -=09=09=09=09=09=09=09=09=09=09=09=09 name, desc); -=09newoption->default_val =3D default_val; - -=09add_reloption((relopt_gen *) newoption); -} - -/* - * add_int_reloption - *=09=09Add a new integer reloption - */ -void -add_int_reloption(bits32 kinds, char *name, char *desc, int default_va= l, -=09=09=09=09 int min_val, int max_val) -{ -=09relopt_int *newoption; - -=09newoption =3D (relopt_int *) allocate_reloption(kinds, RELOPT_TYPE_= INT, -=09=09=09=09=09=09=09=09=09=09=09=09 name, desc); -=09newoption->default_val =3D default_val; -=09newoption->min =3D min_val; -=09newoption->max =3D max_val; - -=09add_reloption((relopt_gen *) newoption); -} - -/* - * add_real_reloption - *=09=09Add a new float reloption - */ -void -add_real_reloption(bits32 kinds, char *name, char *desc, double defaul= t_val, -=09=09=09=09 double min_val, double max_val) -{ -=09relopt_real *newoption; - -=09newoption =3D (relopt_real *) allocate_reloption(kinds, RELOPT_TYPE= _REAL, -=09=09=09=09=09=09=09=09=09=09=09=09 name, desc); -=09newoption->default_val =3D default_val; -=09newoption->min =3D min_val; -=09newoption->max =3D max_val; - -=09add_reloption((relopt_gen *) newoption); -} - -/* - * add_string_reloption - *=09=09Add a new string reloption - * - * "validator" is an optional function pointer that can be used to tes= t the - * validity of the values. It must elog(ERROR) when the argument stri= ng 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 *defau= lt_val, -=09=09=09=09=09 validate_string_relopt validator) -{ -=09relopt_string *newoption; - -=09/* make sure the validator/default combination is sane */ -=09if (validator) -=09=09(validator) (default_val); - -=09newoption =3D (relopt_string *) allocate_reloption(kinds, RELOPT_TY= PE_STRING, -=09=09=09=09=09=09=09=09=09=09=09=09=09 name, desc); -=09newoption->validate_cb =3D validator; -=09if (default_val) -=09{ -=09=09newoption->default_val =3D MemoryContextStrdup(TopMemoryContext,= -=09=09=09=09=09=09=09=09=09=09=09=09=09 default_val); -=09=09newoption->default_len =3D strlen(default_val); -=09=09newoption->default_isnull =3D false; -=09} -=09else -=09{ -=09=09newoption->default_val =3D ""; -=09=09newoption->default_len =3D 0; -=09=09newoption->default_isnull =3D true; -=09} - -=09add_reloption((relopt_gen *) newoption); -} - -/* - * Transform a relation options list (list of DefElem) into the text a= rray - * format that is kept in pg_class.reloptions, including only those op= tions - * 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 "oid= s" - * in the list (it will be or has been handled by interpretOidsOption(= )). - * - * Note that this is not responsible for determining whether the optio= ns - * are valid, but it does check that namespaces for all the options gi= ven are - * listed in validnsps. The NULL namespace is always valid and need n= ot 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 "defaul= t"), - * but we declare them as Datums to avoid including array.h in relopti= ons.h. - */ -Datum -transformRelOptions(Datum oldOptions, List *defList, char *namspace, -=09=09=09=09=09char *validnsps[], bool ignoreOids, bool isReset) -{ -=09Datum=09=09result; -=09ArrayBuildState *astate; -=09ListCell *cell; - -=09/* no change if empty list */ -=09if (defList =3D=3D NIL) -=09=09return oldOptions; - -=09/* We build new array using accumArrayResult */ -=09astate =3D NULL; - -=09/* Copy any oldOptions that aren't to be replaced */ -=09if (PointerIsValid(DatumGetPointer(oldOptions))) -=09{ -=09=09ArrayType *array =3D DatumGetArrayTypeP(oldOptions); -=09=09Datum=09 *oldoptions; -=09=09int=09=09=09noldoptions; -=09=09int=09=09=09i; - -=09=09deconstruct_array(array, TEXTOID, -1, false, 'i', -=09=09=09=09=09=09 &oldoptions, NULL, &noldoptions); - -=09=09for (i =3D 0; i < noldoptions; i++) -=09=09{ -=09=09=09char=09 *text_str =3D VARDATA(oldoptions[i]); -=09=09=09int=09=09=09text_len =3D VARSIZE(oldoptions[i]) - VARHDRSZ; - -=09=09=09/* Search for a match in defList */ -=09=09=09foreach(cell, defList) -=09=09=09{ -=09=09=09=09DefElem *def =3D (DefElem *) lfirst(cell); -=09=09=09=09int=09=09=09kw_len; - -=09=09=09=09/* ignore if not in the same namespace */ -=09=09=09=09if (namspace =3D=3D NULL) -=09=09=09=09{ -=09=09=09=09=09if (def->defnamespace !=3D NULL) -=09=09=09=09=09=09continue; -=09=09=09=09} -=09=09=09=09else if (def->defnamespace =3D=3D NULL) -=09=09=09=09=09continue; -=09=09=09=09else if (pg_strcasecmp(def->defnamespace, namspace) !=3D 0= ) -=09=09=09=09=09continue; - -=09=09=09=09kw_len =3D strlen(def->defname); -=09=09=09=09if (text_len > kw_len && text_str[kw_len] =3D=3D '=3D' && -=09=09=09=09=09pg_strncasecmp(text_str, def->defname, kw_len) =3D=3D 0= ) -=09=09=09=09=09break; -=09=09=09} -=09=09=09if (!cell) -=09=09=09{ -=09=09=09=09/* No match, so keep old option */ -=09=09=09=09astate =3D accumArrayResult(astate, oldoptions[i], -=09=09=09=09=09=09=09=09=09=09 false, TEXTOID, -=09=09=09=09=09=09=09=09=09=09 CurrentMemoryContext); -=09=09=09} -=09=09} -=09} - -=09/* -=09 * If CREATE/SET, add new options to array; if RESET, just check th= at the -=09 * user didn't say RESET (option=3Dval). (Must do this because the= grammar -=09 * doesn't enforce it.) -=09 */ -=09foreach(cell, defList) -=09{ -=09=09DefElem *def =3D (DefElem *) lfirst(cell); - -=09=09if (isReset) -=09=09{ -=09=09=09if (def->arg !=3D NULL) -=09=09=09=09ereport(ERROR, -=09=09=09=09=09=09(errcode(ERRCODE_SYNTAX_ERROR), -=09=09=09=09=09=09 errmsg("RESET must not include values for parameter= s"))); -=09=09} -=09=09else -=09=09{ -=09=09=09text=09 *t; -=09=09=09const char *value; -=09=09=09Size=09=09len; - -=09=09=09/* -=09=09=09 * Error out if the namespace is not valid. A NULL namespace= is -=09=09=09 * always valid. -=09=09=09 */ -=09=09=09if (def->defnamespace !=3D NULL) -=09=09=09{ -=09=09=09=09bool=09=09valid =3D false; -=09=09=09=09int=09=09=09i; - -=09=09=09=09if (validnsps) -=09=09=09=09{ -=09=09=09=09=09for (i =3D 0; validnsps[i]; i++) -=09=09=09=09=09{ -=09=09=09=09=09=09if (pg_strcasecmp(def->defnamespace, -=09=09=09=09=09=09=09=09=09=09 validnsps[i]) =3D=3D 0) -=09=09=09=09=09=09{ -=09=09=09=09=09=09=09valid =3D true; -=09=09=09=09=09=09=09break; -=09=09=09=09=09=09} -=09=09=09=09=09} -=09=09=09=09} - -=09=09=09=09if (!valid) -=09=09=09=09=09ereport(ERROR, -=09=09=09=09=09=09=09(errcode(ERRCODE_INVALID_PARAMETER_VALUE), -=09=09=09=09=09=09=09 errmsg("unrecognized parameter namespace \"%s\""= , -=09=09=09=09=09=09=09=09=09def->defnamespace))); -=09=09=09} - -=09=09=09if (ignoreOids && pg_strcasecmp(def->defname, "oids") =3D=3D = 0) -=09=09=09=09continue; - -=09=09=09/* ignore if not in the same namespace */ -=09=09=09if (namspace =3D=3D NULL) -=09=09=09{ -=09=09=09=09if (def->defnamespace !=3D NULL) -=09=09=09=09=09continue; -=09=09=09} -=09=09=09else if (def->defnamespace =3D=3D NULL) -=09=09=09=09continue; -=09=09=09else if (pg_strcasecmp(def->defnamespace, namspace) !=3D 0) -=09=09=09=09continue; - -=09=09=09/* -=09=09=09 * Flatten the DefElem into a text string like "name=3Darg". = If we -=09=09=09 * have just "name", assume "name=3Dtrue" is meant. Note: th= e -=09=09=09 * namespace is not output. -=09=09=09 */ -=09=09=09if (def->arg !=3D NULL) -=09=09=09=09value =3D defGetString(def); -=09=09=09else -=09=09=09=09value =3D "true"; -=09=09=09len =3D VARHDRSZ + strlen(def->defname) + 1 + strlen(value); -=09=09=09/* +1 leaves room for sprintf's trailing null */ -=09=09=09t =3D (text *) palloc(len + 1); -=09=09=09SET_VARSIZE(t, len); -=09=09=09sprintf(VARDATA(t), "%s=3D%s", def->defname, value); - -=09=09=09astate =3D accumArrayResult(astate, PointerGetDatum(t), -=09=09=09=09=09=09=09=09=09 false, TEXTOID, -=09=09=09=09=09=09=09=09=09 CurrentMemoryContext); -=09=09} -=09} - -=09if (astate) -=09=09result =3D makeArrayResult(astate, CurrentMemoryContext); -=09else -=09=09result =3D (Datum) 0; - -=09return result; -} - - -/* - * Convert the text-array format of reloptions into a List of DefElem.= - * This is the inverse of transformRelOptions(). - */ -List * -untransformRelOptions(Datum options) -{ -=09List=09 *result =3D NIL; -=09ArrayType *array; -=09Datum=09 *optiondatums; -=09int=09=09=09noptions; -=09int=09=09=09i; - -=09/* Nothing to do if no options */ -=09if (!PointerIsValid(DatumGetPointer(options))) -=09=09return result; - -=09array =3D DatumGetArrayTypeP(options); - -=09deconstruct_array(array, TEXTOID, -1, false, 'i', -=09=09=09=09=09 &optiondatums, NULL, &noptions); - -=09for (i =3D 0; i < noptions; i++) -=09{ -=09=09char=09 *s; -=09=09char=09 *p; -=09=09Node=09 *val =3D NULL; - -=09=09s =3D TextDatumGetCString(optiondatums[i]); -=09=09p =3D strchr(s, '=3D'); -=09=09if (p) -=09=09{ -=09=09=09*p++ =3D '\0'; -=09=09=09val =3D (Node *) makeString(pstrdup(p)); -=09=09} -=09=09result =3D lappend(result, makeDefElem(pstrdup(s), val, -1)); -=09} - -=09return result; -} +void add_autovacuum_options(options_catalog * catalog, int base_offset= , +=09=09=09=09=09 bool for_toast); =20 /* * Extract and parse reloptions from a pg_class tuple. @@ -954,11 +45,12 @@ untransformRelOptions(Datum options) */ bytea * extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, -=09=09=09=09 amoptions_function amoptions) +=09=09=09=09 amrelopt_catalog_function catalog_fun) { -=09bytea=09 *options; +=09bytea=09 *options =3D NULL; =09bool=09=09isnull; =09Datum=09=09datum; +=09options_catalog *catalog =3D NULL; =09Form_pg_class classForm; =20 =09datum =3D fastgetattr(tuple, @@ -974,548 +66,321 @@ extractRelOptions(HeapTuple tuple, TupleDesc tup= desc, =09switch (classForm->relkind) =09{ =09=09case RELKIND_RELATION: -=09=09case RELKIND_TOASTVALUE: =09=09case RELKIND_MATVIEW: -=09=09case RELKIND_PARTITIONED_TABLE: -=09=09=09options =3D heap_reloptions(classForm->relkind, datum, false)= ; +=09=09=09catalog =3D get_heap_relopt_catalog(); +=09=09=09break; +=09=09case RELKIND_TOASTVALUE: +=09=09=09catalog =3D get_toast_relopt_catalog(); =09=09=09break; =09=09case RELKIND_VIEW: -=09=09=09options =3D view_reloptions(datum, false); +=09=09=09catalog =3D get_view_relopt_catalog(); =09=09=09break; =09=09case RELKIND_INDEX: -=09=09=09options =3D index_reloptions(amoptions, datum, false); +=09=09=09if (catalog_fun) +=09=09=09=09catalog =3D catalog_fun(); =09=09=09break; =09=09case RELKIND_FOREIGN_TABLE: =09=09=09options =3D NULL; =09=09=09break; +=09=09case RELKIND_PARTITIONED_TABLE: +=09=09=09catalog =3D NULL; /* No options for parted table for now */ +=09=09=09break; =09=09default: =09=09=09Assert(false);=09=09/* can't get here */ =09=09=09options =3D NULL;=09=09/* keep compiler quiet */ =09=09=09break; =09} =20 +=09if (catalog) +=09=09options =3D optionsTextArrayToBytea(catalog, datum); =09return options; } =20 -/* - * Interpret reloptions that are given in text-array format. - * - * options is a reloption text array as constructed by transformRelOpt= ions. - * kind specifies the family of options to be processed. - * - * The return value is a relopt_value * array on which the options act= ually - * set in the options array are marked with isset=3Dtrue. The length = of this - * array is returned in *numrelopts. Options not set are also present= in the - * array; this is so that the caller can easily locate the default val= ues. - * - * If there are no options of the given kind, numrelopts is set to 0 a= nd NULL - * is returned (unless options are illegally supplied despite none bei= ng - * defined, in which case an error occurs). - * - * Note: values of type int, bool and real are allocated as part of th= e - * returned array. Values of type string are allocated separately and= must - * be freed by the caller. - */ -relopt_value * -parseRelOptions(Datum options, bool validate, relopt_kind kind, -=09=09=09=09int *numrelopts) -{ -=09relopt_value *reloptions =3D NULL; -=09int=09=09=09numoptions =3D 0; -=09int=09=09=09i; -=09int=09=09=09j; - -=09if (need_initialization) -=09=09initialize_reloptions(); - -=09/* Build a list of expected options, based on kind */ - -=09for (i =3D 0; relOpts[i]; i++) -=09=09if (relOpts[i]->kinds & kind) -=09=09=09numoptions++; - -=09if (numoptions > 0) -=09{ -=09=09reloptions =3D palloc(numoptions * sizeof(relopt_value)); - -=09=09for (i =3D 0, j =3D 0; relOpts[i]; i++) -=09=09{ -=09=09=09if (relOpts[i]->kinds & kind) -=09=09=09{ -=09=09=09=09reloptions[j].gen =3D relOpts[i]; -=09=09=09=09reloptions[j].isset =3D false; -=09=09=09=09j++; -=09=09=09} -=09=09} -=09} - -=09/* Done if no options */ -=09if (PointerIsValid(DatumGetPointer(options))) -=09{ -=09=09ArrayType *array =3D DatumGetArrayTypeP(options); -=09=09Datum=09 *optiondatums; -=09=09int=09=09=09noptions; - -=09=09deconstruct_array(array, TEXTOID, -1, false, 'i', -=09=09=09=09=09=09 &optiondatums, NULL, &noptions); - -=09=09for (i =3D 0; i < noptions; i++) -=09=09{ -=09=09=09char=09 *text_str =3D VARDATA(optiondatums[i]); -=09=09=09int=09=09=09text_len =3D VARSIZE(optiondatums[i]) - VARHDRSZ;= -=09=09=09int=09=09=09j; - -=09=09=09/* Search for a match in reloptions */ -=09=09=09for (j =3D 0; j < numoptions; j++) -=09=09=09{ -=09=09=09=09int=09=09=09kw_len =3D reloptions[j].gen->namelen; - -=09=09=09=09if (text_len > kw_len && text_str[kw_len] =3D=3D '=3D' && -=09=09=09=09=09pg_strncasecmp(text_str, reloptions[j].gen->name, -=09=09=09=09=09=09=09=09 kw_len) =3D=3D 0) -=09=09=09=09{ -=09=09=09=09=09parse_one_reloption(&reloptions[j], text_str, text_len,= -=09=09=09=09=09=09=09=09=09=09validate); -=09=09=09=09=09break; -=09=09=09=09} -=09=09=09} - -=09=09=09if (j >=3D numoptions && validate) -=09=09=09{ -=09=09=09=09char=09 *s; -=09=09=09=09char=09 *p; - -=09=09=09=09s =3D TextDatumGetCString(optiondatums[i]); -=09=09=09=09p =3D strchr(s, '=3D'); -=09=09=09=09if (p) -=09=09=09=09=09*p =3D '\0'; -=09=09=09=09ereport(ERROR, -=09=09=09=09=09=09(errcode(ERRCODE_INVALID_PARAMETER_VALUE), -=09=09=09=09=09=09 errmsg("unrecognized parameter \"%s\"", s))); -=09=09=09} -=09=09} - -=09=09/* It's worth avoiding memory leaks in this function */ -=09=09pfree(optiondatums); -=09=09if (((void *) array) !=3D DatumGetPointer(options)) -=09=09=09pfree(array); -=09} - -=09*numrelopts =3D numoptions; -=09return reloptions; -} =20 /* - * Subroutine for parseRelOptions, to parse and validate a single opti= on's - * value + * Here goes functions that build catalogs for non-index relations. Ca= talogs + * for index relations are available from Access Methods, all other + * relation catalogs are here */ -static void -parse_one_reloption(relopt_value *option, char *text_str, int text_len= , -=09=09=09=09=09bool validate) -{ -=09char=09 *value; -=09int=09=09=09value_len; -=09bool=09=09parsed; -=09bool=09=09nofree =3D false; - -=09if (option->isset && validate) -=09=09ereport(ERROR, -=09=09=09=09(errcode(ERRCODE_INVALID_PARAMETER_VALUE), -=09=09=09=09 errmsg("parameter \"%s\" specified more than once", -=09=09=09=09=09=09option->gen->name))); - -=09value_len =3D text_len - option->gen->namelen - 1; -=09value =3D (char *) palloc(value_len + 1); -=09memcpy(value, text_str + option->gen->namelen + 1, value_len); -=09value[value_len] =3D '\0'; - -=09switch (option->gen->type) -=09{ -=09=09case RELOPT_TYPE_BOOL: -=09=09=09{ -=09=09=09=09parsed =3D parse_bool(value, &option->values.bool_val); -=09=09=09=09if (validate && !parsed) -=09=09=09=09=09ereport(ERROR, -=09=09=09=09=09=09=09(errcode(ERRCODE_INVALID_PARAMETER_VALUE), -=09=09=09=09=09=09=09 errmsg("invalid value for boolean option \"%s\":= %s", -=09=09=09=09=09=09=09=09=09option->gen->name, value))); -=09=09=09} -=09=09=09break; -=09=09case RELOPT_TYPE_INT: -=09=09=09{ -=09=09=09=09relopt_int *optint =3D (relopt_int *) option->gen; - -=09=09=09=09parsed =3D parse_int(value, &option->values.int_val, 0, NU= LL); -=09=09=09=09if (validate && !parsed) -=09=09=09=09=09ereport(ERROR, -=09=09=09=09=09=09=09(errcode(ERRCODE_INVALID_PARAMETER_VALUE), -=09=09=09=09=09=09=09 errmsg("invalid value for integer option \"%s\":= %s", -=09=09=09=09=09=09=09=09=09option->gen->name, value))); -=09=09=09=09if (validate && (option->values.int_val < optint->min || -=09=09=09=09=09=09=09=09 option->values.int_val > optint->max)) -=09=09=09=09=09ereport(ERROR, -=09=09=09=09=09=09=09(errcode(ERRCODE_INVALID_PARAMETER_VALUE), -=09=09=09=09=09=09=09 errmsg("value %s out of bounds for option \"%s\"= ", -=09=09=09=09=09=09=09=09=09value, option->gen->name), -=09=09=09=09=09=09=09 errdetail("Valid values are between \"%d\" and \= "%d\".", -=09=09=09=09=09=09=09=09=09 optint->min, optint->max))); -=09=09=09} -=09=09=09break; -=09=09case RELOPT_TYPE_REAL: -=09=09=09{ -=09=09=09=09relopt_real *optreal =3D (relopt_real *) option->gen; - -=09=09=09=09parsed =3D parse_real(value, &option->values.real_val); -=09=09=09=09if (validate && !parsed) -=09=09=09=09=09ereport(ERROR, -=09=09=09=09=09=09=09(errcode(ERRCODE_INVALID_PARAMETER_VALUE), -=09=09=09=09=09=09=09 errmsg("invalid value for floating point option = \"%s\": %s", -=09=09=09=09=09=09=09=09=09option->gen->name, value))); -=09=09=09=09if (validate && (option->values.real_val < optreal->min ||= -=09=09=09=09=09=09=09=09 option->values.real_val > optreal->max)) -=09=09=09=09=09ereport(ERROR, -=09=09=09=09=09=09=09(errcode(ERRCODE_INVALID_PARAMETER_VALUE), -=09=09=09=09=09=09=09 errmsg("value %s out of bounds for option \"%s\"= ", -=09=09=09=09=09=09=09=09=09value, option->gen->name), -=09=09=09=09=09=09=09 errdetail("Valid values are between \"%f\" and \= "%f\".", -=09=09=09=09=09=09=09=09=09 optreal->min, optreal->max))); -=09=09=09} -=09=09=09break; -=09=09case RELOPT_TYPE_STRING: -=09=09=09{ -=09=09=09=09relopt_string *optstring =3D (relopt_string *) option->gen= ; - -=09=09=09=09option->values.string_val =3D value; -=09=09=09=09nofree =3D true; -=09=09=09=09if (validate && optstring->validate_cb) -=09=09=09=09=09(optstring->validate_cb) (value); -=09=09=09=09parsed =3D true; -=09=09=09} -=09=09=09break; -=09=09default: -=09=09=09elog(ERROR, "unsupported reloption type %d", option->gen->typ= e); -=09=09=09parsed =3D true;=09=09/* quiet compiler */ -=09=09=09break; -=09} - -=09if (parsed) -=09=09option->isset =3D true; -=09if (!nofree) -=09=09pfree(value); -} =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 v= ariables. + * Both heap and toast relation have almost the same autovacuum set of= options. + * These autovacuum options are stored in the same structure (AutoVacO= pts) + * that is a part of both HeapOptions and ToastOptions. * - * "base" should be sizeof(struct) of the reloptions struct (StdRdOpti= ons or - * equivalent). - */ -void * -allocateReloptStruct(Size base, relopt_value *options, int numoptions)= -{ -=09Size=09=09size =3D base; -=09int=09=09=09i; - -=09for (i =3D 0; i < numoptions; i++) -=09=09if (options[i].gen->type =3D=3D RELOPT_TYPE_STRING) -=09=09=09size +=3D GET_STRING_RELOPTION_LEN(options[i]) + 1; - -=09return palloc0(size); -} - -/* - * Given the result of parseRelOptions and a parsing table, fill in th= e - * struct (previously allocated with allocateReloptStruct) with the pa= rsed - * values. + * Function add_autovacuum_options adds to the "catalog" catalog autov= acuum + * options definition. In binary mode this options are saved into Auto= VacOpts + * structure, which is located in result bytea chunk with base_offset = offset. * - * rdopts is the pointer to the allocated struct to be filled. - * basesize is the sizeof(struct) that was passed to allocateReloptStr= uct. - * options, of length numoptions, is parseRelOptions' output. - * elems, of length numelems, is the table describing the allowed opti= ons. - * When validate is true, it is expected that all options appear in el= ems. + * Heap has two options (autovacuum_analyze_threshold and + * autovacuum_analyze_scale_factor) that is not used for toast. So for= toast + * case this options are added to catalog with OPTION_DEFINITION_FLAG_= REJECT + * flag set, so postgres will reject this toast options from the CREAT= E or + * ALTER command, but still will save default value in binary represen= tation */ + void -fillRelOptions(void *rdopts, Size basesize, -=09=09=09 relopt_value *options, int numoptions, -=09=09=09 bool validate, -=09=09=09 const relopt_parse_elt *elems, int numelems) +add_autovacuum_options(options_catalog * catalog, int base_offset, +=09=09=09=09=09 bool for_toast) { -=09int=09=09=09i; -=09int=09=09=09offset =3D basesize; - -=09for (i =3D 0; i < numoptions; i++) -=09{ -=09=09int=09=09=09j; -=09=09bool=09=09found =3D false; - -=09=09for (j =3D 0; j < numelems; j++) -=09=09{ -=09=09=09if (pg_strcasecmp(options[i].gen->name, elems[j].optname) =3D= =3D 0) -=09=09=09{ -=09=09=09=09relopt_string *optstring; -=09=09=09=09char=09 *itempos =3D ((char *) rdopts) + elems[j].offset= ; -=09=09=09=09char=09 *string_val; - -=09=09=09=09switch (options[i].gen->type) -=09=09=09=09{ -=09=09=09=09=09case RELOPT_TYPE_BOOL: -=09=09=09=09=09=09*(bool *) itempos =3D options[i].isset ? -=09=09=09=09=09=09=09options[i].values.bool_val : -=09=09=09=09=09=09=09((relopt_bool *) options[i].gen)->default_val; -=09=09=09=09=09=09break; -=09=09=09=09=09case RELOPT_TYPE_INT: -=09=09=09=09=09=09*(int *) itempos =3D options[i].isset ? -=09=09=09=09=09=09=09options[i].values.int_val : -=09=09=09=09=09=09=09((relopt_int *) options[i].gen)->default_val; -=09=09=09=09=09=09break; -=09=09=09=09=09case RELOPT_TYPE_REAL: -=09=09=09=09=09=09*(double *) itempos =3D options[i].isset ? -=09=09=09=09=09=09=09options[i].values.real_val : -=09=09=09=09=09=09=09((relopt_real *) options[i].gen)->default_val; -=09=09=09=09=09=09break; -=09=09=09=09=09case RELOPT_TYPE_STRING: -=09=09=09=09=09=09optstring =3D (relopt_string *) options[i].gen; -=09=09=09=09=09=09if (options[i].isset) -=09=09=09=09=09=09=09string_val =3D options[i].values.string_val; -=09=09=09=09=09=09else if (!optstring->default_isnull) -=09=09=09=09=09=09=09string_val =3D optstring->default_val; -=09=09=09=09=09=09else -=09=09=09=09=09=09=09string_val =3D NULL; - -=09=09=09=09=09=09if (string_val =3D=3D NULL) -=09=09=09=09=09=09=09*(int *) itempos =3D 0; -=09=09=09=09=09=09else -=09=09=09=09=09=09{ -=09=09=09=09=09=09=09strcpy((char *) rdopts + offset, string_val); -=09=09=09=09=09=09=09*(int *) itempos =3D offset; -=09=09=09=09=09=09=09offset +=3D strlen(string_val) + 1; -=09=09=09=09=09=09} -=09=09=09=09=09=09break; -=09=09=09=09=09default: -=09=09=09=09=09=09elog(ERROR, "unsupported reloption type %d", -=09=09=09=09=09=09=09 options[i].gen->type); -=09=09=09=09=09=09break; -=09=09=09=09} -=09=09=09=09found =3D true; -=09=09=09=09break; -=09=09=09} -=09=09} -=09=09if (validate && !found) -=09=09=09elog(ERROR, "reloption \"%s\" not found in parse table", -=09=09=09=09 options[i].gen->name); -=09} -=09SET_VARSIZE(rdopts, offset); +=09optionsCatalogAddItemBool(catalog, "autovacuum_enabled", +=09=09=09=09=09=09=09 "Enables autovacuum in this relation", +=09=09=09=09=09=09=09 ShareUpdateExclusiveLock, +=09=09=09=09=09 0, base_offset + offsetof(AutoVacOpts, enabled), true= ); + +=09optionsCatalogAddItemInt(catalog, "autovacuum_vacuum_threshold", +=09=09=09=09"Minimum number of tuple updates or deletes prior to vacuu= m", +=09=09=09=09=09=09=09 ShareUpdateExclusiveLock, +=09=09=09=09=090, base_offset + offsetof(AutoVacOpts, vacuum_threshold= ), +=09=09=09=09=09=09=09 -1, 0, INT_MAX); + +=09optionsCatalogAddItemInt(catalog, "autovacuum_analyze_threshold", +=09=09=09=09"Minimum number of tuple updates or deletes prior to vacuu= m", +=09=09=09=09=09=09=09 ShareUpdateExclusiveLock, +=09=09=09=09=09=09=09 for_toast ? OPTION_DEFINITION_FLAG_REJECT : 0, +=09=09=09=09=09 base_offset + offsetof(AutoVacOpts, analyze_threshold= ), +=09=09=09=09=09=09=09 -1, 0, INT_MAX); + +=09optionsCatalogAddItemInt(catalog, "autovacuum_vacuum_cost_delay", +=09=09=09=09=09=09 "Vacuum cost delay in milliseconds, for autovacuum"= , +=09=09=09=09=09=09=09 ShareUpdateExclusiveLock, +=09=09=09=09 0, base_offset + offsetof(AutoVacOpts, vacuum_cost_dela= y), +=09=09=09=09=09=09=09 -1, 0, 100); + +=09optionsCatalogAddItemInt(catalog, "autovacuum_vacuum_cost_limit", +=09=09=09 "Vacuum cost amount available before napping, for autovacu= um", +=09=09=09=09=09=09=09 ShareUpdateExclusiveLock, +=09=09=09=09 0, base_offset + offsetof(AutoVacOpts, vacuum_cost_limi= t), +=09=09=09=09=09=09=09 -1, 0, 10000); + +=09optionsCatalogAddItemInt(catalog, "autovacuum_freeze_min_age", +=09 "Minimum age at which VACUUM should freeze a table row, for autova= cuum", +=09=09=09=09=09=09=09 ShareUpdateExclusiveLock, +=09=09=09=09=09 0, base_offset + offsetof(AutoVacOpts, freeze_min_age= ), +=09=09=09=09=09=09=09 -1, 0, 1000000000); + +=09optionsCatalogAddItemInt(catalog, "autovacuum_freeze_max_age", +=09"Age at which to autovacuum a table to prevent transaction ID wrapa= round", +=09=09=09=09=09=09=09 ShareUpdateExclusiveLock, +=09=09=09=09=09 0, base_offset + offsetof(AutoVacOpts, freeze_max_age= ), +=09=09=09=09=09=09=09 -1, 100000, 2000000000); + +=09optionsCatalogAddItemInt(catalog, "autovacuum_freeze_table_age", +=09=09=09=09=09=09=09 "Age at which VACUUM should perform a full table= sweep to freeze row versions", +=09=09=09=09=09=09=09 ShareUpdateExclusiveLock, +=09=09=09=09=090, base_offset + offsetof(AutoVacOpts, freeze_table_age= ), +=09=09=09=09=09=09=09 -1, 0, 2000000000); + +=09optionsCatalogAddItemInt(catalog, "autovacuum_multixact_freeze_min_= age", +=09=09=09=09=09=09=09 "Minimum multixact age at which VACUUM should fr= eeze a row multixact's, for autovacuum", +=09=09=09=09=09=09=09 ShareUpdateExclusiveLock, +=09=09=090, base_offset + offsetof(AutoVacOpts, multixact_freeze_min_a= ge), +=09=09=09=09=09=09=09 -1, 0, 1000000000); + +=09optionsCatalogAddItemInt(catalog, "autovacuum_multixact_freeze_max_= age", +=09=09=09=09=09=09=09 "Multixact age at which to autovacuum a table to= prevent multixact wraparound", +=09=09=09=09=09=09=09 ShareUpdateExclusiveLock, +=09=09=090, base_offset + offsetof(AutoVacOpts, multixact_freeze_max_a= ge), +=09=09=09=09=09=09=09 -1, 10000, 2000000000); + +=09optionsCatalogAddItemInt(catalog, "autovacuum_multixact_freeze_tabl= e_age", +=09=09=09=09=09=09=09 "Age of multixact at which VACUUM should perform= a full table sweep to freeze row versions", +=09=09=09=09=09=09=09 ShareUpdateExclusiveLock, +=09=09 0, base_offset + offsetof(AutoVacOpts, multixact_freeze_table_= age), +=09=09=09=09=09=09=09 -1, 0, 2000000000); + +=09optionsCatalogAddItemInt(catalog, "log_autovacuum_min_duration", +=09=09=09=09=09=09=09 "Sets the minimum execution time above which aut= ovacuum actions will be logged", +=09=09=09=09=09=09=09 ShareUpdateExclusiveLock, +=09=09=09=09=090, base_offset + offsetof(AutoVacOpts, log_min_duration= ), +=09=09=09=09=09=09=09 -1, -1, INT_MAX); + +=09optionsCatalogAddItemReal(catalog, "autovacuum_vacuum_scale_factor"= , +=09=09=09=09=09=09=09 "Number of tuple updates or deletes prior to va= cuum as a fraction of reltuples", +=09=09=09=09=09=09=09 ShareUpdateExclusiveLock, +=09=09=09=09 0, base_offset + offsetof(AutoVacOpts, vacuum_scale_facto= r), +=09=09=09=09=09=09=09 -1, 0.0, 100.0); + +=09optionsCatalogAddItemReal(catalog, "autovacuum_analyze_scale_factor= ", +=09=09=09=09=09=09=09 "Number of tuple inserts, updates or deletes pr= ior to analyze as a fraction of reltuples", +=09=09=09=09=09=09=09 ShareUpdateExclusiveLock, +=09=09=09=09=09=09=09 for_toast ? OPTION_DEFINITION_FLAG_REJECT : 0, +=09=09=09=09 base_offset + offsetof(AutoVacOpts, analyze_scale_facto= r), +=09=09=09=09=09=09=09 -1, 0.0, 100.0); } =20 - /* - * Option parser for anything that uses StdRdOptions. + * get_heap_relopt_catalog + *=09=09Returns an options catalog for heap relation. */ -bytea * -default_reloptions(Datum reloptions, bool validate, relopt_kind kind) -{ -=09relopt_value *options; -=09StdRdOptions *rdopts; -=09int=09=09=09numoptions; -=09static const relopt_parse_elt tab[] =3D { -=09=09{"fillfactor", RELOPT_TYPE_INT, offsetof(StdRdOptions, fillfacto= r)}, -=09=09{"autovacuum_enabled", RELOPT_TYPE_BOOL, -=09=09offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, enabl= ed)}, -=09=09{"autovacuum_vacuum_threshold", RELOPT_TYPE_INT, -=09=09offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuu= m_threshold)}, -=09=09{"autovacuum_analyze_threshold", RELOPT_TYPE_INT, -=09=09offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, analy= ze_threshold)}, -=09=09{"autovacuum_vacuum_cost_delay", RELOPT_TYPE_INT, -=09=09offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuu= m_cost_delay)}, -=09=09{"autovacuum_vacuum_cost_limit", RELOPT_TYPE_INT, -=09=09offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuu= m_cost_limit)}, -=09=09{"autovacuum_freeze_min_age", RELOPT_TYPE_INT, -=09=09offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freez= e_min_age)}, -=09=09{"autovacuum_freeze_max_age", RELOPT_TYPE_INT, -=09=09offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freez= e_max_age)}, -=09=09{"autovacuum_freeze_table_age", RELOPT_TYPE_INT, -=09=09offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freez= e_table_age)}, -=09=09{"autovacuum_multixact_freeze_min_age", RELOPT_TYPE_INT, -=09=09offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multi= xact_freeze_min_age)}, -=09=09{"autovacuum_multixact_freeze_max_age", RELOPT_TYPE_INT, -=09=09offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multi= xact_freeze_max_age)}, -=09=09{"autovacuum_multixact_freeze_table_age", RELOPT_TYPE_INT, -=09=09offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multi= xact_freeze_table_age)}, -=09=09{"log_autovacuum_min_duration", RELOPT_TYPE_INT, -=09=09offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, log_m= in_duration)}, -=09=09{"autovacuum_vacuum_scale_factor", RELOPT_TYPE_REAL, -=09=09offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuu= m_scale_factor)}, -=09=09{"autovacuum_analyze_scale_factor", RELOPT_TYPE_REAL, -=09=09offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, analy= ze_scale_factor)}, -=09=09{"user_catalog_table", RELOPT_TYPE_BOOL, -=09=09offsetof(StdRdOptions, user_catalog_table)}, -=09=09{"parallel_workers", RELOPT_TYPE_INT, -=09=09offsetof(StdRdOptions, parallel_workers)} -=09}; +static options_catalog *heap_relopt_catalog =3D NULL; =20 -=09options =3D parseRelOptions(reloptions, validate, kind, &numoptions= ); - -=09/* if none set, we're done */ -=09if (numoptions =3D=3D 0) -=09=09return NULL; - -=09rdopts =3D allocateReloptStruct(sizeof(StdRdOptions), options, numo= ptions); - -=09fillRelOptions((void *) rdopts, sizeof(StdRdOptions), options, numo= ptions, -=09=09=09=09 validate, tab, lengthof(tab)); - -=09pfree(options); - -=09return (bytea *) rdopts; +options_catalog * +get_heap_relopt_catalog(void) +{ +=09if (!heap_relopt_catalog) +=09{ +=09=09heap_relopt_catalog =3D allocateOptionsCatalog(NULL, sizeof(Heap= Options), +=09=09=09=09=09=09=09=09=09=09=09=09=09 14 + 4);=09/* 14 - for autovac= uum +=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09 * options, 4 - core +=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09 * heap options */ + +=09=09optionsCatalogAddItemInt(heap_relopt_catalog, "fillfactor", +=09=09=09=09=09=09=09=09 "Packs table pages only to this percentag", +=09=09=09=09=09=09=09=09 ShareUpdateExclusiveLock,=09=09/* since it ap= plies only +=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09 * to later inserts */= +=09=09=09=09=09=09=09=09 0, offsetof(HeapOptions, fillfactor), +=09=09=09=09=09=09 HEAP_DEFAULT_FILLFACTOR, HEAP_MIN_FILLFACTOR, 100)= ; + +=09=09add_autovacuum_options(heap_relopt_catalog, +=09=09=09=09=09=09=09 offsetof(HeapOptions, autovacuum), false); + +=09=09optionsCatalogAddItemBool(heap_relopt_catalog, "user_catalog_tab= le", +=09=09=09=09=09=09=09=09 "Declare a table as an additional catalog ta= ble, e.g. for the purpose of logical replication", +=09=09=09=09=09=09=09=09 AccessExclusiveLock, +=09=09=09=09=09=09=09=090, offsetof(HeapOptions, user_catalog_table), +=09=09=09=09=09=09=09=09 false); + +=09=09optionsCatalogAddItemInt(heap_relopt_catalog, "parallel_workers"= , +=09=09=09=09=09=09=09=09 "Number of parallel processes that can be use= d per executor node for this relation", +=09=09=09=09=09=09=09=09 ShareUpdateExclusiveLock, +=09=09=09=09=09=09=09=09 0, offsetof(HeapOptions, parallel_workers), +=09=09=09=09=09=09=09=09 -1, 0, 1024); + +=09=09/* +=09=09 * A WITH OID / WITHOUT OIDS expressions are converted by syntax= +=09=09 * parser into "oids" relation option, but this option is proces= sed by +=09=09 * interpretOidsOption(), that does not use core options code. T= hat is +=09=09 * why here we should just ignore this option as if it does not = exist. +=09=09 * Do it with OPTION_DEFINITION_FLAG_IGNORE flag +=09=09 */ +=09=09optionsCatalogAddItemBool(heap_relopt_catalog, "oids", +=09=09=09=09 "Option used for WITH OIDS expression. Processed elsewher= e", +=09=09=09=09=09=09=09=09 NoLock, +=09=09=09=09=09=09=09=09 OPTION_DEFINITION_FLAG_IGNORE, -1, +=09=09=09=09=09=09=09=09 false); +=09} +=09return heap_relopt_catalog; } =20 /* - * Option parser for views + * get_toast_relopt_catalog + *=09=09Returns an options catalog for toast relation. */ -bytea * -view_reloptions(Datum reloptions, bool validate) -{ -=09relopt_value *options; -=09ViewOptions *vopts; -=09int=09=09=09numoptions; -=09static const relopt_parse_elt tab[] =3D { -=09=09{"security_barrier", RELOPT_TYPE_BOOL, -=09=09offsetof(ViewOptions, security_barrier)}, -=09=09{"check_option", RELOPT_TYPE_STRING, -=09=09offsetof(ViewOptions, check_option_offset)} -=09}; - -=09options =3D parseRelOptions(reloptions, validate, RELOPT_KIND_VIEW,= &numoptions); - -=09/* if none set, we're done */ -=09if (numoptions =3D=3D 0) -=09=09return NULL; =20 -=09vopts =3D allocateReloptStruct(sizeof(ViewOptions), options, numopt= ions); +static options_catalog *toast_relopt_catalog =3D NULL; =20 -=09fillRelOptions((void *) vopts, sizeof(ViewOptions), options, numopt= ions, -=09=09=09=09 validate, tab, lengthof(tab)); - -=09pfree(options); - -=09return (bytea *) vopts; -} - -/* - * Parse options for heaps, views and toast tables. - */ -bytea * -heap_reloptions(char relkind, Datum reloptions, bool validate) +options_catalog * +get_toast_relopt_catalog(void) { -=09StdRdOptions *rdopts; - -=09switch (relkind) +=09if (!toast_relopt_catalog) =09{ -=09=09case RELKIND_TOASTVALUE: -=09=09=09rdopts =3D (StdRdOptions *) -=09=09=09=09default_reloptions(reloptions, validate, RELOPT_KIND_TOAST= ); -=09=09=09if (rdopts !=3D NULL) -=09=09=09{ -=09=09=09=09/* adjust default-only parameters for TOAST relations */ -=09=09=09=09rdopts->fillfactor =3D 100; -=09=09=09=09rdopts->autovacuum.analyze_threshold =3D -1; -=09=09=09=09rdopts->autovacuum.analyze_scale_factor =3D -1; -=09=09=09} -=09=09=09return (bytea *) rdopts; -=09=09case RELKIND_RELATION: -=09=09case RELKIND_MATVIEW: -=09=09=09return default_reloptions(reloptions, validate, RELOPT_KIND_H= EAP); -=09=09case RELKIND_PARTITIONED_TABLE: -=09=09=09return default_reloptions(reloptions, validate, -=09=09=09=09=09=09=09=09=09 RELOPT_KIND_PARTITIONED); -=09=09default: -=09=09=09/* other relkinds are not supported */ -=09=09=09return NULL; +=09=09toast_relopt_catalog =3D allocateOptionsCatalog("toast", sizeof(= ToastOptions), +=09=09=09=09=09=09=09=09=09=09=09=09=09 14);=09=09/* 14 - for autovac= uum +=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09 * options */ +=09=09add_autovacuum_options(toast_relopt_catalog, +=09=09=09=09=09=09=09 offsetof(ToastOptions, autovacuum), true); =09} +=09return toast_relopt_catalog; } =20 - /* - * Parse options for indexes. - * - *=09amoptions=09index AM's option parser function - *=09reloptions=09options as text[] datum - *=09validate=09error flag + * get_view_relopt_catalog + *=09=09Returns an options catalog for view relation. */ -bytea * -index_reloptions(amoptions_function amoptions, Datum reloptions, bool = validate) +static options_catalog *view_relopt_catalog =3D NULL; + +options_catalog * +get_view_relopt_catalog(void) { -=09Assert(amoptions !=3D NULL); +=09static const char *enum_names[] =3D VIEW_OPTION_CHECK_OPTION_VALUE_= NAMES; =20 -=09/* Assume function is strict */ -=09if (!PointerIsValid(DatumGetPointer(reloptions))) -=09=09return NULL; +=09if (!view_relopt_catalog) +=09{ +=09=09view_relopt_catalog =3D allocateOptionsCatalog(NULL, +=09=09=09=09=09=09=09=09=09=09=09=09=09 sizeof(ViewOptions), 2); =20 -=09return amoptions(reloptions, validate); +=09=09optionsCatalogAddItemBool(view_relopt_catalog, "security_barrier= ", +=09=09=09=09=09=09=09=09 "View acts as a row security barrier", +=09=09=09=09=09=09=09=09 AccessExclusiveLock, +=09=09=09=09=09=09 0, offsetof(ViewOptions, security_barrier), false)= ; + +=09=09optionsCatalogAddItemEnum(view_relopt_catalog, "check_option", +=09=09=09=09=09"View has WITH CHECK OPTION defined (local or cascaded)= ", +=09=09=09=09=09=09=09=09 AccessExclusiveLock, 0, +=09=09=09=09=09=09=09=09 offsetof(ViewOptions, check_option), +=09=09=09=09=09=09=09=09 enum_names, +=09=09=09=09=09=09=09=09 VIEW_OPTION_CHECK_OPTION_NOT_SET); +=09} +=09return view_relopt_catalog; } =20 /* - * Option parser for attribute reloptions + * get_attribute_options_catalog + *=09=09Returns an options catalog for heap attributes */ -bytea * -attribute_reloptions(Datum reloptions, bool validate) -{ -=09relopt_value *options; -=09AttributeOpts *aopts; -=09int=09=09=09numoptions; -=09static const relopt_parse_elt tab[] =3D { -=09=09{"n_distinct", RELOPT_TYPE_REAL, offsetof(AttributeOpts, n_disti= nct)}, -=09=09{"n_distinct_inherited", RELOPT_TYPE_REAL, offsetof(AttributeOpt= s, n_distinct_inherited)} -=09}; - -=09options =3D parseRelOptions(reloptions, validate, RELOPT_KIND_ATTRI= BUTE, -=09=09=09=09=09=09=09 &numoptions); - -=09/* if none set, we're done */ -=09if (numoptions =3D=3D 0) -=09=09return NULL; - -=09aopts =3D allocateReloptStruct(sizeof(AttributeOpts), options, numo= ptions); +static options_catalog *attribute_options_catalog =3D NULL; =20 -=09fillRelOptions((void *) aopts, sizeof(AttributeOpts), options, numo= ptions, -=09=09=09=09 validate, tab, lengthof(tab)); +options_catalog * +get_attribute_options_catalog(void) +{ +=09if (!attribute_options_catalog) +=09{ +=09=09attribute_options_catalog =3D allocateOptionsCatalog(NULL, +=09=09=09=09=09=09=09=09=09=09=09=09 sizeof(AttributeOpts), 2); =20 -=09pfree(options); +=09=09optionsCatalogAddItemReal(attribute_options_catalog, "n_distinct= ", +=09=09=09=09=09=09=09=09 "Sets the planner's estimate of the number o= f distinct values appearing in a column (excluding child relations)", +=09=09=09=09=09=09=09=09 ShareUpdateExclusiveLock, +=09=09=09=09 0, offsetof(AttributeOpts, n_distinct), 0, -1.0, DBL_MA= X); =20 -=09return (bytea *) aopts; +=09=09optionsCatalogAddItemReal(attribute_options_catalog, +=09=09=09=09=09=09=09=09 "n_distinct_inherited", +=09=09=09=09=09=09=09=09 "Sets the planner's estimate of the number o= f distinct values appearing in a column (including child relations", +=09=09=09=09=09=09=09=09 ShareUpdateExclusiveLock, +=09=09 0, offsetof(AttributeOpts, n_distinct_inherited), 0, -1.0, DBL_= MAX); +=09} +=09return attribute_options_catalog; } =20 /* - * Option parser for tablespace reloptions + * get_tablespace_options_catalog + *=09=09Returns an options catalog for tablespaces */ -bytea * -tablespace_reloptions(Datum reloptions, bool validate) -{ -=09relopt_value *options; -=09TableSpaceOpts *tsopts; -=09int=09=09=09numoptions; -=09static const relopt_parse_elt tab[] =3D { -=09=09{"random_page_cost", RELOPT_TYPE_REAL, offsetof(TableSpaceOpts, = random_page_cost)}, -=09=09{"seq_page_cost", RELOPT_TYPE_REAL, offsetof(TableSpaceOpts, seq= _page_cost)}, -=09=09{"effective_io_concurrency", RELOPT_TYPE_INT, offsetof(TableSpac= eOpts, effective_io_concurrency)} -=09}; - -=09options =3D parseRelOptions(reloptions, validate, RELOPT_KIND_TABLE= SPACE, -=09=09=09=09=09=09=09 &numoptions); - -=09/* if none set, we're done */ -=09if (numoptions =3D=3D 0) -=09=09return NULL; - -=09tsopts =3D allocateReloptStruct(sizeof(TableSpaceOpts), options, nu= moptions); - -=09fillRelOptions((void *) tsopts, sizeof(TableSpaceOpts), options, nu= moptions, -=09=09=09=09 validate, tab, lengthof(tab)); - -=09pfree(options); +static options_catalog *tablespace_options_catalog =3D NULL; =20 -=09return (bytea *) tsopts; +options_catalog * +get_tablespace_options_catalog(void) +{ +=09if (!tablespace_options_catalog) +=09{ +=09=09tablespace_options_catalog =3D allocateOptionsCatalog(NULL, +=09=09=09=09=09=09=09=09=09=09=09=09 sizeof(TableSpaceOpts), 3); + +=09=09optionsCatalogAddItemReal(tablespace_options_catalog, +=09=09=09=09=09=09=09=09 "random_page_cost", +=09=09=09=09=09=09=09=09 "Sets the planner's estimate of the cost of = a nonsequentially fetched disk page", +=09=09=09=09=09=09=09=09 ShareUpdateExclusiveLock, +=09=09=090, offsetof(TableSpaceOpts, random_page_cost), -1, 0.0, DBL_M= AX); + +=09=09optionsCatalogAddItemReal(tablespace_options_catalog, "seq_page_= cost", +=09=09=09=09=09=09=09=09 "Sets the planner's estimate of the cost of = a sequentially fetched disk page", +=09=09=09=09=09=09=09=09 ShareUpdateExclusiveLock, +=09=09=09 0, offsetof(TableSpaceOpts, seq_page_cost), -1, 0.0, DBL_M= AX); + +=09=09optionsCatalogAddItemInt(tablespace_options_catalog, +=09=09=09=09=09=09=09=09 "effective_io_concurrency", +=09=09=09=09=09=09=09=09 "Number of simultaneous requests that can be = handled efficiently by the disk subsystem", +=09=09=09=09=09=09=09=09 ShareUpdateExclusiveLock, +=09=09=09=09=09 0, offsetof(TableSpaceOpts, effective_io_concurrency= ), +#ifdef USE_PREFETCH +=09=09=09=09=09=09=09=09 -1, 0, MAX_IO_CONCURRENCY +#else +=09=09=09=09=09=09=09=09 0, 0, 0 +#endif +=09=09=09); +=09} +=09return tablespace_options_catalog; } =20 /* @@ -1525,33 +390,56 @@ tablespace_reloptions(Datum reloptions, bool val= idate) * for a longer explanation of how this works. */ LOCKMODE -AlterTableGetRelOptionsLockLevel(List *defList) +AlterTableGetRelOptionsLockLevel(Relation rel, List *defList) { =09LOCKMODE=09lockmode =3D NoLock; =09ListCell *cell; +=09options_catalog *catalog =3D NULL; =20 =09if (defList =3D=3D NIL) =09=09return AccessExclusiveLock; =20 -=09if (need_initialization) -=09=09initialize_reloptions(); +=09switch (rel->rd_rel->relkind) +=09{ +=09=09case RELKIND_TOASTVALUE: +=09=09=09catalog =3D get_toast_relopt_catalog(); +=09=09=09break; +=09=09case RELKIND_RELATION: +=09=09case RELKIND_MATVIEW: +=09=09=09catalog =3D get_heap_relopt_catalog(); +=09=09=09break; +=09=09case RELKIND_INDEX: +=09=09=09catalog =3D rel->rd_amroutine->amrelopt_catalog(); +=09=09=09break; +=09=09case RELKIND_VIEW: +=09=09=09catalog =3D get_view_relopt_catalog(); +=09=09=09break; +=09=09case RELKIND_PARTITIONED_TABLE: +=09=09=09catalog =3D NULL; /* No options for parted table for now */ +=09=09=09break; +=09=09default: +=09=09=09Assert(false);=09=09/* can't get here */ +=09=09=09break; +=09} + +=09Assert(catalog);=09=09=09/* No catalog - no reloption change. Shoul= d +=09=09=09=09=09=09=09=09 * not get here */ =20 =09foreach(cell, defList) =09{ =09=09DefElem *def =3D (DefElem *) lfirst(cell); + =09=09int=09=09=09i; =20 -=09=09for (i =3D 0; relOpts[i]; i++) +=09=09for (i =3D 0; i < catalog->num; i++) =09=09{ -=09=09=09if (pg_strncasecmp(relOpts[i]->name, -=09=09=09=09=09=09=09 def->defname, -=09=09=09=09=09=09=09 relOpts[i]->namelen + 1) =3D=3D 0) -=09=09=09{ -=09=09=09=09if (lockmode < relOpts[i]->lockmode) -=09=09=09=09=09lockmode =3D relOpts[i]->lockmode; -=09=09=09} +=09=09=09option_definition_basic *gen =3D catalog->definitions[i]; + +=09=09=09if (pg_strcasecmp(gen->name, +=09=09=09=09=09=09=09 def->defname) =3D=3D 0) +=09=09=09=09if (lockmode < gen->lockmode) +=09=09=09=09=09lockmode =3D gen->lockmode; =09=09} =09} - =09return lockmode; } diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/= ginutil.c index 91e4a8c..0f2af51 100644 --- a/src/backend/access/gin/ginutil.c +++ b/src/backend/access/gin/ginutil.c @@ -16,7 +16,7 @@ =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 para= meters @@ -60,7 +61,6 @@ ginhandler(PG_FUNCTION_ARGS) =09amroutine->amvacuumcleanup =3D ginvacuumcleanup; =09amroutine->amcanreturn =3D NULL; =09amroutine->amcostestimate =3D gincostestimate; -=09amroutine->amoptions =3D ginoptions; =09amroutine->amproperty =3D NULL; =09amroutine->amvalidate =3D ginvalidate; =09amroutine->ambeginscan =3D ginbeginscan; @@ -70,6 +70,7 @@ ginhandler(PG_FUNCTION_ARGS) =09amroutine->amendscan =3D ginendscan; =09amroutine->ammarkpos =3D NULL; =09amroutine->amrestrpos =3D NULL; +=09amroutine->amrelopt_catalog =3D gingetreloptcatalog; =09amroutine->amestimateparallelscan =3D NULL; =09amroutine->aminitparallelscan =3D NULL; =09amroutine->amparallelrescan =3D NULL; @@ -593,35 +594,6 @@ ginExtractEntries(GinState *ginstate, OffsetNumber= attnum, =09return entries; } =20 -bytea * -ginoptions(Datum reloptions, bool validate) -{ -=09relopt_value *options; -=09GinOptions *rdopts; -=09int=09=09=09numoptions; -=09static const relopt_parse_elt tab[] =3D { -=09=09{"fastupdate", RELOPT_TYPE_BOOL, offsetof(GinOptions, useFastUpd= ate)}, -=09=09{"gin_pending_list_limit", RELOPT_TYPE_INT, offsetof(GinOptions,= -=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09 pendingListCleanupSize)}= -=09}; - -=09options =3D parseRelOptions(reloptions, validate, RELOPT_KIND_GIN, -=09=09=09=09=09=09=09 &numoptions); - -=09/* if none set, we're done */ -=09if (numoptions =3D=3D 0) -=09=09return NULL; - -=09rdopts =3D allocateReloptStruct(sizeof(GinOptions), options, numopt= ions); - -=09fillRelOptions((void *) rdopts, sizeof(GinOptions), options, numopt= ions, -=09=09=09=09 validate, tab, lengthof(tab)); - -=09pfree(options); - -=09return (bytea *) rdopts; -} - /* * Fetch index's statistical data into *stats * @@ -698,3 +670,32 @@ ginUpdateStats(Relation index, const GinStatsData = *stats) =20 =09END_CRIT_SECTION(); } + + +static options_catalog *gin_relopt_catalog =3D NULL; + +void * +gingetreloptcatalog(void) +{ +=09if (!gin_relopt_catalog) +=09{ +=09=09gin_relopt_catalog =3D allocateOptionsCatalog(NULL, +=09=09=09=09=09=09=09=09=09=09=09=09=09sizeof(GinOptions), 2); + +=09=09optionsCatalogAddItemBool(gin_relopt_catalog, "fastupdate", +=09=09=09=09=09=09"Enables \"fast update\" feature for this GIN index"= , +=09=09=09=09=09=09=09=09 AccessExclusiveLock, +=09=09=09=09=09=09=09=09 0, +=09=09=09=09=09=09=09=09 offsetof(GinOptions, useFastUpdate), +=09=09=09=09=09=09=09=09 GIN_DEFAULT_USE_FASTUPDATE); + +=09=09optionsCatalogAddItemInt(gin_relopt_catalog, "gin_pending_list_l= imit", +=09=09 "Maximum size of the pending list for this GIN index, in kiloby= tes", +=09=09=09=09=09=09=09=09 AccessExclusiveLock, +=09=09=09=09=09=09=09=09 0, +=09=09=09=09=09=09=09=09 offsetof(GinOptions, pendingListCleanupSize),= +=09=09=09=09=09=09=09=09 -1, 64, MAX_KILOBYTES); + +=09} +=09return gin_relopt_catalog; +} diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/g= ist.c index 565525b..d44916f 100644 --- a/src/backend/access/gist/gist.c +++ b/src/backend/access/gist/gist.c @@ -81,7 +81,6 @@ gisthandler(PG_FUNCTION_ARGS) =09amroutine->amvacuumcleanup =3D gistvacuumcleanup; =09amroutine->amcanreturn =3D gistcanreturn; =09amroutine->amcostestimate =3D gistcostestimate; -=09amroutine->amoptions =3D gistoptions; =09amroutine->amproperty =3D gistproperty; =09amroutine->amvalidate =3D gistvalidate; =09amroutine->ambeginscan =3D gistbeginscan; @@ -91,6 +90,7 @@ gisthandler(PG_FUNCTION_ARGS) =09amroutine->amendscan =3D gistendscan; =09amroutine->ammarkpos =3D NULL; =09amroutine->amrestrpos =3D NULL; +=09amroutine->amrelopt_catalog =3D gistgetreloptcatalog; =09amroutine->amestimateparallelscan =3D NULL; =09amroutine->aminitparallelscan =3D NULL; =09amroutine->amparallelrescan =3D NULL; diff --git a/src/backend/access/gist/gistbuild.c b/src/backend/access/g= ist/gistbuild.c index 4756a70..8d5c839 100644 --- a/src/backend/access/gist/gistbuild.c +++ b/src/backend/access/gist/gistbuild.c @@ -126,11 +126,10 @@ gistbuild(Relation heap, Relation index, IndexInf= o *indexInfo) =09{ =09=09/* Get buffering mode from the options string */ =09=09GiSTOptions *options =3D (GiSTOptions *) index->rd_options; -=09=09char=09 *bufferingMode =3D (char *) options + options->bufferi= ngModeOffset; =20 -=09=09if (strcmp(bufferingMode, "on") =3D=3D 0) +=09=09if (options->buffering_mode =3D=3D GIST_OPTION_BUFFERING_ON) =09=09=09buildstate.bufferingMode =3D GIST_BUFFERING_STATS; -=09=09else if (strcmp(bufferingMode, "off") =3D=3D 0) +=09=09else if (options->buffering_mode =3D=3D GIST_OPTION_BUFFERING_OF= F) =09=09=09buildstate.bufferingMode =3D GIST_BUFFERING_DISABLED; =09=09else =09=09=09buildstate.bufferingMode =3D GIST_BUFFERING_AUTO; @@ -234,25 +233,6 @@ gistbuild(Relation heap, Relation index, IndexInfo= *indexInfo) } =20 /* - * Validator for "buffering" reloption on GiST indexes. Allows "on", "= off" - * and "auto" values. - */ -void -gistValidateBufferingOption(char *value) -{ -=09if (value =3D=3D NULL || -=09=09(strcmp(value, "on") !=3D 0 && -=09=09 strcmp(value, "off") !=3D 0 && -=09=09 strcmp(value, "auto") !=3D 0)) -=09{ -=09=09ereport(ERROR, -=09=09=09=09(errcode(ERRCODE_INVALID_PARAMETER_VALUE), -=09=09=09=09 errmsg("invalid value for \"buffering\" option"), -=09=09=09=09 errdetail("Valid values are \"on\", \"off\", and \"auto\"= ."))); -=09} -} - -/* * Attempt to switch to buffering mode. * * If there is not enough memory for buffering build, sets bufferingMo= de diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gi= st/gistutil.c index b6ccc1a..5f46058 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) =09return buffer; } =20 -bytea * -gistoptions(Datum reloptions, bool validate) -{ -=09relopt_value *options; -=09GiSTOptions *rdopts; -=09int=09=09=09numoptions; -=09static const relopt_parse_elt tab[] =3D { -=09=09{"fillfactor", RELOPT_TYPE_INT, offsetof(GiSTOptions, fillfactor= )}, -=09=09{"buffering", RELOPT_TYPE_STRING, offsetof(GiSTOptions, bufferin= gModeOffset)} -=09}; - -=09options =3D parseRelOptions(reloptions, validate, RELOPT_KIND_GIST,= -=09=09=09=09=09=09=09 &numoptions); - -=09/* if none set, we're done */ -=09if (numoptions =3D=3D 0) -=09=09return NULL; - -=09rdopts =3D allocateReloptStruct(sizeof(GiSTOptions), options, numop= tions); - -=09fillRelOptions((void *) rdopts, sizeof(GiSTOptions), options, numop= tions, -=09=09=09=09 validate, tab, lengthof(tab)); - -=09pfree(options); - -=09return (bytea *) rdopts; -} - /* *=09gistproperty() -- Check boolean properties of indexes. * @@ -965,3 +936,33 @@ gistGetFakeLSN(Relation rel) =09=09return GetFakeLSNForUnloggedRel(); =09} } + +static options_catalog *gist_relopt_catalog =3D NULL; + +void * +gistgetreloptcatalog(void) +{ +=09static const char *enum_names[] =3D GIST_OPTION_BUFFERING_VALUE_NAM= ES; + +=09if (!gist_relopt_catalog) +=09{ +=09=09gist_relopt_catalog =3D allocateOptionsCatalog(NULL, +=09=09=09=09=09=09=09=09=09=09=09=09=09 sizeof(GiSTOptions), 2); + +=09=09optionsCatalogAddItemInt(gist_relopt_catalog, "fillfactor", +=09=09=09=09=09=09=09"Packs gist index pages only to this percentage",= +=09=09=09=09=09=09=09=09 NoLock,=09=09/* No ALTER, no lock */ +=09=09=09=09=09=09=09=09 OPTION_DEFINITION_FLAG_FORBID_ALTER, +=09=09=09=09=09=09=09=09 offsetof(GiSTOptions, fillfactor), +=09=09=09=09=09=09=09=09 GIST_DEFAULT_FILLFACTOR, +=09=09=09=09=09=09=09=09 GIST_MIN_FILLFACTOR, 100); +=09=09optionsCatalogAddItemEnum(gist_relopt_catalog, "buffering", +=09=09=09=09=09=09=09 "Enables buffering build for this GiST index",= +=09=09=09=09=09=09=09=09 NoLock,=09=09/* No ALTER, no lock */ +=09=09=09=09=09=09=09=09 OPTION_DEFINITION_FLAG_FORBID_ALTER, +=09=09=09=09=09=09=09=09 offsetof(GiSTOptions, buffering_mode), +=09=09=09=09=09=09=09=09 enum_names, +=09=09=09=09=09=09=09=09 GIST_OPTION_BUFFERING_AUTO); +=09} +=09return gist_relopt_catalog; +} diff --git a/src/backend/access/hash/hash.c b/src/backend/access/hash/h= ash.c index d89c192..6ef32cf 100644 --- a/src/backend/access/hash/hash.c +++ b/src/backend/access/hash/hash.c @@ -79,7 +79,6 @@ hashhandler(PG_FUNCTION_ARGS) =09amroutine->amvacuumcleanup =3D hashvacuumcleanup; =09amroutine->amcanreturn =3D NULL; =09amroutine->amcostestimate =3D hashcostestimate; -=09amroutine->amoptions =3D hashoptions; =09amroutine->amproperty =3D NULL; =09amroutine->amvalidate =3D hashvalidate; =09amroutine->ambeginscan =3D hashbeginscan; @@ -89,6 +88,7 @@ hashhandler(PG_FUNCTION_ARGS) =09amroutine->amendscan =3D hashendscan; =09amroutine->ammarkpos =3D NULL; =09amroutine->amrestrpos =3D NULL; +=09amroutine->amrelopt_catalog =3D hashgetreloptcatalog; =09amroutine->amestimateparallelscan =3D NULL; =09amroutine->aminitparallelscan =3D NULL; =09amroutine->amparallelrescan =3D NULL; diff --git a/src/backend/access/hash/hashpage.c b/src/backend/access/ha= sh/hashpage.c index 1cb18a7..8330044 100644 --- a/src/backend/access/hash/hashpage.c +++ b/src/backend/access/hash/hashpage.c @@ -360,7 +360,7 @@ _hash_init(Relation rel, double num_tuples, ForkNum= ber forkNum) =09data_width =3D sizeof(uint32); =09item_width =3D MAXALIGN(sizeof(IndexTupleData)) + MAXALIGN(data_wid= th) + =09=09sizeof(ItemIdData);=09=09/* include the line pointer */ -=09ffactor =3D RelationGetTargetPageUsage(rel, HASH_DEFAULT_FILLFACTOR= ) / item_width; +=09ffactor =3D (BLCKSZ * HashGetFillFactor(rel) / 100) / item_width; =09/* keep to a sane range */ =09if (ffactor < 10) =09=09ffactor =3D 10; diff --git a/src/backend/access/hash/hashutil.c b/src/backend/access/ha= sh/hashutil.c index 9b803af..c5309b0 100644 --- a/src/backend/access/hash/hashutil.c +++ b/src/backend/access/hash/hashutil.c @@ -15,7 +15,7 @@ #include "postgres.h" =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" @@ -286,12 +286,6 @@ _hash_checkpage(Relation rel, Buffer buf, int flag= s) =09} } =20 -bytea * -hashoptions(Datum reloptions, bool validate) -{ -=09return default_reloptions(reloptions, validate, RELOPT_KIND_HASH); -} - /* * _hash_get_indextuple_hashkey - get the hash index tuple's hash key = value */ @@ -581,3 +575,28 @@ _hash_kill_items(IndexScanDesc scan) =09=09MarkBufferDirtyHint(so->hashso_curbuf, true); =09} } + +static options_catalog *hash_relopt_catalog =3D NULL; + +void * +hashgetreloptcatalog(void) +{ +=09if (!hash_relopt_catalog) +=09{ +=09=09hash_relopt_catalog =3D allocateOptionsCatalog(NULL, +=09=09=09=09=09=09=09=09=09=09=09=09 sizeof(HashRelOptions), 1); + +=09=09optionsCatalogAddItemInt(hash_relopt_catalog, "fillfactor", +=09=09=09=09=09=09=09"Packs hash index pages only to this percentage",= +=09=09=09=09=09=09=09=09 NoLock,=09=09/* No ALTER -- no lock */ +=09=09=09=09=09=09=09=09 OPTION_DEFINITION_FLAG_FORBID_ALTER,=09/* fil= lfactor is actualy +=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09 * stored in met= apage +=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09 * and should no= t be +=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09 * changed once = index is +=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09 * created */ +=09=09=09=09=09=09=09=09 offsetof(HashRelOptions, fillfactor), +=09=09=09=09=09=09=09=09 HASH_DEFAULT_FILLFACTOR, +=09=09=09=09=09=09=09=09 HASH_MIN_FILLFACTOR, 100); +=09} +=09return hash_relopt_catalog; +} diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap= /heapam.c index 9766723..6780ef8 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -2670,7 +2670,10 @@ heap_multi_insert(Relation relation, HeapTuple *= tuples, int ntuples, =09bool=09=09need_cids =3D RelationIsAccessibleInLogicalDecoding(relat= ion); =20 =09needwal =3D !(options & HEAP_INSERT_SKIP_WAL) && RelationNeedsWAL(r= elation); -=09saveFreeSpace =3D RelationGetTargetPageFreeSpace(relation, +=09if (IsToastRelation(relation)) +=09=09saveFreeSpace =3D ToastGetTargetPageFreeSpace(); +=09else +=09=09saveFreeSpace =3D HeapGetTargetPageFreeSpace(relation, =09=09=09=09=09=09=09=09=09=09=09=09 HEAP_DEFAULT_FILLFACTOR); =20 =09/* Toast and set header data in all the tuples */ diff --git a/src/backend/access/heap/hio.c b/src/backend/access/heap/hi= o.c index 13e3bdc..8c8f023 100644 --- a/src/backend/access/heap/hio.c +++ b/src/backend/access/heap/hio.c @@ -323,7 +323,10 @@ RelationGetBufferForTuple(Relation relation, Size = len, =09=09=09=09=09=09len, MaxHeapTupleSize))); =20 =09/* Compute desired extra freespace due to fillfactor option */ -=09saveFreeSpace =3D RelationGetTargetPageFreeSpace(relation, +=09if (IsToastRelation(relation)) +=09=09saveFreeSpace =3D ToastGetTargetPageFreeSpace(); +=09else +=09=09saveFreeSpace =3D HeapGetTargetPageFreeSpace(relation, =09=09=09=09=09=09=09=09=09=09=09=09 HEAP_DEFAULT_FILLFACTOR); =20 =09if (otherBuffer !=3D InvalidBuffer) diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/h= eap/pruneheap.c index 52231ac..0686cf0 100644 --- a/src/backend/access/heap/pruneheap.c +++ b/src/backend/access/heap/pruneheap.c @@ -130,7 +130,11 @@ heap_page_prune_opt(Relation relation, Buffer buff= er) =09 * important than sometimes getting a wrong answer in what is after= all =09 * just a heuristic estimate. =09 */ -=09minfree =3D RelationGetTargetPageFreeSpace(relation, + +=09if (IsToastRelation(relation)) +=09=09minfree =3D ToastGetTargetPageFreeSpace(); +=09else +=09=09minfree =3D HeapGetTargetPageFreeSpace(relation, =09=09=09=09=09=09=09=09=09=09=09 HEAP_DEFAULT_FILLFACTOR); =09minfree =3D Max(minfree, BLCKSZ / 10); =20 diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access= /heap/rewriteheap.c index bd560e4..b40abc9 100644 --- a/src/backend/access/heap/rewriteheap.c +++ b/src/backend/access/heap/rewriteheap.c @@ -667,7 +667,10 @@ raw_heap_insert(RewriteState state, HeapTuple tup)= =09=09=09=09=09=09len, MaxHeapTupleSize))); =20 =09/* Compute desired extra freespace due to fillfactor option */ -=09saveFreeSpace =3D RelationGetTargetPageFreeSpace(state->rs_new_rel,= +=09if (IsToastRelation(state->rs_new_rel)) +=09=09saveFreeSpace =3D ToastGetTargetPageFreeSpace(); +=09else +=09=09saveFreeSpace =3D HeapGetTargetPageFreeSpace(state->rs_new_rel, =09=09=09=09=09=09=09=09=09=09=09=09 HEAP_DEFAULT_FILLFACTOR); =20 =09/* Now we can check to see if there's enough free space already. */= diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access= /nbtree/nbtinsert.c index 4aca7e4..f37054a 100644 --- a/src/backend/access/nbtree/nbtinsert.c +++ b/src/backend/access/nbtree/nbtinsert.c @@ -1433,8 +1433,7 @@ _bt_findsplitloc(Relation rel, =09state.is_rightmost =3D P_RIGHTMOST(opaque); =09state.have_split =3D false; =09if (state.is_leaf) -=09=09state.fillfactor =3D RelationGetFillFactor(rel, -=09=09=09=09=09=09=09=09=09=09=09=09 BTREE_DEFAULT_FILLFACTOR); +=09=09state.fillfactor =3D BTGetFillFactor(rel); =09else =09=09state.fillfactor =3D BTREE_NONLEAF_FILLFACTOR; =09state.newitemonleft =3D false;=09/* these just to keep compiler qui= et */ diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nb= tree/nbtree.c index 3dbafdd..d164e6c 100644 --- a/src/backend/access/nbtree/nbtree.c +++ b/src/backend/access/nbtree/nbtree.c @@ -21,6 +21,7 @@ #include "access/nbtree.h" #include "access/relscan.h" #include "access/xlog.h" +#include "access/reloptions.h" #include "catalog/index.h" #include "commands/vacuum.h" #include "pgstat.h" @@ -34,7 +35,6 @@ #include "utils/index_selfuncs.h" #include "utils/memutils.h" =20 - /* Working state for btbuild and its callback */ typedef struct { @@ -115,8 +115,7 @@ static void btvacuumscan(IndexVacuumInfo *info, Ind= exBulkDeleteResult *stats, =09=09=09 BTCycleId cycleid); static void btvacuumpage(BTVacState *vstate, BlockNumber blkno, =09=09=09 BlockNumber orig_blkno); - - +static void * btgetreloptcatalog (void); /* * Btree handler function: return IndexAmRoutine with access method pa= rameters * and callbacks. @@ -149,7 +148,6 @@ bthandler(PG_FUNCTION_ARGS) =09amroutine->amvacuumcleanup =3D btvacuumcleanup; =09amroutine->amcanreturn =3D btcanreturn; =09amroutine->amcostestimate =3D btcostestimate; -=09amroutine->amoptions =3D btoptions; =09amroutine->amproperty =3D btproperty; =09amroutine->amvalidate =3D btvalidate; =09amroutine->ambeginscan =3D btbeginscan; @@ -159,6 +157,7 @@ bthandler(PG_FUNCTION_ARGS) =09amroutine->amendscan =3D btendscan; =09amroutine->ammarkpos =3D btmarkpos; =09amroutine->amrestrpos =3D btrestrpos; +=09amroutine->amrelopt_catalog =3D btgetreloptcatalog; =09amroutine->amestimateparallelscan =3D btestimateparallelscan; =09amroutine->aminitparallelscan =3D btinitparallelscan; =09amroutine->amparallelrescan =3D btparallelrescan; @@ -1390,3 +1389,26 @@ btcanreturn(Relation index, int attno) { =09return true; } + + +static options_catalog *bt_relopt_catalog =3D NULL; + +static void * +btgetreloptcatalog(void) +{ +=09if (!bt_relopt_catalog) +=09{ +=09=09bt_relopt_catalog =3D allocateOptionsCatalog(NULL, +=09=09=09=09=09=09=09=09=09=09=09=09 sizeof(BTRelOptions), 1); + +=09=09optionsCatalogAddItemInt(bt_relopt_catalog, "fillfactor", +=09=09=09=09=09=09 "Packs btree index pages only to this percentage"= , +=09=09=09=09=09=09=09=09 ShareUpdateExclusiveLock,=09=09/* since it ap= plies only +=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09 * to later inserts */= +=09=09=09=09=09=09=09=09 0, +=09=09=09=09=09=09=09=09 offsetof(BTRelOptions, fillfactor), +=09=09=09=09=09=09=09=09 BTREE_DEFAULT_FILLFACTOR, +=09=09=09=09=09=09=09=09 BTREE_MIN_FILLFACTOR, 100); +=09} +=09return bt_relopt_catalog; +} diff --git a/src/backend/access/nbtree/nbtsort.c b/src/backend/access/n= btree/nbtsort.c index bf6c03c..5c6957a 100644 --- a/src/backend/access/nbtree/nbtsort.c +++ b/src/backend/access/nbtree/nbtsort.c @@ -344,8 +344,8 @@ _bt_pagestate(BTWriteState *wstate, uint32 level) =09if (level > 0) =09=09state->btps_full =3D (BLCKSZ * (100 - BTREE_NONLEAF_FILLFACTOR) = / 100); =09else -=09=09state->btps_full =3D RelationGetTargetPageFreeSpace(wstate->inde= x, -=09=09=09=09=09=09=09=09=09=09=09=09=09=09 BTREE_DEFAULT_FILLFACTOR);= +=09=09state->btps_full =3D (BLCKSZ * (100 - BTGetFillFactor(wstate->i= ndex)) / 100); + =09/* no parent level, yet */ =09state->btps_next =3D NULL; =20 diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/= nbtree/nbtutils.c index dbfb775..b01d22e 100644 --- a/src/backend/access/nbtree/nbtutils.c +++ b/src/backend/access/nbtree/nbtutils.c @@ -18,7 +18,7 @@ #include =20 #include "access/nbtree.h" -#include "access/reloptions.h" +#include "storage/lock.h" #include "access/relscan.h" #include "miscadmin.h" #include "utils/array.h" @@ -2038,12 +2038,6 @@ BTreeShmemInit(void) =09=09Assert(found); } =20 -bytea * -btoptions(Datum reloptions, bool validate) -{ -=09return default_reloptions(reloptions, validate, RELOPT_KIND_BTREE);= -} - /* *=09btproperty() -- Check boolean properties of indexes. * diff --git a/src/backend/access/spgist/spgutils.c b/src/backend/access/= spgist/spgutils.c index 8656af4..022e893 100644 --- a/src/backend/access/spgist/spgutils.c +++ b/src/backend/access/spgist/spgutils.c @@ -15,7 +15,7 @@ =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) =09amroutine->amvacuumcleanup =3D spgvacuumcleanup; =09amroutine->amcanreturn =3D spgcanreturn; =09amroutine->amcostestimate =3D spgcostestimate; -=09amroutine->amoptions =3D spgoptions; =09amroutine->amproperty =3D NULL; =09amroutine->amvalidate =3D spgvalidate; =09amroutine->ambeginscan =3D spgbeginscan; @@ -69,6 +68,7 @@ spghandler(PG_FUNCTION_ARGS) =09amroutine->amendscan =3D spgendscan; =09amroutine->ammarkpos =3D NULL; =09amroutine->amrestrpos =3D NULL; +=09amroutine->amrelopt_catalog =3D spggetreloptcatalog; =09amroutine->amestimateparallelscan =3D NULL; =09amroutine->aminitparallelscan =3D NULL; =09amroutine->amparallelrescan =3D NULL; @@ -372,8 +372,8 @@ SpGistGetBuffer(Relation index, int flags, int need= Space, bool *isNew) =09 * related to the ones already on it. But fillfactor mustn't cause= an =09 * error for requests that would otherwise be legal. =09 */ -=09needSpace +=3D RelationGetTargetPageFreeSpace(index, -=09=09=09=09=09=09=09=09=09=09=09=09SPGIST_DEFAULT_FILLFACTOR); +=09needSpace +=3D (BLCKSZ * (100 - SpGistGetFillFactor(index)) / 100);= + =09needSpace =3D Min(needSpace, SPGIST_PAGE_CAPACITY); =20 =09/* 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) -{ -=09return default_reloptions(reloptions, validate, RELOPT_KIND_SPGIST)= ; -} - -/* * Get the space needed to store a non-null datum of the indicated typ= e. * Note the result is already rounded up to a MAXALIGN boundary. * Also, we follow the SPGiST convention that pass-by-val types are @@ -911,3 +902,27 @@ SpGistPageAddNewItem(SpGistState *state, Page page= , Item item, Size size, =20 =09return offnum; } + + + +static options_catalog *spgist_relopt_catalog =3D NULL; + +void * +spggetreloptcatalog(void) +{ +=09if (!spgist_relopt_catalog) +=09{ +=09=09spgist_relopt_catalog =3D allocateOptionsCatalog(NULL, +=09=09=09=09=09=09=09=09=09=09=09=09sizeof(SpGistRelOptions), 1); + +=09=09optionsCatalogAddItemInt(spgist_relopt_catalog, "fillfactor", +=09=09=09=09=09=09 "Packs spgist index pages only to this percentage"= , +=09=09=09=09=09=09=09=09 ShareUpdateExclusiveLock,=09=09/* since it ap= plies only +=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09 * to later inserts */= +=09=09=09=09=09=09=09=09 0, +=09=09=09=09=09=09=09=09 offsetof(SpGistRelOptions, fillfactor), +=09=09=09=09=09=09=09=09 SPGIST_DEFAULT_FILLFACTOR, +=09=09=09=09=09=09=09=09 SPGIST_MIN_FILLFACTOR, 100); +=09} +=09return spgist_relopt_catalog; +} diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toast= ing.c index 29756eb..aaa9270 100644 --- a/src/backend/catalog/toasting.c +++ b/src/backend/catalog/toasting.c @@ -36,7 +36,7 @@ /* Potentially set by pg_upgrade_support functions */ Oid=09=09=09binary_upgrade_next_toast_pg_type_oid =3D InvalidOid; =20 -static void CheckAndCreateToastTable(Oid relOid, Datum reloptions, +static bool CheckAndCreateToastTable(Oid relOid, Datum reloptions, =09=09=09=09=09=09 LOCKMODE lockmode, bool check); static bool create_toast_table(Relation rel, Oid toastOid, Oid toastIn= dexOid, =09=09=09=09 Datum reloptions, LOCKMODE lockmode, bool check); @@ -67,23 +67,26 @@ NewHeapCreateToastTable(Oid relOid, Datum reloption= s, LOCKMODE lockmode) =09CheckAndCreateToastTable(relOid, reloptions, lockmode, false); } =20 -void +bool NewRelationCreateToastTable(Oid relOid, Datum reloptions) { -=09CheckAndCreateToastTable(relOid, reloptions, AccessExclusiveLock, f= alse); +=09return CheckAndCreateToastTable(relOid, reloptions, +=09=09=09=09=09=09=09=09=09AccessExclusiveLock, false); } =20 -static void +static bool CheckAndCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmo= de, bool check) { =09Relation=09rel; +=09bool=09=09success; =20 =09rel =3D heap_open(relOid, lockmode); =20 =09/* create_toast_table does all the work */ -=09(void) create_toast_table(rel, InvalidOid, InvalidOid, reloptions, = lockmode, check); +=09success =3D create_toast_table(rel, InvalidOid, InvalidOid, relopti= ons, lockmode, check); =20 =09heap_close(rel, NoLock); +=09return success; } =20 /* diff --git a/src/backend/commands/createas.c b/src/backend/commands/cre= ateas.c index 97f9c55..79c5147 100644 --- a/src/backend/commands/createas.c +++ b/src/backend/commands/createas.c @@ -89,6 +89,8 @@ create_ctas_internal(List *attrList, IntoClause *into= ) =09Datum=09=09toast_options; =09static char *validnsps[] =3D HEAP_RELOPT_NAMESPACES; =09ObjectAddress intoRelationAddr; +=09List=09 *toastDefList; +=09bool=09=09toast_created; =20 =09/* This code supports both CREATE TABLE AS and CREATE MATERIALIZED = VIEW */ =09is_matview =3D (into->viewQuery !=3D NULL); @@ -122,15 +124,20 @@ create_ctas_internal(List *attrList, IntoClause *= into) =09CommandCounterIncrement(); =20 =09/* parse and validate reloptions for the toast table */ -=09toast_options =3D transformRelOptions((Datum) 0, -=09=09=09=09=09=09=09=09=09=09create->options, -=09=09=09=09=09=09=09=09=09=09"toast", -=09=09=09=09=09=09=09=09=09=09validnsps, -=09=09=09=09=09=09=09=09=09=09true, false); =20 -=09(void) heap_reloptions(RELKIND_TOASTVALUE, toast_options, true); =20 -=09NewRelationCreateToastTable(intoRelationAddr.objectId, toast_option= s); +=09optionsDefListValdateNamespaces(create->options, validnsps); +=09toastDefList =3D optionsDefListFilterNamespaces(create->options, "t= oast"); + +=09toast_options =3D transformOptions(get_toast_relopt_catalog(), (Dat= um) 0, +=09=09=09=09=09=09=09=09=09 toastDefList, 0); + +=09toast_created =3D NewRelationCreateToastTable(intoRelationAddr.obje= ctId, +=09=09=09=09=09=09=09=09=09=09=09=09toast_options); +=09if (!toast_created && toast_options) +=09=09ereport(ERROR, +=09=09=09=09(errcode(ERRCODE_INVALID_PARAMETER_VALUE), +=09=09=09=09 errmsg("no TOAST relation was created for a table. Can't = set toast.* storage parameters"))); =20 =09/* Create the "view" part of a materialized view. */ =09if (is_matview) diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/= foreigncmds.c index 9ad9915..6f78d7b 100644 --- a/src/backend/commands/foreigncmds.c +++ b/src/backend/commands/foreigncmds.c @@ -111,7 +111,7 @@ transformGenericOptions(Oid catalogId, =09=09=09=09=09=09List *options, =09=09=09=09=09=09Oid fdwvalidator) { -=09List=09 *resultOptions =3D untransformRelOptions(oldOptions); +=09List=09 *resultOptions =3D optionsTextArrayToDefList(oldOptions);= =09ListCell *optcell; =09Datum=09=09result; =20 diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/in= dexcmds.c index 620704e..c3c8b21 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -329,7 +329,7 @@ DefineIndex(Oid relationId, =09Form_pg_am=09accessMethodForm; =09IndexAmRoutine *amRoutine; =09bool=09=09amcanorder; -=09amoptions_function amoptions; +=09amrelopt_catalog_function amoption_catalog; =09Datum=09=09reloptions; =09int16=09 *coloptions; =09IndexInfo *indexInfo; @@ -536,7 +536,7 @@ DefineIndex(Oid relationId, =09=09=09=09=09=09accessMethodName))); =20 =09amcanorder =3D amRoutine->amcanorder; -=09amoptions =3D amRoutine->amoptions; +=09amoption_catalog =3D amRoutine->amrelopt_catalog; =20 =09pfree(amRoutine); =09ReleaseSysCache(tuple); @@ -550,10 +550,18 @@ DefineIndex(Oid relationId, =09/* =09 * Parse AM-specific options, convert to text array form, validate.= =09 */ -=09reloptions =3D transformRelOptions((Datum) 0, stmt->options, -=09=09=09=09=09=09=09=09=09 NULL, NULL, false, false); - -=09(void) index_reloptions(amoptions, reloptions, true); +=09if (amoption_catalog) +=09{ +=09=09reloptions =3D transformOptions(amoption_catalog(), +=09=09=09=09=09=09=09=09=09 (Datum) 0, stmt->options, 0); +=09} +=09else +=09{ +=09=09ereport(ERROR, +=09=09=09=09(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), +=09=09=09=09 errmsg("access method %s does not support options", +=09=09=09=09=09=09accessMethodName))); +=09} =20 =09/* =09 * Prepare arguments for index_create, primarily an IndexInfo struc= ture. diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/ta= blecmds.c index 7d9c769..0847809 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -513,7 +513,6 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid = ownerId, =09Datum=09=09reloptions; =09ListCell *listptr; =09AttrNumber=09attnum; -=09static char *validnsps[] =3D HEAP_RELOPT_NAMESPACES; =09Oid=09=09=09ofTypeId; =09ObjectAddress address; =20 @@ -599,13 +598,23 @@ DefineRelation(CreateStmt *stmt, char relkind, Oi= d ownerId, =09/* =09 * Parse and validate reloptions, if any. =09 */ -=09reloptions =3D transformRelOptions((Datum) 0, stmt->options, NULL, = validnsps, -=09=09=09=09=09=09=09=09=09 true, false); - =09if (relkind =3D=3D RELKIND_VIEW) -=09=09(void) view_reloptions(reloptions, true); +=09{ +=09=09reloptions =3D transformOptions( +=09=09=09=09=09=09=09=09=09 get_view_relopt_catalog(), +=09=09=09=09=09=09=09=09=09 (Datum) 0, stmt->options, 0); +=09} =09else -=09=09(void) heap_reloptions(relkind, reloptions, true); +=09{ +=09=09/* If it is not view, then it if heap */ +=09=09char=09 *namespaces[] =3D HEAP_RELOPT_NAMESPACES; +=09=09List=09 *heapDefList; + +=09=09optionsDefListValdateNamespaces(stmt->options, namespaces); +=09=09heapDefList =3D optionsDefListFilterNamespaces(stmt->options, NU= LL); +=09=09reloptions =3D transformOptions(get_heap_relopt_catalog(), +=09=09=09=09=09=09=09=09=09 (Datum) 0, heapDefList, 0); +=09} =20 =09if (stmt->ofTypename) =09{ @@ -3113,7 +3122,8 @@ void AlterTableInternal(Oid relid, List *cmds, bool recurse) { =09Relation=09rel; -=09LOCKMODE=09lockmode =3D AlterTableGetLockLevel(cmds); + +=09LOCKMODE=09lockmode =3D AlterTableGetLockLevel(relid, cmds); =20 =09rel =3D relation_open(relid, lockmode); =20 @@ -3155,13 +3165,15 @@ AlterTableInternal(Oid relid, List *cmds, bool = recurse) * otherwise we might end up with an inconsistent dump that can't rest= ore. */ LOCKMODE -AlterTableGetLockLevel(List *cmds) + +AlterTableGetLockLevel(Oid relid, List *cmds) { =09/* =09 * This only works if we read catalog tables using MVCC snapshots. =09 */ =09ListCell *lcmd; =09LOCKMODE=09lockmode =3D ShareUpdateExclusiveLock; +=09Relation=09rel; =20 =09foreach(lcmd, cmds) =09{ @@ -3372,7 +3384,10 @@ AlterTableGetLockLevel(List *cmds) =09=09=09=09=09=09=09=09=09 * getTables() */ =09=09=09case AT_ResetRelOptions:=09/* Uses MVCC in getIndexes() and =09=09=09=09=09=09=09=09=09=09 * getTables() */ -=09=09=09=09cmd_lockmode =3D AlterTableGetRelOptionsLockLevel((List *)= cmd->def); +=09=09=09=09rel =3D relation_open(relid, NoLock); +=09=09=09=09cmd_lockmode =3D AlterTableGetRelOptionsLockLevel(rel, +=09=09=09=09=09=09=09=09=09=09=09=09=09castNode(List, cmd->def)); +=09=09=09=09relation_close(rel,NoLock); =09=09=09=09break; =20 =09=09=09case AT_AttachPartition: @@ -4147,7 +4162,7 @@ ATRewriteTables(AlterTableStmt *parsetree, List *= *wqueue, LOCKMODE lockmode) =09=09=09=09=09=09 errmsg("cannot rewrite system relation \"%s\"", =09=09=09=09=09=09=09=09RelationGetRelationName(OldHeap)))); =20 -=09=09=09if (RelationIsUsedAsCatalogTable(OldHeap)) +=09=09=09if (HeapIsUsedAsCatalogTable(OldHeap)) =09=09=09=09ereport(ERROR, =09=09=09=09=09=09(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), =09=09=09=09=09=09 errmsg("cannot rewrite table \"%s\" used as a catal= og table", @@ -6228,11 +6243,11 @@ ATExecSetOptions(Relation rel, const char *colN= ame, Node *options, =09/* Generate new proposed attoptions (text array) */ =09datum =3D SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attopti= ons, =09=09=09=09=09=09=09&isnull); -=09newOptions =3D transformRelOptions(isnull ? (Datum) 0 : datum, -=09=09=09=09=09=09=09=09=09 castNode(List, options), NULL, NULL, -=09=09=09=09=09=09=09=09=09 false, isReset); -=09/* Validate new options */ -=09(void) attribute_reloptions(newOptions, true); + +=09newOptions =3D transformOptions(get_attribute_options_catalog(), +=09=09=09=09=09=09=09=09 isnull ? (Datum) 0 : datum, +=09=09=09=09=09 castNode(List, options), OPTIONS_PARSE_MODE_FOR_ALTER= | +=09=09=09=09=09=09=09 (isReset ? OPTIONS_PARSE_MODE_FOR_RESET : 0));= =20 =09/* Build new tuple. */ =09memset(repl_null, false, sizeof(repl_null)); @@ -10256,7 +10271,7 @@ ATExecSetRelOptions(Relation rel, List *defList= , AlterTableType operation, =09Datum=09=09repl_val[Natts_pg_class]; =09bool=09=09repl_null[Natts_pg_class]; =09bool=09=09repl_repl[Natts_pg_class]; -=09static char *validnsps[] =3D HEAP_RELOPT_NAMESPACES; +=09options_parse_mode parse_mode; =20 =09if (defList =3D=3D NIL && operation !=3D AT_ReplaceRelOptions) =09=09return;=09=09=09=09=09/* nothing to do */ @@ -10285,25 +10300,52 @@ ATExecSetRelOptions(Relation rel, List *defLi= st, AlterTableType operation, =09=09=09=09=09=09=09=09&isnull); =09} =20 -=09/* Generate new proposed reloptions (text array) */ -=09newOptions =3D transformRelOptions(isnull ? (Datum) 0 : datum, -=09=09=09=09=09=09=09=09=09 defList, NULL, validnsps, false, -=09=09=09=09=09=09=09=09=09 operation =3D=3D AT_ResetRelOptions); - =09/* Validate */ +=09parse_mode =3D OPTIONS_PARSE_MODE_FOR_ALTER; +=09if (operation =3D=3D AT_ResetRelOptions) +=09=09parse_mode |=3D OPTIONS_PARSE_MODE_FOR_RESET; + =09switch (rel->rd_rel->relkind) =09{ =09=09case RELKIND_RELATION: =09=09case RELKIND_TOASTVALUE: =09=09case RELKIND_MATVIEW: =09=09case RELKIND_PARTITIONED_TABLE: -=09=09=09(void) heap_reloptions(rel->rd_rel->relkind, newOptions, true= ); +=09=09=09{ +=09=09=09=09char=09 *namespaces[] =3D HEAP_RELOPT_NAMESPACES; +=09=09=09=09List=09 *heapDefList; + +=09=09=09=09optionsDefListValdateNamespaces(defList, namespaces); +=09=09=09=09heapDefList =3D optionsDefListFilterNamespaces( +=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09 defList, NULL); +=09=09=09=09newOptions =3D transformOptions(get_heap_relopt_catalog(),= +=09=09=09=09=09=09=09=09=09=09=09 isnull ? (Datum) 0 : datum, +=09=09=09=09=09=09=09=09=09=09=09 heapDefList, parse_mode); +=09=09=09} =09=09=09break; =09=09case RELKIND_VIEW: -=09=09=09(void) view_reloptions(newOptions, true); +=09=09=09{ +=09=09=09=09newOptions =3D transformOptions( +=09=09=09=09=09=09=09=09=09=09=09 get_view_relopt_catalog(), +=09=09=09=09=09=09=09=09=09=09=09 isnull ? (Datum) 0 : datum, +=09=09=09=09=09=09=09=09=09=09=09 defList, parse_mode); +=09=09=09} =09=09=09break; =09=09case RELKIND_INDEX: -=09=09=09(void) index_reloptions(rel->rd_amroutine->amoptions, newOpti= ons, true); +=09=09=09if (rel->rd_amroutine->amrelopt_catalog) +=09=09=09{ +=09=09=09=09newOptions =3D transformOptions( +=09=09=09=09=09=09=09=09=09 rel->rd_amroutine->amrelopt_catalog(), +=09=09=09=09=09=09=09=09=09=09=09 isnull ? (Datum) 0 : datum, +=09=09=09=09=09=09=09=09=09=09=09 defList, parse_mode); +=09=09=09} +=09=09=09else +=09=09=09{ +=09=09=09=09ereport(ERROR, +=09=09=09=09=09=09(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), +=09=09=09=09=09=09 errmsg("index %s does not support options", +=09=09=09=09=09=09=09=09RelationGetRelationName(rel)))); +=09=09=09} =09=09=09break; =09=09default: =09=09=09ereport(ERROR, @@ -10317,7 +10359,7 @@ ATExecSetRelOptions(Relation rel, List *defList= , AlterTableType operation, =09if (rel->rd_rel->relkind =3D=3D RELKIND_VIEW) =09{ =09=09Query=09 *view_query =3D get_view_query(rel); -=09=09List=09 *view_options =3D untransformRelOptions(newOptions); +=09=09List=09 *view_options =3D optionsTextArrayToDefList(newOptions= ); =09=09ListCell *cell; =09=09bool=09=09check_option =3D false; =20 @@ -10377,6 +10419,8 @@ ATExecSetRelOptions(Relation rel, List *defList= , AlterTableType operation, =09{ =09=09Relation=09toastrel; =09=09Oid=09=09=09toastid =3D rel->rd_rel->reltoastrelid; +=09=09List=09 *toastDefList; +=09=09options_parse_mode parse_mode; =20 =09=09toastrel =3D heap_open(toastid, lockmode); =20 @@ -10400,12 +10444,15 @@ ATExecSetRelOptions(Relation rel, List *defLi= st, AlterTableType operation, =09=09=09datum =3D SysCacheGetAttr(RELOID, tuple, Anum_pg_class_relopt= ions, =09=09=09=09=09=09=09=09=09&isnull); =09=09} +=09=09parse_mode =3D OPTIONS_PARSE_MODE_FOR_ALTER; +=09=09if (operation =3D=3D AT_ResetRelOptions) +=09=09=09parse_mode |=3D OPTIONS_PARSE_MODE_FOR_RESET; =20 -=09=09newOptions =3D transformRelOptions(isnull ? (Datum) 0 : datum, -=09=09=09=09=09=09=09=09=09=09 defList, "toast", validnsps, false, -=09=09=09=09=09=09=09=09=09=09 operation =3D=3D AT_ResetRelOptions); +=09=09toastDefList =3D optionsDefListFilterNamespaces(defList, "toast"= ); =20 -=09=09(void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true); +=09=09newOptions =3D transformOptions(get_toast_relopt_catalog(), +=09=09=09=09=09=09=09=09=09 isnull ? (Datum) 0 : datum, +=09=09=09=09=09=09=09=09=09 toastDefList, parse_mode); =20 =09=09memset(repl_val, 0, sizeof(repl_val)); =09=09memset(repl_null, false, sizeof(repl_null)); @@ -10433,6 +10480,18 @@ ATExecSetRelOptions(Relation rel, List *defLis= t, AlterTableType operation, =20 =09=09heap_close(toastrel, NoLock); =09} +=09else +=09{ +=09=09List=09 *toastDefList; + +=09=09toastDefList =3D optionsDefListFilterNamespaces(defList, "toast"= ); +=09=09if (toastDefList) +=09=09{ +=09=09=09ereport(ERROR, +=09=09=09=09=09(errcode(ERRCODE_INVALID_PARAMETER_VALUE), +=09=09=09=09=09 errmsg("TOAST relation for this table does not exist. = Can't change toast.* storage parameters"))); +=09=09} +=09} =20 =09heap_close(pgclass, RowExclusiveLock); } diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/t= ablespace.c index 8559c3b..3498fef 100644 --- a/src/backend/commands/tablespace.c +++ b/src/backend/commands/tablespace.c @@ -332,10 +332,11 @@ CreateTableSpace(CreateTableSpaceStmt *stmt) =09nulls[Anum_pg_tablespace_spcacl - 1] =3D true; =20 =09/* Generate new proposed spcoptions (text array) */ -=09newOptions =3D transformRelOptions((Datum) 0, -=09=09=09=09=09=09=09=09=09 stmt->options, -=09=09=09=09=09=09=09=09=09 NULL, NULL, false, false); -=09(void) tablespace_reloptions(newOptions, true); + +=09newOptions =3D transformOptions( +=09=09=09=09=09=09=09=09 get_tablespace_options_catalog(), +=09=09=09=09=09=09=09=09 (Datum) 0, stmt->options, 0); + =09if (newOptions !=3D (Datum) 0) =09=09values[Anum_pg_tablespace_spcoptions - 1] =3D newOptions; =09else @@ -1023,10 +1024,13 @@ AlterTableSpaceOptions(AlterTableSpaceOptionsSt= mt *stmt) =09/* Generate new proposed spcoptions (text array) */ =09datum =3D heap_getattr(tup, Anum_pg_tablespace_spcoptions, =09=09=09=09=09=09 RelationGetDescr(rel), &isnull); -=09newOptions =3D transformRelOptions(isnull ? (Datum) 0 : datum, -=09=09=09=09=09=09=09=09=09 stmt->options, NULL, NULL, false, -=09=09=09=09=09=09=09=09=09 stmt->isReset); -=09(void) tablespace_reloptions(newOptions, true); + + +=09newOptions =3D transformOptions(get_tablespace_options_catalog(), +=09=09=09=09=09=09=09=09 isnull ? (Datum) 0 : datum, +=09=09=09=09=09=09=09=09 stmt->options, +=09=09=09=09=09=09=09=09 OPTIONS_PARSE_MODE_FOR_ALTER | +=09=09=09=09=09=09 (stmt->isReset ? OPTIONS_PARSE_MODE_FOR_RESET : 0))= ; =20 =09/* Build new tuple. */ =09memset(repl_null, false, sizeof(repl_null)); diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreig= n.c index a113bf5..eb564fd 100644 --- a/src/backend/foreign/foreign.c +++ b/src/backend/foreign/foreign.c @@ -62,7 +62,7 @@ GetForeignDataWrapper(Oid fdwid) =09if (isnull) =09=09fdw->options =3D NIL; =09else -=09=09fdw->options =3D untransformRelOptions(datum); +=09=09fdw->options =3D optionsTextArrayToDefList(datum); =20 =09ReleaseSysCache(tp); =20 @@ -133,7 +133,7 @@ GetForeignServer(Oid serverid) =09if (isnull) =09=09server->options =3D NIL; =09else -=09=09server->options =3D untransformRelOptions(datum); +=09=09server->options =3D optionsTextArrayToDefList(datum); =20 =09ReleaseSysCache(tp); =20 @@ -201,7 +201,7 @@ GetUserMapping(Oid userid, Oid serverid) =09if (isnull) =09=09um->options =3D NIL; =09else -=09=09um->options =3D untransformRelOptions(datum); +=09=09um->options =3D optionsTextArrayToDefList(datum); =20 =09ReleaseSysCache(tp); =20 @@ -238,7 +238,7 @@ GetForeignTable(Oid relid) =09if (isnull) =09=09ft->options =3D NIL; =09else -=09=09ft->options =3D untransformRelOptions(datum); +=09=09ft->options =3D optionsTextArrayToDefList(datum); =20 =09ReleaseSysCache(tp); =20 @@ -271,7 +271,7 @@ GetForeignColumnOptions(Oid relid, AttrNumber attnu= m) =09if (isnull) =09=09options =3D NIL; =09else -=09=09options =3D untransformRelOptions(datum); +=09=09options =3D optionsTextArrayToDefList(datum); =20 =09ReleaseSysCache(tp); =20 @@ -540,7 +540,7 @@ pg_options_to_table(PG_FUNCTION_ARGS) =09Datum=09=09array =3D PG_GETARG_DATUM(0); =20 =09deflist_to_tuplestore((ReturnSetInfo *) fcinfo->resultinfo, -=09=09=09=09=09=09 untransformRelOptions(array)); +=09=09=09=09=09=09 optionsTextArrayToDefList(array)); =20 =09return (Datum) 0; } @@ -611,7 +611,7 @@ is_conninfo_option(const char *option, Oid context)= Datum postgresql_fdw_validator(PG_FUNCTION_ARGS) { -=09List=09 *options_list =3D untransformRelOptions(PG_GETARG_DATUM(0= )); +=09List=09 *options_list =3D optionsTextArrayToDefList(PG_GETARG_DAT= UM(0)); =09Oid=09=09=09catalog =3D PG_GETARG_OID(1); =20 =09ListCell *cell; diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimiz= er/util/plancat.c index dc0b0b0..0bf8492 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -140,7 +140,7 @@ get_relation_info(PlannerInfo *root, Oid relationOb= jectId, bool inhparent, =09=09=09=09=09=09 &rel->pages, &rel->tuples, &rel->allvisfrac); =20 =09/* Retrieve the parallel_workers reloption, or -1 if not set. */ -=09rel->rel_parallel_workers =3D RelationGetParallelWorkers(relation, = -1); +=09rel->rel_parallel_workers =3D HeapGetParallelWorkers(relation, -1);= =20 =09/* =09 * 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/par= se_clause.c index 9ff80b8..35e8008 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -3148,7 +3148,7 @@ transformOnConflictArbiter(ParseState *pstate, =09=09=09=09=09=09=09=09=09exprLocation((Node *) onConflictClause))));= =20 =09/* Same applies to table used by logical decoding as catalog table = */ -=09if (RelationIsUsedAsCatalogTable(pstate->p_target_relation)) +=09if (HeapIsUsedAsCatalogTable(pstate->p_target_relation)) =09=09ereport(ERROR, =09=09=09=09(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), =09=09=09=09 errmsg("ON CONFLICT is not supported on table \"%s\" used= as a catalog table", diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/pa= rse_utilcmd.c index ee5f3a3..b528068 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -1520,7 +1520,7 @@ generateClonedIndexStmt(CreateStmtContext *cxt, R= elation source_idx, =09datum =3D SysCacheGetAttr(RELOID, ht_idxrel, =09=09=09=09=09=09=09Anum_pg_class_reloptions, &isnull); =09if (!isnull) -=09=09index->options =3D untransformRelOptions(datum); +=09=09index->options =3D optionsTextArrayToDefList(datum); =20 =09/* If it's a partial index, decompile and append the predicate */ =09datum =3D SysCacheGetAttr(INDEXRELID, ht_idx, diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmast= er/autovacuum.c index 00b1e82..506748d 100644 --- a/src/backend/postmaster/autovacuum.c +++ b/src/backend/postmaster/autovacuum.c @@ -2736,6 +2736,7 @@ extract_autovac_opts(HeapTuple tup, TupleDesc pg_= class_desc) { =09bytea=09 *relopts; =09AutoVacOpts *av; +=09AutoVacOpts *src; =20 =09Assert(((Form_pg_class) GETSTRUCT(tup))->relkind =3D=3D RELKIND_REL= ATION || =09=09 ((Form_pg_class) GETSTRUCT(tup))->relkind =3D=3D RELKIND_MATV= IEW || @@ -2745,8 +2746,13 @@ extract_autovac_opts(HeapTuple tup, TupleDesc pg= _class_desc) =09if (relopts =3D=3D NULL) =09=09return NULL; =20 +=09if (((Form_pg_class) GETSTRUCT(tup))->relkind =3D=3D RELKIND_TOASTV= ALUE) +=09=09src =3D &(((ToastOptions *) relopts)->autovacuum); +=09else +=09=09src =3D &(((HeapOptions *) relopts)->autovacuum); + =09av =3D palloc(sizeof(AutoVacOpts)); -=09memcpy(av, &(((StdRdOptions *) relopts)->autovacuum), sizeof(AutoVa= cOpts)); +=09memcpy(av, src, sizeof(AutoVacOpts)); =09pfree(relopts); =20 =09return av; diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite= /rewriteHandler.c index 6225216..f822968 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -1531,7 +1531,7 @@ ApplyRetrieveRule(Query *parsetree, =20 =09rte->rtekind =3D RTE_SUBQUERY; =09rte->relid =3D InvalidOid; -=09rte->security_barrier =3D RelationIsSecurityView(relation); +=09rte->security_barrier =3D ViewIsSecurityView(relation); =09rte->subquery =3D rule_action; =09rte->inh =3D false;=09=09=09/* must not be set for a subquery */ =20 @@ -3035,7 +3035,7 @@ rewriteTargetView(Query *parsetree, Relation view= ) =20 =09=09ChangeVarNodes(viewqual, base_rt_index, new_rt_index, 0); =20 -=09=09if (RelationIsSecurityView(view)) +=09=09if (ViewIsSecurityView(view)) =09=09{ =09=09=09/* =09=09=09 * The view's quals go in front of existing barrier quals: th= ose @@ -3071,8 +3071,9 @@ rewriteTargetView(Query *parsetree, Relation view= ) =09 */ =09if (parsetree->commandType !=3D CMD_DELETE) =09{ -=09=09bool=09=09has_wco =3D RelationHasCheckOption(view); -=09=09bool=09=09cascaded =3D RelationHasCascadedCheckOption(view); + +=09=09bool=09=09has_wco =3D ViewHasCheckOption(view); +=09=09bool=09=09cascaded =3D ViewHasCascadedCheckOption(view); =20 =09=09/* =09=09 * If the parent view has a cascaded check option, treat this vi= ew as diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index ddacac8..19dcbb1 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -1003,6 +1003,8 @@ ProcessUtilitySlow(ParseState *pstate, =09=09=09=09=09=09{ =09=09=09=09=09=09=09Datum=09=09toast_options; =09=09=09=09=09=09=09static char *validnsps[] =3D HEAP_RELOPT_NAMESPAC= ES; +=09=09=09=09=09=09=09List=09 *toastDefList; +=09=09=09=09=09=09=09bool=09=09success; =20 =09=09=09=09=09=09=09/* Create the table itself */ =09=09=09=09=09=09=09address =3D DefineRelation((CreateStmt *) stmt, @@ -1023,18 +1025,24 @@ ProcessUtilitySlow(ParseState *pstate, =09=09=09=09=09=09=09 * parse and validate reloptions for the toast =09=09=09=09=09=09=09 * table =09=09=09=09=09=09=09 */ -=09=09=09=09=09=09=09toast_options =3D transformRelOptions((Datum) 0, -=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09((CreateStmt *) stmt)-= >options, -=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09"toast", -=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09validnsps, -=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09true, -=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09false); -=09=09=09=09=09=09=09(void) heap_reloptions(RELKIND_TOASTVALUE, -=09=09=09=09=09=09=09=09=09=09=09=09 toast_options, -=09=09=09=09=09=09=09=09=09=09=09=09 true); =20 -=09=09=09=09=09=09=09NewRelationCreateToastTable(address.objectId, -=09=09=09=09=09=09=09=09=09=09=09=09=09=09toast_options); +=09=09=09=09=09=09=09optionsDefListValdateNamespaces( +=09=09=09=09=09=09=09=09=09=09=09 ((CreateStmt *) stmt)->options, +=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09validnsps); + +=09=09=09=09=09=09=09toastDefList =3D optionsDefListFilterNamespaces( +=09=09=09=09=09=09=09=09=09((CreateStmt *) stmt)->options, "toast"); + +=09=09=09=09=09=09=09toast_options =3D transformOptions( +=09=09=09=09=09=09=09=09=09 get_toast_relopt_catalog(), (Datum) 0, +=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09 toastDefList, 0); + +=09=09=09=09=09=09=09success =3D NewRelationCreateToastTable( +=09=09=09=09=09=09=09=09=09=09=09address.objectId, toast_options); +=09=09=09=09=09=09=09if (!success && toast_options) +=09=09=09=09=09=09=09=09ereport(ERROR, +=09=09=09=09=09=09=09=09 (errcode(ERRCODE_INVALID_PARAMETER_VALUE), +=09=09=09=09=09=09=09=09=09errmsg("no TOAST relation was created for a= table. Can't set toast.* storage parameters"))); =09=09=09=09=09=09} =09=09=09=09=09=09else if (IsA(stmt, CreateForeignTableStmt)) =09=09=09=09=09=09{ @@ -1101,8 +1109,13 @@ ProcessUtilitySlow(ParseState *pstate, =09=09=09=09=09 * lock on (for example) a relation on which we have no= =09=09=09=09=09 * permissions. =09=09=09=09=09 */ -=09=09=09=09=09lockmode =3D AlterTableGetLockLevel(atstmt->cmds); -=09=09=09=09=09relid =3D AlterTableLookupRelation(atstmt, lockmode); + +=09=09=09=09=09relid =3D AlterTableLookupRelation(atstmt, NoLock); +=09=09=09=09=09if (OidIsValid(relid)) +=09=09=09=09=09{ +=09=09=09=09=09=09lockmode =3D AlterTableGetLockLevel(relid, atstmt->c= mds); +=09=09=09=09=09=09relid =3D AlterTableLookupRelation(atstmt, lockmode)= ; +=09=09=09=09=09} =20 =09=09=09=09=09if (OidIsValid(relid)) =09=09=09=09=09{ diff --git a/src/backend/utils/cache/attoptcache.c b/src/backend/utils/= cache/attoptcache.c index da8b42d..e8c4309 100644 --- a/src/backend/utils/cache/attoptcache.c +++ b/src/backend/utils/cache/attoptcache.c @@ -149,7 +149,10 @@ get_attribute_options(Oid attrelid, int attnum) =09=09=09=09opts =3D NULL; =09=09=09else =09=09=09{ -=09=09=09=09bytea=09 *bytea_opts =3D attribute_reloptions(datum, fal= se); +=09=09=09=09bytea=09 *bytea_opts; + +=09=09=09=09bytea_opts =3D optionsTextArrayToBytea( +=09=09=09=09=09=09=09=09=09 get_attribute_options_catalog(), datum); =20 =09=09=09=09opts =3D MemoryContextAlloc(CacheMemoryContext, =09=09=09=09=09=09=09=09=09=09 VARSIZE(bytea_opts)); diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cac= he/relcache.c index 43238dd..50a2566 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -460,7 +460,8 @@ RelationParseRelOptions(Relation relation, HeapTupl= e tuple) =09options =3D extractRelOptions(tuple, =09=09=09=09=09=09=09=09GetPgClassDescriptor(), =09=09=09=09=09=09=09=09relation->rd_rel->relkind =3D=3D RELKIND_INDEX= ? -=09=09=09=09=09=09=09=09relation->rd_amroutine->amoptions : NULL); +=09=09=09=09=09=09=09 relation->rd_amroutine->amrelopt_catalog : NULL= +=09=09); =20 =09/* =09 * Copy parsed data into CacheMemoryContext. To guard against the diff --git a/src/backend/utils/cache/spccache.c b/src/backend/utils/cac= he/spccache.c index eafc00f..35db485 100644 --- a/src/backend/utils/cache/spccache.c +++ b/src/backend/utils/cache/spccache.c @@ -149,7 +149,10 @@ get_tablespace(Oid spcid) =09=09=09opts =3D NULL; =09=09else =09=09{ -=09=09=09bytea=09 *bytea_opts =3D tablespace_reloptions(datum, false= ); +=09=09=09bytea=09 *bytea_opts; + +=09=09=09bytea_opts =3D optionsTextArrayToBytea( +=09=09=09=09=09=09=09=09=09get_tablespace_options_catalog(), datum); =20 =09=09=09opts =3D MemoryContextAlloc(CacheMemoryContext, VARSIZE(bytea= _opts)); =09=09=09memcpy(opts, bytea_opts, VARSIZE(bytea_opts)); @@ -194,7 +197,6 @@ get_tablespace_page_costs(Oid spcid, =09=09else =09=09=09*spc_random_page_cost =3D spc->opts->random_page_cost; =09} - =09if (spc_seq_page_cost) =09{ =09=09if (!spc->opts || spc->opts->seq_page_cost < 0) diff --git a/src/include/access/amapi.h b/src/include/access/amapi.h index 0db4fc7..b226dc7 100644 --- a/src/include/access/amapi.h +++ b/src/include/access/amapi.h @@ -98,10 +98,6 @@ typedef void (*amcostestimate_function) (struct Plan= nerInfo *root, =09=09=09=09=09=09=09=09=09=09 double *indexCorrelation, =09=09=09=09=09=09=09=09=09=09 double *indexPages); =20 -/* parse index reloptions */ -typedef bytea *(*amoptions_function) (Datum reloptions, -=09=09=09=09=09=09=09=09=09 bool validate); - /* report AM, index, or index column property */ typedef bool (*amproperty_function) (Oid index_oid, int attno, =09=09=09=09=09=09=09=09=09 IndexAMProperty prop, const char *propname= , @@ -139,6 +135,9 @@ typedef void (*ammarkpos_function) (IndexScanDesc s= can); /* 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 =09amvacuumcleanup_function amvacuumcleanup; =09amcanreturn_function amcanreturn;=09/* can be NULL */ =09amcostestimate_function amcostestimate; -=09amoptions_function amoptions; =09amproperty_function amproperty; /* can be NULL */ =09amvalidate_function amvalidate; =09ambeginscan_function ambeginscan; @@ -212,6 +210,7 @@ typedef struct IndexAmRoutine =09amendscan_function amendscan; =09ammarkpos_function ammarkpos;=09/* can be NULL */ =09amrestrpos_function amrestrpos; /* can be NULL */ +=09amrelopt_catalog_function amrelopt_catalog; /* can be NULL */ =20 =09/* interface functions to support parallel index scans */ =09amestimateparallelscan_function amestimateparallelscan; /* can be N= ULL */ diff --git a/src/include/access/brin.h b/src/include/access/brin.h index 61a3880..d65ad4e 100644 --- a/src/include/access/brin.h +++ b/src/include/access/brin.h @@ -37,6 +37,9 @@ typedef struct BrinStatsData =20 =20 #define BRIN_DEFAULT_PAGES_PER_RANGE=09128 +#define BRIN_MIN_PAGES_PER_RANGE=09=091 +#define BRIN_MAX_PAGES_PER_RANGE=09=09131072 + #define BrinGetPagesPerRange(relation) \ =09((relation)->rd_options ? \ =09 ((BrinOptions *) (relation)->rd_options)->pagesPerRange : \ diff --git a/src/include/access/brin_internal.h b/src/include/access/br= in_internal.h index 3ed6743..00eccac 100644 --- a/src/include/access/brin_internal.h +++ b/src/include/access/brin_internal.h @@ -102,7 +102,6 @@ extern IndexBulkDeleteResult *brinbulkdelete(IndexV= acuumInfo *info, =09=09=09 void *callback_state); extern IndexBulkDeleteResult *brinvacuumcleanup(IndexVacuumInfo *info,= =09=09=09=09 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_= private.h index adfdb0c..451b372 100644 --- a/src/include/access/gin_private.h +++ b/src/include/access/gin_private.h @@ -84,7 +84,7 @@ typedef struct GinState =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,= OffsetNumber attnum, extern OffsetNumber gintuple_get_attrnum(GinState *ginstate, IndexTupl= e tuple); extern Datum gintuple_get_key(GinState *ginstate, IndexTuple tuple, =09=09=09=09 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/gis= t_private.h index bfef2df..6a9090e 100644 --- a/src/include/access/gist_private.h +++ b/src/include/access/gist_private.h @@ -367,6 +367,24 @@ typedef struct GISTBuildBuffers =09int=09=09=09rootlevel; } 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 { \ +=09"on",=09=09=09=09=09=09=09=09=09\ +=09"off",=09=09=09=09=09=09=09=09=09\ +=09"auto",=09=09=09=09=09=09=09=09=09\ +=09(const char *) NULL=09=09=09=09=09=09\ +} +typedef enum gist_option_buffering_value_numbers +{ +=09GIST_OPTION_BUFFERING_ON =3D 0, +=09GIST_OPTION_BUFFERING_OFF =3D 1, +=09GIST_OPTION_BUFFERING_AUTO =3D 2, +}=09gist_option_buffering_value_numbers; + /* * Storage type for GiST's reloptions */ @@ -374,7 +392,7 @@ typedef struct GiSTOptions { =09int32=09=09vl_len_;=09=09/* varlena header (do not touch directly!)= */ =09int=09=09=09fillfactor;=09=09/* page fill factor in percent (0..100= ) */ -=09int=09=09=09bufferingModeOffset;=09/* use buffering build? */ +=09int=09=09=09buffering_mode; /* use buffering build? */ } GiSTOptions; =20 /* gist.c */ @@ -435,7 +453,6 @@ extern bool gistvalidate(Oid opclassoid); #define GIST_MIN_FILLFACTOR=09=09=0910 #define GIST_DEFAULT_FILLFACTOR=09=0990 =20 -extern bytea *gistoptions(Datum reloptions, bool validate); extern bool gistproperty(Oid index_oid, int attno, =09=09=09 IndexAMProperty prop, const char *propname, =09=09=09 bool *res, bool *isnull); @@ -485,6 +502,7 @@ extern void gistMakeUnionKey(GISTSTATE *giststate, = int attno, =09=09=09=09 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, I= ndexTuple *itup, /* gistbuild.c */ extern IndexBuildResult *gistbuild(Relation heap, Relation index, =09=09 struct IndexInfo *indexInfo); -extern void gistValidateBufferingOption(char *value); =20 /* gistbuildbuffers.c */ extern GISTBuildBuffers *gistInitBuildBuffers(int pagesPerBuffer, int = levelStep, diff --git a/src/include/access/hash.h b/src/include/access/hash.h index 7fa868b..4c3cc38 100644 --- a/src/include/access/hash.h +++ b/src/include/access/hash.h @@ -224,6 +224,19 @@ typedef struct HashMetaPageData =20 typedef HashMetaPageData *HashMetaPage; =20 + +typedef struct HashRelOptions +{ +=09int32=09=09varlena_header_;=09/* varlena header (do not touch direc= tly!) */ +=09int=09=09=09fillfactor;=09=09/* page fill factor in percent (0..100= ) */ +}=09HashRelOptions; + + +#define HashGetFillFactor(relation) \ +=09((relation)->rd_options ? \ +=09=09((HashRelOptions *) (relation)->rd_options)->fillfactor : \ +=09=09HASH_DEFAULT_FILLFACTOR) + /* * Maximum size of a hash index item (it's okay to have only one per p= age) */ @@ -319,7 +332,6 @@ extern IndexBulkDeleteResult *hashbulkdelete(IndexV= acuumInfo *info, =09=09=09 void *callback_state); extern IndexBulkDeleteResult *hashvacuumcleanup(IndexVacuumInfo *info,= =09=09=09=09 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 ke= ylen); @@ -422,4 +434,6 @@ extern void hashbucketcleanup(Relation rel, Bucket = cur_bucket, =09=09=09=09 bool bucket_has_garbage, =09=09=09=09 IndexBulkDeleteCallback callback, void *callback_state);= =20 +extern void *hashgetreloptcatalog(void); + #endif=09=09=09=09=09=09=09/* HASH_H */ diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h index e6abbec..9bbf426 100644 --- a/src/include/access/nbtree.h +++ b/src/include/access/nbtree.h @@ -427,6 +427,17 @@ typedef BTScanOpaqueData *BTScanOpaque; #define SK_BT_DESC=09=09=09(INDOPTION_DESC << SK_BT_INDOPTION_SHIFT) #define SK_BT_NULLS_FIRST=09(INDOPTION_NULLS_FIRST << SK_BT_INDOPTION_= SHIFT) =20 +typedef struct BTRelOptions +{ +=09int32=09=09varlena_header_;=09/* varlena header (do not touch direc= tly!) */ +=09int=09=09=09fillfactor;=09=09/* page fill factor in percent (0..100= ) */ +}=09BTRelOptions; + +#define BTGetFillFactor(relation) \ +=09((relation)->rd_options ? \ +=09=09((BTRelOptions *) (relation)->rd_options)->fillfactor : \ +=09=09BTREE_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, =09=09 IndexAMProperty prop, const char *propname, =09=09 bool *res, bool *isnull); diff --git a/src/include/access/options.h b/src/include/access/options.= h new file mode 100644 index 0000000..8c1c03c --- /dev/null +++ b/src/include/access/options.h @@ -0,0 +1,225 @@ +/*--------------------------------------------------------------------= ----- + * + * options.h + *=09 Core support for relation and tablespace options (pg_class.relo= ptions + *=09 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 Gro= up + * Portions Copyright (c) 1994, Regents of the University of Californi= a + * + * 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 +{ +=09OPTION_TYPE_BOOL, +=09OPTION_TYPE_INT, +=09OPTION_TYPE_REAL, +=09OPTION_TYPE_ENUM, +=09OPTION_TYPE_STRING +}=09option_type; + +typedef enum option_value_status +{ +=09OPTION_VALUE_STATUS_EMPTY,=09/* Option was just initialized */ +=09OPTION_VALUE_STATUS_RAW,=09/* Option just came from syntax analyzer= in +=09=09=09=09=09=09=09=09 * has name, and raw (unparsed) value */ +=09OPTION_VALUE_STATUS_PARSED, /* Option was parsed and has link to ca= talog +=09=09=09=09=09=09=09=09 * entry and proper value */ +=09OPTION_VALUE_STATUS_FOR_RESET=09=09/* This option came from ALTER x= xx +=09=09=09=09=09=09=09=09=09=09 * RESET */ +}=09option_value_status; + +/* flags for reloptinon definition */ +typedef enum option_definition_flags +{ +=09OPTION_DEFINITION_FLAG_FORBID_ALTER =3D (1 << 0),=09=09/* Altering = this option +=09=09=09=09=09=09=09=09=09=09=09=09=09=09 * is forbidden */ +=09OPTION_DEFINITION_FLAG_IGNORE =3D (1 << 1),=09/* Skip this option w= hile +=09=09=09=09=09=09=09=09=09=09=09=09 * parsing. Used for WITH OIDS +=09=09=09=09=09=09=09=09=09=09=09=09 * special case */ +=09OPTION_DEFINITION_FLAG_REJECT =3D (1 << 2)=09/* Option will be reje= cted +=09=09=09=09=09=09=09=09=09=09=09=09 * when comes from syntax +=09=09=09=09=09=09=09=09=09=09=09=09 * analyzer, but still have +=09=09=09=09=09=09=09=09=09=09=09=09 * default value and offset */ +} option_definition_flags; + +/* flags that tells reloption parser how to parse*/ +typedef enum options_parse_mode +{ +=09OPTIONS_PARSE_MODE_VALIDATE =3D (1 << 0), +=09OPTIONS_PARSE_MODE_FOR_ALTER =3D (1 << 1), +=09OPTIONS_PARSE_MODE_FOR_RESET =3D (1 << 2) +} options_parse_mode; + + +/* generic struct to hold shared data */ +typedef struct option_definition_basic +{ +=09const char *name;=09=09=09/* must be first (used as list terminatio= n +=09=09=09=09=09=09=09=09 * marker) */ +=09const char *desc; +=09LOCKMODE=09lockmode; +=09option_definition_flags flags; +=09option_type type; +=09int=09=09=09struct_offset;=09/* offset of the value in Bytea repres= entation */ +}=09option_definition_basic; + +/* holds a parsed value */ +typedef struct option_value +{ +=09option_definition_basic *gen; +=09char=09 *namespace; +=09option_value_status status; +=09char=09 *raw_value;=09=09/* allocated separately */ +=09char=09 *raw_name; +=09union +=09{ +=09=09bool=09=09bool_val; +=09=09int=09=09=09int_val; +=09=09double=09=09real_val; +=09=09int=09=09=09enum_val; +=09=09char=09 *string_val; /* allocated separately */ +=09}=09=09=09values; +}=09option_value; + +/* reloptions records for specific variable types */ +typedef struct option_definition_bool +{ +=09option_definition_basic base; +=09bool=09=09default_val; +}=09option_definition_bool; + +typedef struct option_definition_int +{ +=09option_definition_basic base; +=09int=09=09=09default_val; +=09int=09=09=09min; +=09int=09=09=09max; +}=09option_definition_int; + +typedef struct option_definition_real +{ +=09option_definition_basic base; +=09double=09=09default_val; +=09double=09=09min; +=09double=09=09max; +}=09option_definition_real; + +typedef struct option_definition_enum +{ +=09option_definition_basic base; +=09const char **allowed_values;/* Null terminated array of allowed val= ues for +=09=09=09=09=09=09=09=09 * the option */ +=09int=09=09=09default_val;=09/* Number of item of allowed_values arra= y */ +}=09option_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 def= ault + * 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 relpt= ions + * can deal this case. For better readability it was defined as a cons= tant. + */ +#define OPTION_STRING_VALUE_NOT_SET_OFFSET -1 + +typedef struct option_definition_string +{ +=09option_definition_basic base; +=09validate_string_relopt validate_cb; +=09char=09 *default_val; +}=09option_definition_string; + +typedef void (*postprocess_bytea_options_function) (void *data, bool v= alidate); + +typedef struct options_catalog +{ +=09option_definition_basic **definitions; +=09int=09=09=09num;=09=09=09/* Number of catalog items in use */ +=09int=09=09=09num_allocated;=09/* Number of catalog items allocated *= / +=09bool=09=09forbid_realloc;=09/* If number of items of the catalog we= re +=09=09=09=09=09=09=09=09=09* strictly set to certain value +=09=09=09=09=09=09=09=09=09* do no allow adding more idems */ +=09Size=09=09struct_size;=09/* Size of a structure for options in bina= ry +=09=09=09=09=09=09=09=09 * representation */ +=09postprocess_bytea_options_function postprocess_fun; /* This functio= n is +=09=09=09=09=09=09=09=09=09=09=09=09=09=09 * called after options +=09=09=09=09=09=09=09=09=09=09=09=09=09=09 * were converted in +=09=09=09=09=09=09=09=09=09=09=09=09=09=09 * Bytea represenation. +=09=09=09=09=09=09=09=09=09=09=09=09=09=09 * Can be used for extra +=09=09=09=09=09=09=09=09=09=09=09=09=09=09 * validation and so on */ +=09char=09 *namespace;=09=09/* Catalog is used for options from this= +=09=09=09=09=09=09=09=09 * namespase */ +}=09options_catalog; + + +/* + * Options catalog related functions + */ +extern options_catalog *allocateOptionsCatalog(char *namespace, int si= ze_of_bytea, +=09=09=09int num_items_expected); +extern void optionsCatalogAddItemBool(options_catalog * catalog, char = *name, +=09=09=09=09char *desc, LOCKMODE lockmode, option_definition_flags fla= gs, +=09=09=09=09=09=09 int struct_offset, bool default_val); +extern void optionsCatalogAddItemInt(options_catalog * catalog, char *= name, +=09=09=09=09char *desc, LOCKMODE lockmode, option_definition_flags fla= gs, +=09=09=09 int struct_offset, int default_val, int min_val, int max_v= al); +extern void optionsCatalogAddItemReal(options_catalog * catalog, char = *name, +=09=09=09=09char *desc, LOCKMODE lockmode, option_definition_flags fla= gs, +=09 int struct_offset, double default_val, double min_val, double max= _val); +extern void optionsCatalogAddItemEnum(options_catalog * catalog, +=09char *name, char *desc, LOCKMODE lockmode, option_definition_flags = flags, +=09=09=09int struct_offset, const char **allowed_values, int default_v= al); +extern void optionsCatalogAddItemString(options_catalog * catalog, cha= r *name, +=09=09=09=09char *desc, LOCKMODE lockmode, option_definition_flags fla= gs, +=09 int struct_offset, char *default_val, validate_string_relopt valid= ator); + + +/* + * This macro allows to get string option value from bytea representat= ion. + * "optstruct" - is a structure that is stored in bytea options repres= entation + * "member" - member of this structure that has string option value + * (actually string values are stored in bytea after the structure, an= d + * and "member" will contain an offset to this value. This macro do al= l + * the math + */ +#define GET_STRING_OPTION(optstruct, member) \ +=09((optstruct)->member =3D=3D OPTION_STRING_VALUE_NOT_SET_OFFSET ? NU= LL : \ +=09 (char *)(optstruct) + (optstruct)->member) + +/* + * Functions related to option convertation, parsing, manipulation + * and validation + */ +extern void optionsDefListValdateNamespaces(List *defList, +=09=09=09=09=09=09=09=09char **allowed_namespaces); +extern List *optionsDefListFilterNamespaces(List *defList, char *names= pace); +extern List *optionsTextArrayToDefList(Datum options); + +/* + * Meta functions that uses functions above to get options for relatio= ns, + * tablespaces, views and so on + */ + +extern bytea *optionsTextArrayToBytea(options_catalog * catalog, Datum= data); +extern Datum transformOptions(options_catalog * catalog, Datum oldOpti= ons, +=09=09=09=09 List *defList, options_parse_mode parse_mode); + +#endif /* OPTIONS_H */ diff --git a/src/include/access/reloptions.h b/src/include/access/relop= tions.h index 5cdaa3b..396263f 100644 --- a/src/include/access/reloptions.h +++ b/src/include/access/reloptions.h @@ -1,13 +1,9 @@ /*--------------------------------------------------------------------= ----- * * reloptions.h - *=09 Core support for relation and tablespace options (pg_class.relo= ptions + *=09 Support for relation and tablespace options (pg_class.reloption= s *=09 and pg_tablespace.spcoptions) * - * Note: the functions dealing with text-array reloptions values decla= re - * 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 Gro= up * Portions Copyright (c) 1994, Regents of the University of Californi= a @@ -20,264 +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 -{ -=09RELOPT_TYPE_BOOL, -=09RELOPT_TYPE_INT, -=09RELOPT_TYPE_REAL, -=09RELOPT_TYPE_STRING -} relopt_type; +#include "access/options.h" =20 -/* kinds supported by reloptions */ -typedef enum relopt_kind -{ -=09RELOPT_KIND_HEAP =3D (1 << 0), -=09RELOPT_KIND_TOAST =3D (1 << 1), -=09RELOPT_KIND_BTREE =3D (1 << 2), -=09RELOPT_KIND_HASH =3D (1 << 3), -=09RELOPT_KIND_GIN =3D (1 << 4), -=09RELOPT_KIND_GIST =3D (1 << 5), -=09RELOPT_KIND_ATTRIBUTE =3D (1 << 6), -=09RELOPT_KIND_TABLESPACE =3D (1 << 7), -=09RELOPT_KIND_SPGIST =3D (1 << 8), -=09RELOPT_KIND_VIEW =3D (1 << 9), -=09RELOPT_KIND_BRIN =3D (1 << 10), -=09RELOPT_KIND_PARTITIONED =3D (1 << 11), -=09/* if you add a new kind, make sure you update "last_default" too *= / -=09RELOPT_KIND_LAST_DEFAULT =3D RELOPT_KIND_PARTITIONED, -=09/* some compilers treat enums as signed ints, so we can't use 1 << = 31 */ -=09RELOPT_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 -{ -=09const char *name;=09=09=09/* must be first (used as list terminatio= n -=09=09=09=09=09=09=09=09 * marker) */ -=09const char *desc; -=09bits32=09=09kinds; -=09LOCKMODE=09lockmode; -=09int=09=09=09namelen; -=09relopt_type type; -} relopt_gen; - -/* holds a parsed value */ -typedef struct relopt_value -{ -=09relopt_gen *gen; -=09bool=09=09isset; -=09union -=09{ -=09=09bool=09=09bool_val; -=09=09int=09=09=09int_val; -=09=09double=09=09real_val; -=09=09char=09 *string_val; /* allocated separately */ -=09}=09=09=09values; -} relopt_value; - -/* reloptions records for specific variable types */ -typedef struct relopt_bool -{ -=09relopt_gen=09gen; -=09bool=09=09default_val; -} relopt_bool; - -typedef struct relopt_int -{ -=09relopt_gen=09gen; -=09int=09=09=09default_val; -=09int=09=09=09min; -=09int=09=09=09max; -} relopt_int; - -typedef struct relopt_real -{ -=09relopt_gen=09gen; -=09double=09=09default_val; -=09double=09=09min; -=09double=09=09max; -} relopt_real; - -/* validation routines for strings */ -typedef void (*validate_string_relopt) (char *value); - -typedef struct relopt_string -{ -=09relopt_gen=09gen; -=09int=09=09=09default_len; -=09bool=09=09default_isnull; -=09validate_string_relopt validate_cb; -=09char=09 *default_val; -} relopt_string; - -/* This is the table datatype for fillRelOptions */ -typedef struct -{ -=09const char *optname;=09=09/* option's name */ -=09relopt_type opttype;=09=09/* option's datatype */ -=09int=09=09=09offset;=09=09=09/* offset of field in result struct */ -} relopt_parse_elt; - - -/* - * These macros exist for the convenience of amoptions writers (but co= nsider - * using fillRelOptions, which is a lot simpler). Beware of multiple - * evaluation of arguments! - * - * The last argument in the HANDLE_*_RELOPTION macros allows the calle= r 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 d= oes 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 ret= urned by - * parseRelOptions: - * for (i =3D 0; options[i].gen->name; i++) - * { - *=09=09if (HAVE_RELOPTION("fillfactor", options[i]) - *=09=09{ - *=09=09=09HANDLE_INT_RELOPTION("fillfactor", rdopts->fillfactor, opti= ons[i], &isset); - *=09=09=09continue; - *=09=09} - *=09=09if (HAVE_RELOPTION("default_row_acl", options[i]) - *=09=09{ - *=09=09=09... - *=09=09} - *=09=09... - *=09=09if (validate) - *=09=09=09ereport(ERROR, - *=09=09=09=09=09(errmsg("unknown option"))); - *=09} - * - *=09Note that this is more or less the same that fillRelOptions does,= so only - *=09use this if you need to do something non-standard within some opt= ion's - *=09code block. - */ -#define HAVE_RELOPTION(optname, option) \ -=09(pg_strncasecmp(option.gen->name, optname, option.gen->namelen + 1)= =3D=3D 0) - -#define HANDLE_INT_RELOPTION(optname, var, option, wasset)=09=09\ -=09do {=09=09=09=09=09=09=09=09=09=09=09=09=09=09\ -=09=09if (option.isset)=09=09=09=09=09=09=09=09=09=09\ -=09=09=09var =3D option.values.int_val;=09=09=09=09=09=09\ -=09=09else=09=09=09=09=09=09=09=09=09=09=09=09=09\ -=09=09=09var =3D ((relopt_int *) option.gen)->default_val;=09=09\ -=09=09(wasset) !=3D NULL ? *(wasset) =3D option.isset : (dummyret)NULL= ; \ -=09} while (0) - -#define HANDLE_BOOL_RELOPTION(optname, var, option, wasset)=09=09=09\ -=09do {=09=09=09=09=09=09=09=09=09=09=09=09=09=09=09\ -=09=09if (option.isset)=09=09=09=09=09=09=09=09=09=09\ -=09=09=09var =3D option.values.bool_val;=09=09=09=09=09=09\ -=09=09else=09=09=09=09=09=09=09=09=09=09=09=09=09\ -=09=09=09var =3D ((relopt_bool *) option.gen)->default_val;=09\ -=09=09(wasset) !=3D NULL ? *(wasset) =3D option.isset : (dummyret) NUL= L; \ -=09} while (0) =20 -#define HANDLE_REAL_RELOPTION(optname, var, option, wasset)=09=09\ -=09do {=09=09=09=09=09=09=09=09=09=09=09=09=09=09\ -=09=09if (option.isset)=09=09=09=09=09=09=09=09=09=09\ -=09=09=09var =3D option.values.real_val;=09=09=09=09=09=09\ -=09=09else=09=09=09=09=09=09=09=09=09=09=09=09=09\ -=09=09=09var =3D ((relopt_real *) option.gen)->default_val;=09\ -=09=09(wasset) !=3D NULL ? *(wasset) =3D option.isset : (dummyret) NUL= L; \ -=09} while (0) - -/* - * Note that this assumes that the variable is already allocated at th= e tail of - * reloptions structure (StdRdOptions or equivalent). - * - * "base" is a pointer to the reloptions structure, and "offset" is an= integer - * variable that must be initialized to sizeof(reloptions structure). = This - * struct must have been allocated with enough space to hold any strin= g option - * present, including terminating \0 for every option. SET_VARSIZE() = must be - * called on the struct with this offset as the second argument, after= all the - * string options have been processed. - */ -#define HANDLE_STRING_RELOPTION(optname, var, option, base, offset, wa= sset) \ -=09do {=09=09=09=09=09=09=09=09=09=09=09=09=09=09\ -=09=09relopt_string *optstring =3D (relopt_string *) option.gen;\ -=09=09char *string_val;=09=09=09=09=09=09=09=09=09=09\ -=09=09if (option.isset)=09=09=09=09=09=09=09=09=09=09\ -=09=09=09string_val =3D option.values.string_val;=09=09=09=09\ -=09=09else if (!optstring->default_isnull)=09=09=09=09=09\ -=09=09=09string_val =3D optstring->default_val;=09=09=09=09\ -=09=09else=09=09=09=09=09=09=09=09=09=09=09=09=09\ -=09=09=09string_val =3D NULL;=09=09=09=09=09=09=09=09=09\ -=09=09(wasset) !=3D NULL ? *(wasset) =3D option.isset : (dummyret) NUL= L; \ -=09=09if (string_val =3D=3D NULL)=09=09=09=09=09=09=09=09=09\ -=09=09=09var =3D 0;=09=09=09=09=09=09=09=09=09=09=09\ -=09=09else=09=09=09=09=09=09=09=09=09=09=09=09=09\ -=09=09{=09=09=09=09=09=09=09=09=09=09=09=09=09=09\ -=09=09=09strcpy(((char *)(base)) + (offset), string_val);=09\ -=09=09=09var =3D (offset);=09=09=09=09=09=09=09=09=09=09\ -=09=09=09(offset) +=3D strlen(string_val) + 1;=09=09=09=09=09\ -=09=09}=09=09=09=09=09=09=09=09=09=09=09=09=09=09\ -=09} 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) \ -=09((option).isset ? strlen((option).values.string_val) : \ -=09 ((relopt_string *) (option).gen)->default_len) +extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, +=09=09=09=09 amrelopt_catalog_function catalog_fun); =20 /* - * For use by code reading options already parsed: get a pointer to th= e string - * value itself. "optstruct" is the StdRdOption struct or equivalent,= "member" - * is the struct member corresponding to the string option + * Functions that creates option catalog instances for heap, toast, vi= ew + * and other object types that are part of the postgres core */ -#define GET_STRING_RELOPTION(optstruct, member) \ -=09((optstruct)->member =3D=3D 0 ? NULL : \ -=09 (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, -=09=09=09=09 bool default_val); -extern void add_int_reloption(bits32 kinds, char *name, char *desc, -=09=09=09=09 int default_val, int min_val, int max_val); -extern void add_real_reloption(bits32 kinds, char *name, char *desc, -=09=09=09=09 double default_val, double min_val, double max_val); -extern void add_string_reloption(bits32 kinds, char *name, char *desc,= -=09=09=09=09=09 char *default_val, validate_string_relopt validator); - -extern Datum transformRelOptions(Datum oldOptions, List *defList, -=09=09=09=09=09char *namspace, char *validnsps[], -=09=09=09=09=09bool ignoreOids, bool isReset); -extern List *untransformRelOptions(Datum options); -extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, -=09=09=09=09 amoptions_function amoptions); -extern relopt_value *parseRelOptions(Datum options, bool validate, -=09=09=09=09relopt_kind kind, int *numrelopts); -extern void *allocateReloptStruct(Size base, relopt_value *options, -=09=09=09=09=09 int numoptions); -extern void fillRelOptions(void *rdopts, Size basesize, -=09=09=09 relopt_value *options, int numoptions, -=09=09=09 bool validate, -=09=09=09 const relopt_parse_elt *elems, int nelems); - -extern bytea *default_reloptions(Datum reloptions, bool validate, -=09=09=09=09 relopt_kind kind); -extern bytea *heap_reloptions(char relkind, Datum reloptions, bool val= idate); -extern bytea *view_reloptions(Datum reloptions, bool validate); -extern bytea *index_reloptions(amoptions_function amoptions, Datum rel= options, -=09=09=09=09 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 *d= efList); =20 #endif=09=09=09=09=09=09=09/* RELOPTIONS_H */ diff --git a/src/include/access/spgist.h b/src/include/access/spgist.h index d1bc396..780e0d7 100644 --- a/src/include/access/spgist.h +++ b/src/include/access/spgist.h @@ -20,10 +20,6 @@ #include "lib/stringinfo.h" =20 =20 -/* reloption parameters */ -#define SPGIST_MIN_FILLFACTOR=09=09=0910 -#define SPGIST_DEFAULT_FILLFACTOR=09=0980 - /* SPGiST opclass support function numbers */ #define SPGIST_CONFIG_PROC=09=09=09=091 #define SPGIST_CHOOSE_PROC=09=09=09=092 @@ -181,9 +177,6 @@ typedef struct spgLeafConsistentOut } spgLeafConsistentOut; =20 =20 -/* spgutils.c */ -extern bytea *spgoptions(Datum reloptions, bool validate); - /* spginsert.c */ extern IndexBuildResult *spgbuild(Relation heap, Relation index, =09=09 struct IndexInfo *indexInfo); diff --git a/src/include/access/spgist_private.h b/src/include/access/s= pgist_private.h index 1c4b321..f8c562a 100644 --- a/src/include/access/spgist_private.h +++ b/src/include/access/spgist_private.h @@ -21,6 +21,19 @@ #include "utils/relcache.h" =20 =20 +typedef struct SpGistRelOptions +{ +=09int32=09=09varlena_header_;=09/* varlena header (do not touch direc= tly!) */ +=09int=09=09=09fillfactor;=09=09/* page fill factor in percent (0..100= ) */ +}=09SpGistRelOptions; + + +#define SpGistGetFillFactor(relation) \ +=09((relation)->rd_options ? \ +=09=09((SpGistRelOptions *) (relation)->rd_options)->fillfactor : \ +=09=09SPGIST_DEFAULT_FILLFACTOR) + + /* Page numbers of fixed-location pages */ #define SPGIST_METAPAGE_BLKNO=09 (0)=09/* metapage */ #define SPGIST_ROOT_BLKNO=09=09 (1)=09/* root for normal entries */ @@ -380,6 +393,11 @@ typedef SpGistDeadTupleData *SpGistDeadTuple; #define GBUF_REQ_NULLS(flags)=09((flags) & GBUF_NULLS) =20 /* spgutils.c */ + +/* reloption parameters */ +#define SPGIST_MIN_FILLFACTOR=09=09=0910 +#define SPGIST_DEFAULT_FILLFACTOR=09=0980 + extern SpGistCache *spgGetCache(Relation index); extern void initSpGistState(SpGistState *state, Relation index); extern Buffer SpGistNewBuffer(Relation index); @@ -407,6 +425,7 @@ extern OffsetNumber SpGistPageAddNewItem(SpGistStat= e *state, Page page, =09=09=09=09=09 Item item, Size size, =09=09=09=09=09 OffsetNumber *startOffset, =09=09=09=09=09 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/toast= ing.h index f82a799..c649173 100644 --- a/src/include/catalog/toasting.h +++ b/src/include/catalog/toasting.h @@ -19,7 +19,7 @@ /* * toasting.c prototypes */ -extern void NewRelationCreateToastTable(Oid relOid, Datum reloptions);= +extern bool NewRelationCreateToastTable(Oid relOid, Datum reloptions);= extern void NewHeapCreateToastTable(Oid relOid, Datum reloptions, =09=09=09=09=09=09LOCKMODE lockmode); extern void AlterTableCreateToastTable(Oid relOid, Datum reloptions, diff --git a/src/include/commands/tablecmds.h b/src/include/commands/ta= blecmds.h index abd31b6..21e85ed 100644 --- a/src/include/commands/tablecmds.h +++ b/src/include/commands/tablecmds.h @@ -31,7 +31,7 @@ extern Oid=09AlterTableLookupRelation(AlterTableStmt = *stmt, LOCKMODE lockmode); =20 extern void AlterTable(Oid relid, LOCKMODE lockmode, AlterTableStmt *s= tmt); =20 -extern LOCKMODE AlterTableGetLockLevel(List *cmds); +extern LOCKMODE AlterTableGetLockLevel(Oid relid, List *cmds); =20 extern void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool re= cursing, LOCKMODE lockmode); =20 diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h index 4bc61e5..e276d8f 100644 --- a/src/include/utils/rel.h +++ b/src/include/utils/rel.h @@ -273,114 +273,123 @@ typedef struct AutoVacOpts =09float8=09=09analyze_scale_factor; } AutoVacOpts; =20 -typedef struct StdRdOptions +typedef struct HeapOptions { =09int32=09=09vl_len_;=09=09/* varlena header (do not touch directly!)= */ =09int=09=09=09fillfactor;=09=09/* page fill factor in percent (0..100= ) */ =09AutoVacOpts autovacuum;=09=09/* autovacuum-related options */ =09bool=09=09user_catalog_table; /* use as an additional catalog relat= ion */ =09int=09=09=09parallel_workers;=09/* max number of parallel workers *= / -} StdRdOptions; +} HeapOptions; + +typedef struct ToastOptions +{ +=09int32=09=09vl_len_;=09=09/* varlena header (do not touch directly!)= */ +=09AutoVacOpts autovacuum;=09=09/* autovacuum-related options */ +}=09ToastOptions; =20 #define HEAP_MIN_FILLFACTOR=09=09=0910 #define HEAP_DEFAULT_FILLFACTOR=09=09100 =20 +#define TOAST_DEFAULT_FILLFACTOR=09100 /* Only default is actually use= d */ + /* - * RelationGetFillFactor + * HeapGetFillFactor *=09=09Returns the relation's fillfactor. Note multiple eval of argu= ment! */ -#define RelationGetFillFactor(relation, defaultff) \ +#define HeapGetFillFactor(relation, defaultff) \ =09((relation)->rd_options ? \ -=09 ((StdRdOptions *) (relation)->rd_options)->fillfactor : (defaultff= )) +=09 ((HeapOptions *) (relation)->rd_options)->fillfactor : (defaultff)= ) =20 /* - * RelationGetTargetPageUsage - *=09=09Returns the relation's desired space usage per page in bytes. - */ -#define RelationGetTargetPageUsage(relation, defaultff) \ -=09(BLCKSZ * RelationGetFillFactor(relation, defaultff) / 100) - -/* - * RelationGetTargetPageFreeSpace + * HeapGetTargetPageFreeSpace *=09=09Returns the relation's desired freespace per page in bytes. */ -#define RelationGetTargetPageFreeSpace(relation, defaultff) \ -=09(BLCKSZ * (100 - RelationGetFillFactor(relation, defaultff)) / 100)= +#define HeapGetTargetPageFreeSpace(relation, defaultff) \ +=09(BLCKSZ * (100 - HeapGetFillFactor(relation, defaultff)) / 100) =20 /* - * RelationIsUsedAsCatalogTable + * HeapIsUsedAsCatalogTable *=09=09Returns whether the relation should be treated as a catalog ta= ble *=09=09from the pov of logical decoding. Note multiple eval of argum= ent! */ -#define RelationIsUsedAsCatalogTable(relation)=09\ +#define HeapIsUsedAsCatalogTable(relation)=09\ =09((relation)->rd_options && \ =09 ((relation)->rd_rel->relkind =3D=3D RELKIND_RELATION || \ =09 (relation)->rd_rel->relkind =3D=3D RELKIND_MATVIEW) ? \ -=09 ((StdRdOptions *) (relation)->rd_options)->user_catalog_table : fa= lse) +=09 ((HeapOptions *) (relation)->rd_options)->user_catalog_table : fal= se) =20 /* - * RelationGetParallelWorkers + * HeapGetParallelWorkers *=09=09Returns the relation's parallel_workers reloption setting. *=09=09Note multiple eval of argument! */ -#define RelationGetParallelWorkers(relation, defaultpw) \ +#define HeapGetParallelWorkers(relation, defaultpw) \ =09((relation)->rd_options ? \ -=09 ((StdRdOptions *) (relation)->rd_options)->parallel_workers : (def= aultpw)) +=09 ((HeapOptions *) (relation)->rd_options)->parallel_workers : (defa= ultpw)) =20 +#define ToastGetTargetPageFreeSpace() \ +=09(BLCKSZ * (100 - TOAST_DEFAULT_FILLFACTOR) / 100) =20 /* * ViewOptions *=09=09Contents 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 {=09\ +=09"local",=09=09=09=09=09=09=09=09=09\ +=09"cascaded",=09=09=09=09=09=09=09=09=09\ +=09(const char *) NULL=09=09=09=09=09=09=09\ +} + +typedef enum view_option_check_option_value_numbers +{ +=09VIEW_OPTION_CHECK_OPTION_NOT_SET =3D -1, +=09VIEW_OPTION_CHECK_OPTION_LOCAL =3D 0, +=09VIEW_OPTION_CHECK_OPTION_CASCADED =3D 1, +}=09view_option_check_option_value_numbers; + typedef struct ViewOptions { =09int32=09=09vl_len_;=09=09/* varlena header (do not touch directly!)= */ =09bool=09=09security_barrier; -=09int=09=09=09check_option_offset; +=09int=09=09=09check_option; } ViewOptions; =20 /* - * RelationIsSecurityView - *=09=09Returns whether the relation is security view, or not. Note m= ultiple - *=09=09eval of argument! + * ViewIsSecurityView + *=09=09Returns whether the view is security view, or not. Note multip= le eval + *=09=09of argument! */ -#define RelationIsSecurityView(relation)=09\ +#define ViewIsSecurityView(relation)=09=09\ =09((relation)->rd_options ?=09=09=09=09\ -=09 ((ViewOptions *) (relation)->rd_options)->security_barrier : false= ) +=09=09((ViewOptions *) (relation)->rd_options)->security_barrier : fal= se) =20 /* - * RelationHasCheckOption - *=09=09Returns true if the relation is a view defined with either the= local - *=09=09or the cascaded check option. Note multiple eval of argument!= + * ViewHasCheckOption + *=09=09Returns true if view is defined with either the local or the c= ascaded + *=09=09check option. Note multiple eval of argument! */ -#define RelationHasCheckOption(relation)=09=09=09=09=09=09=09=09=09\ -=09((relation)->rd_options &&=09=09=09=09=09=09=09=09=09=09=09=09\ -=09 ((ViewOptions *) (relation)->rd_options)->check_option_offset !=3D= 0) =20 -/* - * RelationHasLocalCheckOption - *=09=09Returns true if the relation is a view defined with the local = check - *=09=09option. Note multiple eval of argument! - */ -#define RelationHasLocalCheckOption(relation)=09=09=09=09=09=09=09=09\= -=09((relation)->rd_options &&=09=09=09=09=09=09=09=09=09=09=09=09\ -=09 ((ViewOptions *) (relation)->rd_options)->check_option_offset !=3D= 0 ?=09\ -=09 strcmp((char *) (relation)->rd_options +=09=09=09=09=09=09=09=09\ -=09=09=09((ViewOptions *) (relation)->rd_options)->check_option_offset= ,=09\ -=09=09=09"local") =3D=3D 0 : false) +#define ViewHasCheckOption(relation)=09=09=09=09=09=09=09=09=09=09\ +((relation)->rd_options &&=09=09=09=09=09=09=09=09=09=09=09=09=09\ +=09((ViewOptions *) (relation)->rd_options)->check_option !=3D=09=09=09= =09\ +=09VIEW_OPTION_CHECK_OPTION_NOT_SET) =20 /* - * RelationHasCascadedCheckOption - *=09=09Returns true if the relation is a view defined with the cascad= ed check - *=09=09option. Note multiple eval of argument! + * ViewHasCascadedCheckOption + *=09=09Returns true if the view is defined with the cascaded check op= tion. + *=09=09Note multiple eval of argument! */ -#define RelationHasCascadedCheckOption(relation)=09=09=09=09=09=09=09\= -=09((relation)->rd_options &&=09=09=09=09=09=09=09=09=09=09=09=09\ -=09 ((ViewOptions *) (relation)->rd_options)->check_option_offset !=3D= 0 ?=09\ -=09 strcmp((char *) (relation)->rd_options +=09=09=09=09=09=09=09=09\ -=09=09=09((ViewOptions *) (relation)->rd_options)->check_option_offset= ,=09\ -=09=09=09"cascaded") =3D=3D 0 : false) =20 +#define ViewHasCascadedCheckOption(relation)=09=09=09=09=09=09=09=09\ +((relation)->rd_options &&=09=09=09=09=09=09=09=09=09=09=09=09=09\ +=09((ViewOptions *) (relation)->rd_options)->check_option =3D=3D=09=09= =09=09\ +=09VIEW_OPTION_CHECK_OPTION_CASCADED) =20 /* * RelationIsValid @@ -559,7 +568,7 @@ typedef struct ViewOptions #define RelationIsAccessibleInLogicalDecoding(relation) \ =09(XLogLogicalInfoActive() && \ =09 RelationNeedsWAL(relation) && \ -=09 (IsCatalogRelation(relation) || RelationIsUsedAsCatalogTable(relat= ion))) +=09 (IsCatalogRelation(relation) || HeapIsUsedAsCatalogTable(relation)= )) =20 /* * RelationIsLogicallyLogged diff --git a/src/test/regress/expected/alter_table.out b/src/test/regre= ss/expected/alter_table.out index 13d6a4b..c0bd149 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -2354,7 +2354,9 @@ ERROR: unrecognized parameter "autovacuum_enable= d" 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; @@ -3506,3 +3508,10 @@ create table parted_validate_test_1 partition of= parted_validate_test for values alter table parted_validate_test add constraint parted_validate_test_c= hka check (a > 0) not valid; alter table parted_validate_test validate constraint parted_validate_t= est_chka; drop table parted_validate_test; +-- test alter column options +CREATE TABLE tmp(i integer); +INSERT INTO tmp VALUES (1); +ALTER TABLE tmp ALTER COLUMN i SET (n_distinct =3D 1, n_distinct_inher= ited =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/expe= cted/brin.out index ca80f00..6e9c4a2 100644 --- a/src/test/regress/expected/brin.out +++ b/src/test/regress/expected/brin.out @@ -507,3 +507,8 @@ EXPLAIN (COSTS OFF) SELECT * FROM brin_test WHERE b= =3D 1; Filter: (b =3D 1) (2 rows) =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/regr= ess/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=3D= 128 CREATE INDEX hash_i4_index ON hash_i4_heap USING hash (random int4_ops= ); CREATE INDEX hash_name_index ON hash_name_heap USING hash (random name= _ops); CREATE INDEX hash_txt_index ON hash_txt_heap USING hash (random text_o= ps); -CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_o= ps); +CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_o= ps) WITH (fillfactor=3D60); CREATE UNLOGGED TABLE unlogged_hash_table (id int4); CREATE INDEX unlogged_hash_index ON unlogged_hash_table USING hash (id= int4_ops); DROP TABLE unlogged_hash_table; diff --git a/src/test/regress/expected/gist.out b/src/test/regress/expe= cted/gist.out index 91f9998..df52e46 100644 --- a/src/test/regress/expected/gist.out +++ b/src/test/regress/expected/gist.out @@ -12,6 +12,20 @@ insert into gist_point_tbl (id, p) select g+100000, point(g*10+1, g*10+1) from generate_series(1, 10000) = g; -- To test vacuum, delete some entries from all over the index. delete from gist_point_tbl where id % 2 =3D 1; +-- Test buffering and fillfactor reloption takes valid values +create index gist_pointidx2 on gist_point_tbl using gist(p) with (buff= ering =3D on, fillfactor=3D50); +create index gist_pointidx3 on gist_point_tbl using gist(p) with (buff= ering =3D off); +create index gist_pointidx4 on gist_point_tbl using gist(p) with (buff= ering =3D auto); +--Test buffering and fillfactor for refising invalid values +create index gist_pointidx5 on gist_point_tbl using gist(p) with (buff= ering =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 (fill= factor=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 (fill= factor=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 current= ly -- 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/regres= s/expected/hash_index.out index 0bbaa2a..6d5cf0d 100644 --- a/src/test/regress/expected/hash_index.out +++ b/src/test/regress/expected/hash_index.out @@ -229,3 +229,12 @@ CREATE TABLE hash_heap_float4 (x float4, y int); INSERT INTO hash_heap_float4 VALUES (1.1,1); CREATE INDEX hash_idx ON hash_heap_float4 USING hash (x); DROP TABLE hash_heap_float4 CASCADE; +-- Check reloptions min max values and that it is not allowed to ALTER= +CREATE INDEX hash_f8_index2 ON hash_f8_heap USING hash (random float8_= ops) WITH (fillfactor=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/regres= s/expected/reloptions.out new file mode 100644 index 0000000..835d72f --- /dev/null +++ b/src/test/regress/expected/reloptions.out @@ -0,0 +1,193 @@ +-- Simple create +CREATE TABLE reloptions_test(i INT) WITH (fillfactor=3D30,autovacuum_e= nabled =3D false, autovacuum_analyze_scale_factor =3D 0.2); +SELECT reloptions FROM pg_class WHERE oid =3D 'reloptions_test'::regcl= ass; + reloptions = =20 +----------------------------------------------------------------------= -------- + {fillfactor=3D30,autovacuum_enabled=3Dfalse,autovacuum_analyze_scale_= factor=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'::regc= lass; + reloptions =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_fa= ctor =3D -10.0); +ERROR: value -10.0 out of bounds for option "autovacuum_analyze_scale= _factor" +DETAIL: Valid values are between "0.000000" and "100.000000". +CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_fa= ctor =3D 110.0); +ERROR: value 110.0 out of bounds for option "autovacuum_analyze_scale= _factor" +DETAIL: Valid values are between "0.000000" and "100.000000". +-- Fail when option and namespase do not exist +CREATE TABLE reloptions_test2(i INT) WITH (not_existing_option=3D2); +ERROR: unrecognized parameter "not_existing_option" +CREATE TABLE reloptions_test2(i INT) WITH (not_existing_namespace.fill= factor=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'strin= g'); +ERROR: invalid value for boolean option "autovacuum_enabled": string +CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_fa= ctor=3D'string'); +ERROR: invalid value for floating point option "autovacuum_analyze_sc= ale_factor": string +CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_fa= ctor=3Dtrue); +ERROR: invalid value for floating point option "autovacuum_analyze_sc= ale_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_s= cale_factor =3D 0.3); +SELECT reloptions FROM pg_class WHERE oid =3D 'reloptions_test'::regcl= ass; + reloptions = =20 +----------------------------------------------------------------------= -------- + {autovacuum_enabled=3Dfalse,fillfactor=3D31,autovacuum_analyze_scale_= factor=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'::regcl= ass; + reloptions = =20 +----------------------------------------------------------------------= ------- + {autovacuum_analyze_scale_factor=3D0.3,autovacuum_enabled=3Dtrue,fill= factor=3D32} +(1 row) + +-- Check that RESET works well +ALTER TABLE reloptions_test RESET (fillfactor); +SELECT reloptions FROM pg_class WHERE oid =3D 'reloptions_test'::regcl= ass; + reloptions =20 +--------------------------------------------------------------- + {autovacuum_analyze_scale_factor=3D0.3,autovacuum_enabled=3Dtrue} +(1 row) + +-- Check that RESETting all values make NULL reloptions record in pg_c= lass +ALTER TABLE reloptions_test RESET (autovacuum_enabled, autovacuum_anal= yze_scale_factor); +SELECT reloptions FROM pg_class WHERE oid =3D 'reloptions_test'::regcl= ass 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'::regcl= ass; + reloptions =20 +----------------- + {fillfactor=3D20} +(1 row) + +-- Now testing toast.* options +DROP TABLE reloptions_test; +CREATE TABLE reloptions_test (s VARCHAR) WITH (toast.autovacuum_vacuum= _cost_delay =3D 23 ); +SELECT reloptions FROM pg_class WHERE oid =3D (SELECT reltoastrelid FR= OM pg_class WHERE oid =3D 'reloptions_test'::regclass); + reloptions =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 FR= OM pg_class WHERE oid =3D 'reloptions_test'::regclass); + reloptions =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 FR= OM pg_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 rel= ation +CREATE TABLE reloptions_test2 (i int) WITH (toast.autovacuum_vacuum_co= st_delay =3D 23 ); +ERROR: no TOAST relation was created for a table. Can't set toast.* s= torage 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 toa= st.* storage parameters +ALTER TABLE reloptions_test RESET (toast.autovacuum_vacuum_cost_delay)= ; +ERROR: TOAST relation for this table does not exist. Can't change toa= st.* storage parameters +-- autovacuum_analyze_scale_factor and autovacuum_analyze_threshold sh= ould be +-- accepted by heap but rejected by toast (special case) +DROP TABLE reloptions_test; +CREATE TABLE reloptions_test (s VARCHAR) WITH (autovacuum_analyze_scal= e_factor=3D1, autovacuum_analyze_threshold=3D1); +CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analy= ze_scale_factor=3D1); +ERROR: unrecognized parameter "toast.autovacuum_analyze_scale_factor"= +CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analy= ze_threshold=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= _cost_delay =3D 23, autovacuum_vacuum_cost_delay =3D 24, fillfactor =3D= 40); +SELECT reloptions FROM pg_class WHERE oid =3D 'reloptions_test'::regcl= ass; + reloptions =20 +------------------------------------------------- + {autovacuum_vacuum_cost_delay=3D24,fillfactor=3D40} +(1 row) + +SELECT reloptions FROM pg_class WHERE oid =3D (SELECT reltoastrelid FR= OM pg_class WHERE oid =3D 'reloptions_test'::regclass); + reloptions =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 (fillfact= or=3D30); +SELECT reloptions FROM pg_class WHERE oid =3D 'reloptions_test_idx'::r= egclass; + reloptions =20 +----------------- + {fillfactor=3D30} +(1 row) + +-- Fail when option and namespase do not exist +CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_exis= ting_option=3D2); +ERROR: unrecognized parameter "not_existing_option" +CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_exis= ting_option.fillfactor=3D2); +ERROR: unrecognized parameter namespace "not_existing_option" +-- Check ranges +CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfac= tor=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 (fillfac= tor=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'::r= egclass; + reloptions =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'::= regclass; + reloptions =20 +----------------- + {fillfactor=3D40} +(1 row) + diff --git a/src/test/regress/expected/spgist.out b/src/test/regress/ex= pected/spgist.out index 0691e91..bb58fb7 100644 --- a/src/test/regress/expected/spgist.out +++ b/src/test/regress/expected/spgist.out @@ -4,7 +4,7 @@ -- There are other tests to test different SP-GiST opclasses. This is = for -- testing SP-GiST code itself. create table spgist_point_tbl(id int4, p point); -create index spgist_point_idx on spgist_point_tbl using spgist(p); +create index spgist_point_idx on spgist_point_tbl using spgist(p) with= (fillfactor =3D 75); -- Test vacuum-root operation. It gets invoked when the root is also a= leaf, -- i.e. the index is very small. insert into spgist_point_tbl (id, p) @@ -37,3 +37,12 @@ select g, 'baaaaaaaaaaaaaar' || g from generate_seri= es(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_serie= s(1, 100) g; +-- Check reloptions min, max values +create index spgist_point_idx2 on spgist_point_tbl using spgist(p) wit= h (fillfactor =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) wit= h (fillfactor =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/r= egress/expected/updatable_views.out index 971dddd..85d1daf 100644 --- a/src/test/regress/expected/updatable_views.out +++ b/src/test/regress/expected/updatable_views.out @@ -1551,7 +1551,7 @@ SELECT * FROM base_tbl; =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/regres= s/input/tablespace.source index 03a62bd..7f7934b 100644 --- a/src/test/regress/input/tablespace.source +++ b/src/test/regress/input/tablespace.source @@ -12,10 +12,10 @@ DROP TABLESPACE regress_tblspacewith; CREATE TABLESPACE regress_tblspace LOCATION '@testtablespace@'; =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_p= age_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_co= st); -- ok +ALTER TABLESPACE regress_tblspace RESET (random_page_cost, effective_i= o_concurrency); -- ok =20 -- create a schema we can use CREATE SCHEMA testschema; diff --git a/src/test/regress/output/tablespace.source b/src/test/regre= ss/output/tablespace.source index aaedf5f..2443511 100644 --- a/src/test/regress/output/tablespace.source +++ b/src/test/regress/output/tablespace.source @@ -14,12 +14,12 @@ DROP TABLESPACE regress_tblspacewith; -- create a tablespace we can use CREATE TABLESPACE regress_tblspace LOCATION '@testtablespace@'; -- try setting and resetting some properties for the new tablespace -ALTER TABLESPACE regress_tblspace SET (random_page_cost =3D 1.0); +ALTER TABLESPACE regress_tblspace SET (random_page_cost =3D 1.0, seq_p= age_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_co= st); -- ok +ALTER TABLESPACE regress_tblspace RESET (random_page_cost, effective_i= o_concurrency); -- ok -- create a schema we can use CREATE SCHEMA testschema; -- try a table diff --git a/src/test/regress/parallel_schedule b/src/test/regress/para= llel_schedule index 2369261..970025b 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -109,7 +109,7 @@ test: select_views portals_p2 foreign_key cluster d= ependency 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 wit= hout_oid conversion truncate alter_table sequence polymorphism rowtypes= returning largeobject with xml +test: plancache limit plpgsql copy2 temp domain rangefuncs prepare wit= hout_oid conversion truncate alter_table sequence polymorphism rowtypes= returning largeobject with xml reloptions =20 # ---------- # Another group of parallel tests diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial= _schedule index 5e8b7e9..55d5deb 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -178,5 +178,6 @@ test: returning test: largeobject test: with test: xml +test: reloptions test: event_trigger test: stats diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sq= l/alter_table.sql index 5dd1402..4e84fb8 100644 --- a/src/test/regress/sql/alter_table.sql +++ b/src/test/regress/sql/alter_table.sql @@ -2307,3 +2307,10 @@ create table parted_validate_test_1 partition of= parted_validate_test for values alter table parted_validate_test add constraint parted_validate_test_c= hka check (a > 0) not valid; alter table parted_validate_test validate constraint parted_validate_t= est_chka; drop table parted_validate_test; +-- test alter column options +CREATE TABLE tmp(i integer); +INSERT INTO tmp VALUES (1); +ALTER TABLE tmp ALTER COLUMN i SET (n_distinct =3D 1, n_distinct_inher= ited =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 11f8fe9..3559ef6 100644 --- a/src/test/regress/sql/brin.sql +++ b/src/test/regress/sql/brin.sql @@ -460,3 +460,6 @@ VACUUM ANALYZE brin_test; EXPLAIN (COSTS OFF) SELECT * FROM brin_test WHERE a =3D 1; -- Ensure brin index is not used when values are not correlated EXPLAIN (COSTS OFF) SELECT * FROM brin_test WHERE b =3D 1; +-- 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/s= ql/create_index.sql index 1648072..1f8d101 100644 --- a/src/test/regress/sql/create_index.sql +++ b/src/test/regress/sql/create_index.sql @@ -682,7 +682,7 @@ CREATE INDEX hash_name_index ON hash_name_heap USIN= G hash (random name_ops); =20 CREATE INDEX hash_txt_index ON hash_txt_heap USING hash (random text_o= ps); =20 -CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_o= ps); +CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_o= ps) WITH (fillfactor=3D60); =20 CREATE UNLOGGED TABLE unlogged_hash_table (id int4); CREATE INDEX unlogged_hash_index ON unlogged_hash_table USING hash (id= int4_ops); diff --git a/src/test/regress/sql/gist.sql b/src/test/regress/sql/gist.= sql index 49126fd..f65ce37 100644 --- a/src/test/regress/sql/gist.sql +++ b/src/test/regress/sql/gist.sql @@ -17,6 +17,17 @@ select g+100000, point(g*10+1, g*10+1) from generate= _series(1, 10000) g; -- To test vacuum, delete some entries from all over the index. delete from gist_point_tbl where id % 2 =3D 1; =20 +-- Test buffering and fillfactor reloption takes valid values +create index gist_pointidx2 on gist_point_tbl using gist(p) with (buff= ering =3D on, fillfactor=3D50); +create index gist_pointidx3 on gist_point_tbl using gist(p) with (buff= ering =3D off); +create index gist_pointidx4 on gist_point_tbl using gist(p) with (buff= ering =3D auto); +--Test buffering and fillfactor for refising invalid values +create index gist_pointidx5 on gist_point_tbl using gist(p) with (buff= ering =3D invalid_value); +create index gist_pointidx5 on gist_point_tbl using gist(p) with (fill= factor=3D9); +create index gist_pointidx5 on gist_point_tbl using gist(p) with (fill= factor=3D101); + + + -- And also delete some concentration of values. (GiST doesn't current= ly -- attempt to delete pages even when they become empty, but if it did,= this -- would exercise it) diff --git a/src/test/regress/sql/hash_index.sql b/src/test/regress/sql= /hash_index.sql index 9af03d2..f245525 100644 --- a/src/test/regress/sql/hash_index.sql +++ b/src/test/regress/sql/hash_index.sql @@ -192,3 +192,8 @@ CREATE TABLE hash_heap_float4 (x float4, y int); INSERT INTO hash_heap_float4 VALUES (1.1,1); CREATE INDEX hash_idx ON hash_heap_float4 USING hash (x); DROP TABLE hash_heap_float4 CASCADE; + +-- Check reloptions min max values and that it is not allowed to ALTER= +CREATE INDEX hash_f8_index2 ON hash_f8_heap USING hash (random float8_= ops) WITH (fillfactor=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= /reloptions.sql new file mode 100644 index 0000000..0bfec28 --- /dev/null +++ b/src/test/regress/sql/reloptions.sql @@ -0,0 +1,153 @@ + +-- Simple create +CREATE TABLE reloptions_test(i INT) WITH (fillfactor=3D30,autovacuum_e= nabled =3D false, autovacuum_analyze_scale_factor =3D 0.2); + +SELECT reloptions FROM pg_class WHERE oid =3D 'reloptions_test'::regcl= ass; + +-- Test =D1=81ase insensitive +CREATE TABLE reloptions_test3(i INT) WITH (FiLlFaCtoR=3D40); + +SELECT reloptions FROM pg_class WHERE oid =3D 'reloptions_test3'::regc= lass; + +-- 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_fa= ctor =3D -10.0); + +CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_fa= ctor =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.fill= factor=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'strin= g'); + +CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_fa= ctor=3D'string'); + +CREATE TABLE reloptions_test2(i INT) WITH (autovacuum_analyze_scale_fa= ctor=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_s= cale_factor =3D 0.3); + +SELECT reloptions FROM pg_class WHERE oid =3D 'reloptions_test'::regcl= ass; + +-- 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'::regcl= ass; + +-- Check that RESET works well + +ALTER TABLE reloptions_test RESET (fillfactor); + +SELECT reloptions FROM pg_class WHERE oid =3D 'reloptions_test'::regcl= ass; + +-- Check that RESETting all values make NULL reloptions record in pg_c= lass +ALTER TABLE reloptions_test RESET (autovacuum_enabled, autovacuum_anal= yze_scale_factor); +SELECT reloptions FROM pg_class WHERE oid =3D 'reloptions_test'::regcl= ass 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'::regcl= ass; + +-- Now testing toast.* options +DROP TABLE reloptions_test; + +CREATE TABLE reloptions_test (s VARCHAR) WITH (toast.autovacuum_vacuum= _cost_delay =3D 23 ); + +SELECT reloptions FROM pg_class WHERE oid =3D (SELECT reltoastrelid FR= OM pg_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 FR= OM pg_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 FR= OM pg_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 rel= ation +CREATE TABLE reloptions_test2 (i int) WITH (toast.autovacuum_vacuum_co= st_delay =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 sh= ould be +-- accepted by heap but rejected by toast (special case) +DROP TABLE reloptions_test; +CREATE TABLE reloptions_test (s VARCHAR) WITH (autovacuum_analyze_scal= e_factor=3D1, autovacuum_analyze_threshold=3D1); + +CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analy= ze_scale_factor=3D1); +CREATE TABLE reloptions_test2 (s VARCHAR) WITH (toast.autovacuum_analy= ze_threshold=3D1); + +-- And now mixed toast + heap +DROP TABLE reloptions_test; + +CREATE TABLE reloptions_test (s VARCHAR) WITH (toast.autovacuum_vacuum= _cost_delay =3D 23, autovacuum_vacuum_cost_delay =3D 24, fillfactor =3D= 40); + +SELECT reloptions FROM pg_class WHERE oid =3D 'reloptions_test'::regcl= ass; +SELECT reloptions FROM pg_class WHERE oid =3D (SELECT reltoastrelid FR= OM pg_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 (fillfact= or=3D30); + +SELECT reloptions FROM pg_class WHERE oid =3D 'reloptions_test_idx'::r= egclass; + +-- Fail when option and namespase do not exist + +CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_exis= ting_option=3D2); + +CREATE INDEX reloptions_test_idx ON reloptions_test (s) WITH (not_exis= ting_option.fillfactor=3D2); + +-- Check ranges + +CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfac= tor=3D1); + +CREATE INDEX reloptions_test_idx2 ON reloptions_test (s) WITH (fillfac= tor=3D130); + +-- Check alter + +ALTER INDEX reloptions_test_idx SET (fillfactor=3D40); + +SELECT reloptions FROM pg_class WHERE oid =3D 'reloptions_test_idx'::r= egclass; + +-- 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'::= regclass; + + diff --git a/src/test/regress/sql/spgist.sql b/src/test/regress/sql/spg= ist.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= (fillfactor =3D 75); =20 -- Test vacuum-root operation. It gets invoked when the root is also a= leaf, -- i.e. the index is very small. @@ -48,3 +48,10 @@ select g, 'baaaaaaaaaaaaaar' || g from generate_seri= es(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_serie= s(1, 100) g; + +-- Check reloptions min, max values +create index spgist_point_idx2 on spgist_point_tbl using spgist(p) wit= h (fillfactor =3D 9); +create index spgist_point_idx2 on spgist_point_tbl using spgist(p) wit= h (fillfactor =3D 101); + +-- ALTER reloption should work +alter index spgist_point_idx set (fillfactor =3D 76); --nextPart5742593.9NUVLNG8jH-- --nextPart1694667.WJnQhjP3oG Content-Type: application/pgp-signature; name="signature.asc" Content-Description: This is a digitally signed message part. Content-Transfer-Encoding: 7Bit -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQEcBAABCAAGBQJZT/t9AAoJEKc13HowK+H4wEIIAIkyWjWmIfsxc1qZun8AiCfi vrJiEj9Fr0Vv+0RptL/I5QkV6K973fkBMoWp/L2Lh0xxzjTq6CbXaQzPl9+wRq7f fCZR+7iSXqgBD84abavL6q/EfVP30ArizK0u7EOj/8+6DIIsGjZ15rpP7mJtHnIO kUwTQf8swqWQIhYLQ1a9y2K4G20rhCV87Sd8p80lVQdqwx82GLTQtg6olRRiBeJa lWZtmXkWTI5xXY916oCP3Cx6o8bHekw1CxLuVwyrzD0lOGON97Y1zCNSVloUopPT 7JE6qs9N+xnjCtmVSdwRXa66PspH4OeTYHsPrXFD3v7s5hX/13thAcLlGM4EZbc= =Nos9 -----END PGP SIGNATURE----- --nextPart1694667.WJnQhjP3oG--