Thread

  1. [PATCH v2] Convert `vacuum_index_cleanup` and GiST's `buffering` reloptions to ternary type

    Nikolay Shaplov <dhyan@nataraj.su> — 2026-05-10T10:23:15Z

    `vacuum_index_cleanup` and GiST's `buffering` reloptions behave almost like
    ternary options; the only difference is that their third "unset" state
    can be set explicitly, using keyword "auto".
    
    This patch extends ternary option definition with `unset_alias` value, that
    will allow users to explicitly set option to "third value"
    
    Both `vacuum_index_cleanup` and `buffering` have been `boolean` in the past,
    so this patch returns them back to their original design: `boolean` with
    additional `auto` value.
    
    Author: Nikolay Shaplov <dhyan@nataraj.su>
    Discussion: https://postgr.es/m/20b95759-b5d6-4d46-9ab5-3ca54a9b58be%40nataraj.su
    ---
     src/backend/access/common/reloptions.c        | 111 +++++++++---------
     src/backend/access/gist/gistbuild.c           |   4 +-
     src/backend/commands/vacuum.c                 |  11 +-
     src/include/access/gist_private.h             |  10 +-
     src/include/access/reloptions.h               |  11 +-
     src/include/utils/rel.h                       |  10 +-
     src/test/modules/dummy_index_am/README        |   3 +-
     .../modules/dummy_index_am/dummy_index_am.c   |  11 +-
     .../dummy_index_am/expected/reloptions.out    |  27 +++--
     .../modules/dummy_index_am/sql/reloptions.sql |   7 ++
     src/test/regress/expected/gist.out            |   2 +-
     src/test/regress/expected/reloptions.out      |  18 +++
     src/test/regress/sql/reloptions.sql           |  10 ++
     13 files changed, 139 insertions(+), 96 deletions(-)
    
    diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
    index 3e832c3797e..7094016a9cc 100644
    --- a/src/backend/access/common/reloptions.c
    +++ b/src/backend/access/common/reloptions.c
    @@ -174,7 +174,29 @@ static relopt_ternary ternaryRelOpts[] =
     			"Enables vacuum to truncate empty pages at the end of this table",
     			RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
     			ShareUpdateExclusiveLock
    -		}
    +		},
    +		NULL,
    +		PG_TERNARY_UNSET
    +	},
    +	{
    +		{
    +			"vacuum_index_cleanup",
    +			"Controls index vacuuming and index cleanup",
    +			RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
    +			ShareUpdateExclusiveLock
    +		},
    +		"auto",
    +		PG_TERNARY_UNSET
    +	},
    +	{
    +		{
    +			"buffering",
    +			"Enables buffering build for this GiST index",
    +			RELOPT_KIND_GIST,
    +			AccessExclusiveLock
    +		},
    +		"auto",
    +		PG_TERNARY_UNSET
     	},
     	/* list terminator */
     	{
    @@ -515,30 +537,6 @@ static relopt_real realRelOpts[] =
     	{{NULL}}
     };
     
    -/* values from StdRdOptIndexCleanup */
    -static relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
    -{
    -	{"auto", STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO},
    -	{"on", STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON},
    -	{"off", STDRD_OPTION_VACUUM_INDEX_CLEANUP_OFF},
    -	{"true", STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON},
    -	{"false", STDRD_OPTION_VACUUM_INDEX_CLEANUP_OFF},
    -	{"yes", STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON},
    -	{"no", STDRD_OPTION_VACUUM_INDEX_CLEANUP_OFF},
    -	{"1", STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON},
    -	{"0", STDRD_OPTION_VACUUM_INDEX_CLEANUP_OFF},
    -	{(const char *) NULL}		/* list terminator */
    -};
    -
    -/* values from GistOptBufferingMode */
    -static relopt_enum_elt_def gistBufferingOptValues[] =
    -{
    -	{"auto", GIST_OPTION_BUFFERING_AUTO},
    -	{"on", GIST_OPTION_BUFFERING_ON},
    -	{"off", GIST_OPTION_BUFFERING_OFF},
    -	{(const char *) NULL}		/* list terminator */
    -};
    -
     /* values from ViewOptCheckOption */
     static relopt_enum_elt_def viewCheckOptValues[] =
     {
    @@ -550,28 +548,6 @@ static relopt_enum_elt_def viewCheckOptValues[] =
     
     static relopt_enum enumRelOpts[] =
     {
    -	{
    -		{
    -			"vacuum_index_cleanup",
    -			"Controls index vacuuming and index cleanup",
    -			RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
    -			ShareUpdateExclusiveLock
    -		},
    -		StdRdOptIndexCleanupValues,
    -		STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO,
    -		gettext_noop("Valid values are \"on\", \"off\", and \"auto\".")
    -	},
    -	{
    -		{
    -			"buffering",
    -			"Enables buffering build for this GiST index",
    -			RELOPT_KIND_GIST,
    -			AccessExclusiveLock
    -		},
    -		gistBufferingOptValues,
    -		GIST_OPTION_BUFFERING_AUTO,
    -		gettext_noop("Valid values are \"on\", \"off\", and \"auto\".")
    -	},
     	{
     		{
     			"check_option",
    @@ -939,12 +915,14 @@ add_local_bool_reloption(local_relopts *relopts, const char *name,
      */
     static relopt_ternary *
     init_ternary_reloption(uint32 kinds, const char *name, const char *desc,
    -					   LOCKMODE lockmode)
    +			 pg_ternary default_val, const char* unset_alias, LOCKMODE lockmode)
     {
     	relopt_ternary *newoption;
     
     	newoption = (relopt_ternary *)
     		allocate_reloption(kinds, RELOPT_TYPE_TERNARY, name, desc, lockmode);
    +	newoption->default_val = default_val;
    +	newoption->unset_alias = unset_alias;
     
     	return newoption;
     }
    @@ -955,12 +933,12 @@ init_ternary_reloption(uint32 kinds, const char *name, const char *desc,
      */
     void
     add_ternary_reloption(uint32 kinds, const char *name, const char *desc,
    -					  LOCKMODE lockmode)
    +		  pg_ternary default_val, const char* unset_alias,  LOCKMODE lockmode)
     {
     	relopt_ternary *newoption;
     
    -	newoption =
    -		init_ternary_reloption(kinds, name, desc, lockmode);
    +	newoption = init_ternary_reloption(kinds, name, desc, default_val,
    +													unset_alias, lockmode);
     
     	add_reloption((relopt_gen *) newoption);
     }
    @@ -973,12 +951,13 @@ add_ternary_reloption(uint32 kinds, const char *name, const char *desc,
      */
     void
     add_local_ternary_reloption(local_relopts *relopts, const char *name,
    -							const char *desc, int offset)
    +							const char *desc, pg_ternary default_val,
    +							const char* unset_alias, int offset)
     {
     	relopt_ternary *newoption;
     
    -	newoption =
    -		init_ternary_reloption(RELOPT_KIND_LOCAL, name, desc, 0);
    +	newoption = init_ternary_reloption(RELOPT_KIND_LOCAL, name, desc,
    +												default_val, unset_alias, 0);
     
     	add_local_reloption(relopts, (relopt_gen *) newoption, offset);
     }
    @@ -1719,15 +1698,35 @@ parse_one_reloption(relopt_value *option, char *text_str, int text_len,
     		case RELOPT_TYPE_TERNARY:
     			{
     				bool		b;
    +				relopt_ternary *opt = (relopt_ternary *) option->gen;
     
     				parsed = parse_bool(value, &b);
     				option->ternary_val = b ? PG_TERNARY_TRUE :
     					PG_TERNARY_FALSE;
    -				if (validate && !parsed)
    +
    +				/* If no "unset alias" set, this option behaves almost like
    +				 * boolean, so report error accordingly */
    +				if (!opt->unset_alias && validate && !parsed)
     					ereport(ERROR,
     							errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     							errmsg("invalid value for boolean option \"%s\": %s",
     								   option->gen->name, value));
    +
    +				if (!parsed && opt->unset_alias)
    +				{
    +					if (pg_strcasecmp(value, opt->unset_alias) == 0)
    +					{
    +						option->ternary_val = PG_TERNARY_UNSET;
    +						parsed = true;
    +					}
    +				}
    +				if (validate && !parsed)
    +					ereport(ERROR,
    +							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    +							errmsg("invalid value for option \"%s\": %s",
    +									option->gen->name, value),
    +							errdetail("Valid values are \"on\", \"off\", and \"%s\".",
    +										opt->unset_alias)));
     			}
     			break;
     		case RELOPT_TYPE_INT:
    @@ -2020,7 +2019,7 @@ default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
     		offsetof(StdRdOptions, user_catalog_table)},
     		{"parallel_workers", RELOPT_TYPE_INT,
     		offsetof(StdRdOptions, parallel_workers)},
    -		{"vacuum_index_cleanup", RELOPT_TYPE_ENUM,
    +		{"vacuum_index_cleanup", RELOPT_TYPE_TERNARY,
     		offsetof(StdRdOptions, vacuum_index_cleanup)},
     		{"vacuum_truncate", RELOPT_TYPE_TERNARY,
     		offsetof(StdRdOptions, vacuum_truncate)},
    diff --git a/src/backend/access/gist/gistbuild.c b/src/backend/access/gist/gistbuild.c
    index 7f57c787f4c..4031a07de0e 100644
    --- a/src/backend/access/gist/gistbuild.c
    +++ b/src/backend/access/gist/gistbuild.c
    @@ -213,9 +213,9 @@ gistbuild(Relation heap, Relation index, IndexInfo *indexInfo)
     	 */
     	if (options)
     	{
    -		if (options->buffering_mode == GIST_OPTION_BUFFERING_ON)
    +		if (options->buffering_mode == PG_TERNARY_TRUE)
     			buildstate.buildMode = GIST_BUFFERING_STATS;
    -		else if (options->buffering_mode == GIST_OPTION_BUFFERING_OFF)
    +		else if (options->buffering_mode == PG_TERNARY_FALSE)
     			buildstate.buildMode = GIST_BUFFERING_DISABLED;
     		else					/* must be "auto" */
     			buildstate.buildMode = GIST_BUFFERING_AUTO;
    diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
    index a4abb29cf64..08bb3ad6c6a 100644
    --- a/src/backend/commands/vacuum.c
    +++ b/src/backend/commands/vacuum.c
    @@ -2188,22 +2188,21 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params,
     	 */
     	if (params.index_cleanup == VACOPTVALUE_UNSPECIFIED)
     	{
    -		StdRdOptIndexCleanup vacuum_index_cleanup;
    +		pg_ternary vacuum_index_cleanup;
     
     		if (rel->rd_options == NULL)
    -			vacuum_index_cleanup = STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO;
    +			vacuum_index_cleanup = PG_TERNARY_UNSET;
     		else
     			vacuum_index_cleanup =
     				((StdRdOptions *) rel->rd_options)->vacuum_index_cleanup;
     
    -		if (vacuum_index_cleanup == STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO)
    +		if (vacuum_index_cleanup == PG_TERNARY_UNSET)
     			params.index_cleanup = VACOPTVALUE_AUTO;
    -		else if (vacuum_index_cleanup == STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON)
    +		else if (vacuum_index_cleanup == PG_TERNARY_TRUE)
     			params.index_cleanup = VACOPTVALUE_ENABLED;
     		else
     		{
    -			Assert(vacuum_index_cleanup ==
    -				   STDRD_OPTION_VACUUM_INDEX_CLEANUP_OFF);
    +			Assert(vacuum_index_cleanup == PG_TERNARY_FALSE);
     			params.index_cleanup = VACOPTVALUE_DISABLED;
     		}
     	}
    diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h
    index 44514f1cb8d..4307b8126a6 100644
    --- a/src/include/access/gist_private.h
    +++ b/src/include/access/gist_private.h
    @@ -380,14 +380,6 @@ typedef struct GISTBuildBuffers
     	int			rootlevel;
     } GISTBuildBuffers;
     
    -/* GiSTOptions->buffering_mode values */
    -typedef enum GistOptBufferingMode
    -{
    -	GIST_OPTION_BUFFERING_AUTO,
    -	GIST_OPTION_BUFFERING_ON,
    -	GIST_OPTION_BUFFERING_OFF,
    -} GistOptBufferingMode;
    -
     /*
      * Storage type for GiST's reloptions
      */
    @@ -395,7 +387,7 @@ typedef struct GiSTOptions
     {
     	int32		vl_len_;		/* varlena header (do not touch directly!) */
     	int			fillfactor;		/* page fill factor in percent (0..100) */
    -	GistOptBufferingMode buffering_mode;	/* buffering build mode */
    +	pg_ternary	buffering_mode;	/* buffering build mode */
     } GiSTOptions;
     
     /* gist.c */
    diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
    index e8cb7f7a627..1c5ea85e76d 100644
    --- a/src/include/access/reloptions.h
    +++ b/src/include/access/reloptions.h
    @@ -98,7 +98,8 @@ typedef struct relopt_bool
     typedef struct relopt_ternary
     {
     	relopt_gen	gen;
    -	/* ternaries have no default_val: otherwise they'd just be bools */
    +	const char	*unset_alias; /* word that will be treated as unset value */
    +	int			default_val;
     } relopt_ternary;
     
     typedef struct relopt_int
    @@ -190,7 +191,8 @@ extern relopt_kind add_reloption_kind(void);
     extern void add_bool_reloption(uint32 kinds, const char *name, const char *desc,
     							   bool default_val, LOCKMODE lockmode);
     extern void add_ternary_reloption(uint32 kinds, const char *name,
    -								  const char *desc, LOCKMODE lockmode);
    +					const char *desc, pg_ternary default_val,
    +					const char* unset_alias, LOCKMODE lockmode);
     extern void add_int_reloption(uint32 kinds, const char *name, const char *desc,
     							  int default_val, int min_val, int max_val,
     							  LOCKMODE lockmode);
    @@ -211,8 +213,9 @@ extern void add_local_bool_reloption(local_relopts *relopts, const char *name,
     									 const char *desc, bool default_val,
     									 int offset);
     extern void add_local_ternary_reloption(local_relopts *relopts,
    -										const char *name, const char *desc,
    -										int offset);
    +								const char *name, const char *desc,
    +								pg_ternary default_val, const char* unset_alias,
    +								int offset);
     extern void add_local_int_reloption(local_relopts *relopts, const char *name,
     									const char *desc, int default_val,
     									int min_val, int max_val, int offset);
    diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
    index cd1e92f2302..15b5bf39705 100644
    --- a/src/include/utils/rel.h
    +++ b/src/include/utils/rel.h
    @@ -332,14 +332,6 @@ typedef struct AutoVacOpts
     	float8		analyze_scale_factor;
     } AutoVacOpts;
     
    -/* StdRdOptions->vacuum_index_cleanup values */
    -typedef enum StdRdOptIndexCleanup
    -{
    -	STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO = 0,
    -	STDRD_OPTION_VACUUM_INDEX_CLEANUP_OFF,
    -	STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON,
    -} StdRdOptIndexCleanup;
    -
     typedef struct StdRdOptions
     {
     	int32		vl_len_;		/* varlena header (do not touch directly!) */
    @@ -348,7 +340,7 @@ typedef struct StdRdOptions
     	AutoVacOpts autovacuum;		/* autovacuum-related options */
     	bool		user_catalog_table; /* use as an additional catalog relation */
     	int			parallel_workers;	/* max number of parallel workers */
    -	StdRdOptIndexCleanup vacuum_index_cleanup;	/* controls index vacuuming */
    +	pg_ternary	vacuum_index_cleanup; /* controls index vacuuming */
     	pg_ternary	vacuum_truncate;	/* enables vacuum to truncate a relation */
     
     	/*
    diff --git a/src/test/modules/dummy_index_am/README b/src/test/modules/dummy_index_am/README
    index 604d823c2e4..d80aff0db19 100644
    --- a/src/test/modules/dummy_index_am/README
    +++ b/src/test/modules/dummy_index_am/README
    @@ -5,7 +5,8 @@ Dummy index AM is a module for testing any facility usable by an index
     access method, whose code is kept a maximum simple.
     
     This includes tests for all relation option types:
    -- boolean & ternary
    +- boolean
    +- ternary
     - enum
     - integer
     - real
    diff --git a/src/test/modules/dummy_index_am/dummy_index_am.c b/src/test/modules/dummy_index_am/dummy_index_am.c
    index 31f8d2b8161..e8f6dcac9de 100644
    --- a/src/test/modules/dummy_index_am/dummy_index_am.c
    +++ b/src/test/modules/dummy_index_am/dummy_index_am.c
    @@ -41,6 +41,7 @@ typedef struct DummyIndexOptions
     	double		option_real;
     	bool		option_bool;
     	pg_ternary	option_ternary_1;
    +	pg_ternary	option_ternary_2;
     	DummyAmEnum option_enum;
     	int			option_string_val_offset;
     	int			option_string_null_offset;
    @@ -104,12 +105,20 @@ create_reloptions_table(void)
     
     	add_ternary_reloption(di_relopt_kind, "option_ternary_1",
     						  "One ternary option for dummy_index_am",
    -						  AccessExclusiveLock);
    +					   PG_TERNARY_UNSET, NULL, AccessExclusiveLock);
     	di_relopt_tab[i].optname = "option_ternary_1";
     	di_relopt_tab[i].opttype = RELOPT_TYPE_TERNARY;
     	di_relopt_tab[i].offset = offsetof(DummyIndexOptions, option_ternary_1);
     	i++;
     
    +	add_ternary_reloption(di_relopt_kind, "option_ternary_2",
    +					   "Second ternary option for dummy_index_am",
    +					   PG_TERNARY_TRUE, "do_not_know_yet", AccessExclusiveLock);
    +	di_relopt_tab[i].optname = "option_ternary_2";
    +	di_relopt_tab[i].opttype = RELOPT_TYPE_TERNARY;
    +	di_relopt_tab[i].offset = offsetof(DummyIndexOptions, option_ternary_2);
    +	i++;
    +
     	add_enum_reloption(di_relopt_kind, "option_enum",
     					   "Enum option for dummy_index_am",
     					   dummyAmEnumValues,
    diff --git a/src/test/modules/dummy_index_am/expected/reloptions.out b/src/test/modules/dummy_index_am/expected/reloptions.out
    index 3b06d514995..90abbe2e373 100644
    --- a/src/test/modules/dummy_index_am/expected/reloptions.out
    +++ b/src/test/modules/dummy_index_am/expected/reloptions.out
    @@ -19,6 +19,7 @@ CREATE INDEX dummy_test_idx ON dummy_test_tab
       USING dummy_index_am (i) WITH (
       option_bool = false,
       option_ternary_1,
    +  option_ternary_2 = off,
       option_int = 5,
       option_real = 3.1,
       option_enum = 'two',
    @@ -33,17 +34,19 @@ SELECT unnest(reloptions) FROM pg_class WHERE relname = 'dummy_test_idx';
     ------------------------
      option_bool=false
      option_ternary_1=true
    + option_ternary_2=off
      option_int=5
      option_real=3.1
      option_enum=two
      option_string_val=null
      option_string_null=val
    -(7 rows)
    +(8 rows)
     
     -- ALTER INDEX .. SET
     ALTER INDEX dummy_test_idx SET (option_int = 10);
     ALTER INDEX dummy_test_idx SET (option_bool = true);
     ALTER INDEX dummy_test_idx SET (option_ternary_1 = false);
    +ALTER INDEX dummy_test_idx SET (option_ternary_2 = Do_Not_Know_YET);
     ALTER INDEX dummy_test_idx SET (option_real = 3.2);
     ALTER INDEX dummy_test_idx SET (option_string_val = 'val2');
     ALTER INDEX dummy_test_idx SET (option_string_null = NULL);
    @@ -52,21 +55,23 @@ ALTER INDEX dummy_test_idx SET (option_enum = 'three');
     ERROR:  invalid value for enum option "option_enum": three
     DETAIL:  Valid values are "one" and "two".
     SELECT unnest(reloptions) FROM pg_class WHERE relname = 'dummy_test_idx';
    -         unnest          
    --------------------------
    +              unnest              
    +----------------------------------
      option_int=10
      option_bool=true
      option_ternary_1=false
    + option_ternary_2=do_not_know_yet
      option_real=3.2
      option_string_val=val2
      option_string_null=null
      option_enum=one
    -(7 rows)
    +(8 rows)
     
     -- ALTER INDEX .. RESET
     ALTER INDEX dummy_test_idx RESET (option_int);
     ALTER INDEX dummy_test_idx RESET (option_bool);
     ALTER INDEX dummy_test_idx RESET (option_ternary_1);
    +ALTER INDEX dummy_test_idx RESET (option_ternary_2);
     ALTER INDEX dummy_test_idx RESET (option_real);
     ALTER INDEX dummy_test_idx RESET (option_enum);
     ALTER INDEX dummy_test_idx RESET (option_string_val);
    @@ -113,13 +118,21 @@ ALTER INDEX dummy_test_idx SET (option_ternary_1 = 3.4); -- error
     ERROR:  invalid value for boolean option "option_ternary_1": 3.4
     ALTER INDEX dummy_test_idx SET (option_ternary_1 = 'val4'); -- error
     ERROR:  invalid value for boolean option "option_ternary_1": val4
    +ALTER INDEX dummy_test_idx SET (option_ternary_1 = 'do_not_know_yet'); -- error. Valid for ternary2 not for ternary1
    +ERROR:  invalid value for boolean option "option_ternary_1": do_not_know_yet
    +ALTER INDEX dummy_test_idx SET (option_ternary_2 = 'do_not_know_yet'); -- ok
    +ALTER INDEX dummy_test_idx SET (option_ternary_2 = 'illegal_value'); -- error
    +ERROR:  invalid value for option "option_ternary_2": illegal_value
    +DETAIL:  Valid values are "on", "off", and "do_not_know_yet".
     SELECT unnest(reloptions) FROM pg_class WHERE relname = 'dummy_test_idx';
    -       unnest       
    ---------------------
    +              unnest              
    +----------------------------------
      option_ternary_1=1
    -(1 row)
    + option_ternary_2=do_not_know_yet
    +(2 rows)
     
     ALTER INDEX dummy_test_idx RESET (option_ternary_1);
    +ALTER INDEX dummy_test_idx RESET (option_ternary_2);
     -- Float
     ALTER INDEX dummy_test_idx SET (option_real = 4); -- ok
     ALTER INDEX dummy_test_idx SET (option_real = true); -- error
    diff --git a/src/test/modules/dummy_index_am/sql/reloptions.sql b/src/test/modules/dummy_index_am/sql/reloptions.sql
    index 2cdff0820f6..f8b985055c1 100644
    --- a/src/test/modules/dummy_index_am/sql/reloptions.sql
    +++ b/src/test/modules/dummy_index_am/sql/reloptions.sql
    @@ -19,6 +19,7 @@ CREATE INDEX dummy_test_idx ON dummy_test_tab
       USING dummy_index_am (i) WITH (
       option_bool = false,
       option_ternary_1,
    +  option_ternary_2 = off,
       option_int = 5,
       option_real = 3.1,
       option_enum = 'two',
    @@ -32,6 +33,7 @@ SELECT unnest(reloptions) FROM pg_class WHERE relname = 'dummy_test_idx';
     ALTER INDEX dummy_test_idx SET (option_int = 10);
     ALTER INDEX dummy_test_idx SET (option_bool = true);
     ALTER INDEX dummy_test_idx SET (option_ternary_1 = false);
    +ALTER INDEX dummy_test_idx SET (option_ternary_2 = Do_Not_Know_YET);
     ALTER INDEX dummy_test_idx SET (option_real = 3.2);
     ALTER INDEX dummy_test_idx SET (option_string_val = 'val2');
     ALTER INDEX dummy_test_idx SET (option_string_null = NULL);
    @@ -43,6 +45,7 @@ SELECT unnest(reloptions) FROM pg_class WHERE relname = 'dummy_test_idx';
     ALTER INDEX dummy_test_idx RESET (option_int);
     ALTER INDEX dummy_test_idx RESET (option_bool);
     ALTER INDEX dummy_test_idx RESET (option_ternary_1);
    +ALTER INDEX dummy_test_idx RESET (option_ternary_2);
     ALTER INDEX dummy_test_idx RESET (option_real);
     ALTER INDEX dummy_test_idx RESET (option_enum);
     ALTER INDEX dummy_test_idx RESET (option_string_val);
    @@ -68,8 +71,12 @@ ALTER INDEX dummy_test_idx SET (option_ternary_1 = 4); -- error
     ALTER INDEX dummy_test_idx SET (option_ternary_1 = 1); -- ok, as true
     ALTER INDEX dummy_test_idx SET (option_ternary_1 = 3.4); -- error
     ALTER INDEX dummy_test_idx SET (option_ternary_1 = 'val4'); -- error
    +ALTER INDEX dummy_test_idx SET (option_ternary_1 = 'do_not_know_yet'); -- error. Valid for ternary2 not for ternary1
    +ALTER INDEX dummy_test_idx SET (option_ternary_2 = 'do_not_know_yet'); -- ok
    +ALTER INDEX dummy_test_idx SET (option_ternary_2 = 'illegal_value'); -- error
     SELECT unnest(reloptions) FROM pg_class WHERE relname = 'dummy_test_idx';
     ALTER INDEX dummy_test_idx RESET (option_ternary_1);
    +ALTER INDEX dummy_test_idx RESET (option_ternary_2);
     -- Float
     ALTER INDEX dummy_test_idx SET (option_real = 4); -- ok
     ALTER INDEX dummy_test_idx SET (option_real = true); -- error
    diff --git a/src/test/regress/expected/gist.out b/src/test/regress/expected/gist.out
    index c75bbb23b6e..19dc547d5f8 100644
    --- a/src/test/regress/expected/gist.out
    +++ b/src/test/regress/expected/gist.out
    @@ -12,7 +12,7 @@ create index gist_pointidx4 on gist_point_tbl using gist(p) with (buffering = au
     drop index gist_pointidx2, gist_pointidx3, gist_pointidx4;
     -- Make sure bad values are refused
     create index gist_pointidx5 on gist_point_tbl using gist(p) with (buffering = invalid_value);
    -ERROR:  invalid value for enum option "buffering": invalid_value
    +ERROR:  invalid value for option "buffering": invalid_value
     DETAIL:  Valid values are "on", "off", and "auto".
     create index gist_pointidx5 on gist_point_tbl using gist(p) with (fillfactor=9);
     ERROR:  value 9 out of bounds for option "fillfactor"
    diff --git a/src/test/regress/expected/reloptions.out b/src/test/regress/expected/reloptions.out
    index e3a974f2611..6e65cd5c3da 100644
    --- a/src/test/regress/expected/reloptions.out
    +++ b/src/test/regress/expected/reloptions.out
    @@ -116,6 +116,24 @@ SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
      {vacuum_truncate=fals}
     (1 row)
     
    +-- preferred "true" alias is stored in pg_class
    +DROP TABLE reloptions_test;
    +CREATE TABLE reloptions_test(i INT) WITH (vacuum_index_cleanup=on);
    +SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
    +        reloptions         
    +---------------------------
    + {vacuum_index_cleanup=on}
    +(1 row)
    +
    +-- custom "third" value is available
    +DROP TABLE reloptions_test;
    +CREATE TABLE reloptions_test(i INT) WITH (vacuum_index_cleanup=auto);
    +SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
    +         reloptions          
    +-----------------------------
    + {vacuum_index_cleanup=auto}
    +(1 row)
    +
     -- Test vacuum_truncate option
     DROP TABLE reloptions_test;
     CREATE TEMP TABLE reloptions_test(i INT NOT NULL, j text)
    diff --git a/src/test/regress/sql/reloptions.sql b/src/test/regress/sql/reloptions.sql
    index 680c8bf8614..c99673db9ec 100644
    --- a/src/test/regress/sql/reloptions.sql
    +++ b/src/test/regress/sql/reloptions.sql
    @@ -70,6 +70,16 @@ DROP TABLE reloptions_test;
     CREATE TABLE reloptions_test(i INT) WITH (vacuum_truncate=FaLS);
     SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
     
    +-- preferred "true" alias is stored in pg_class
    +DROP TABLE reloptions_test;
    +CREATE TABLE reloptions_test(i INT) WITH (vacuum_index_cleanup=on);
    +SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
    +
    +-- custom "third" value is available
    +DROP TABLE reloptions_test;
    +CREATE TABLE reloptions_test(i INT) WITH (vacuum_index_cleanup=auto);
    +SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
    +
     -- Test vacuum_truncate option
     DROP TABLE reloptions_test;
     
    -- 
    2.47.3
    
    
    --nextPart2058770.usQuhbGJ8B--
    
    --nextPart3610129.QJadu78ljV
    Content-Type: application/pgp-signature; name="signature.asc"
    Content-Description: This is a digitally signed message part.
    Content-Transfer-Encoding: 7Bit
    
    -----BEGIN PGP SIGNATURE-----
    
    iQEzBAABCgAdFiEE+sk3ebqQKlezKOi8PMbfuIHAGpgFAmoAZ2gACgkQPMbfuIHA
    GpirwwgAnT+MgmIE5m1ayaChqoJfg0n4gzUrk5r4Mo7FVDBt+NgYvHwsV6NCtxC4
    fPuprZV17jGDQPaJ0HKFtuOmJqa3ozoTzFm4n6l/rn5x3HV6IBrPGwy3X9QV57Wk
    cvhTBSzgyy7GhJB20718fbgaFoUnUhI/ieQODJzPecbdUdgle6bn6gnkRiUK72Kr
    /Hu8qc15vuyh/GLJUINAM/m2goNVTEb6mVcZh6zoV1FZfGYvw4X5+sNO+beg8NkW
    9UZUJkIr0qolAPS1zstrPHk0MG5YfL2PR54AK7wXCT2ZUQgtlks3ghvbZINMCEmO
    x1AFchU+EQSS4RzdiSF5NgIcJYfpsw==
    =Qsjj
    -----END PGP SIGNATURE-----
    
    --nextPart3610129.QJadu78ljV--