Allow Boolean reloptions to have ternary values
authorÁlvaro Herrera <alvherre@kurilemu.de>
Wed, 21 Jan 2026 19:06:01 +0000 (20:06 +0100)
committerÁlvaro Herrera <alvherre@kurilemu.de>
Wed, 21 Jan 2026 19:06:01 +0000 (20:06 +0100)
From the user's point of view these are just Boolean values; from the
implementation side we can now distinguish an option that hasn't been
set.  Reimplement the vacuum_truncate reloption using this type.

This could also be used for reloptions vacuum_index_cleanup and
buffering, but those additionally need a per-option "alias" for the
state where the variable is unset (currently the value "auto").

Author: Nikolay Shaplov <dhyan@nataraj.su>
Reviewed-by: Timur Magomedov <t.magomedov@postgrespro.ru>
Reviewed-by: Nathan Bossart <nathandbossart@gmail.com>
Discussion: https://postgr.es/m/3474141.usfYGdeWWP@thinkpad-pgpro

12 files changed:
src/backend/access/common/reloptions.c
src/backend/commands/vacuum.c
src/include/access/reloptions.h
src/include/postgres.h
src/include/utils/rel.h
src/test/modules/dummy_index_am/README
src/test/modules/dummy_index_am/dummy_index_am.c
src/test/modules/dummy_index_am/expected/reloptions.out
src/test/modules/dummy_index_am/sql/reloptions.sql
src/test/regress/expected/reloptions.out
src/test/regress/sql/reloptions.sql
src/tools/pgindent/typedefs.list

index 0b83f98ed5f0f926bb3eaa175d26dead16321667..83feaaba7f135ea58139098c5b597fb32a24f248 100644 (file)
@@ -40,9 +40,9 @@
  *
  * To add an option:
  *
- * (i) decide on a type (bool, integer, real, enum, string), name, default
- * value, upper and lower bounds (if applicable); for strings, consider a
- * validation routine.
+ * (i) decide on a type (bool, ternary, integer, real, enum, string), name,
+ * default value, upper and lower bounds (if applicable); for strings,
+ * consider a validation routine.
  * (ii) add a record below (or use add_<type>_reloption).
  * (iii) add it to the appropriate options struct (perhaps StdRdOptions)
  * (iv) add it to the appropriate handling routine (perhaps
  * (v) make sure the lock level is set correctly for that operation
  * (vi) don't forget to document the option
  *
+ * From the user's point of view, a 'ternary' is exactly like a Boolean,
+ * so we don't document it separately.  On the implementation side, the
+ * handling code can detect the case where the option has not been set.
+ *
  * The default choice for any new option should be AccessExclusiveLock.
  * In some cases the lock level can be reduced from there, but the lock
  * level chosen should always conflict with itself to ensure that multiple
@@ -147,15 +151,6 @@ static relopt_bool boolRelOpts[] =
                },
                false
        },
-       {
-               {
-                       "vacuum_truncate",
-                       "Enables vacuum to truncate empty pages at the end of this table",
-                       RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
-                       ShareUpdateExclusiveLock
-               },
-               true
-       },
        {
                {
                        "deduplicate_items",
@@ -170,6 +165,24 @@ static relopt_bool boolRelOpts[] =
        {{NULL}}
 };
 
+static relopt_ternary ternaryRelOpts[] =
+{
+       {
+               {
+                       "vacuum_truncate",
+                       "Enables vacuum to truncate empty pages at the end of this table",
+                       RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
+                       ShareUpdateExclusiveLock
+               }
+       },
+       /* list terminator */
+       {
+               {
+                       NULL
+               }
+       }
+};
+
 static relopt_int intRelOpts[] =
 {
        {
@@ -609,6 +622,13 @@ initialize_reloptions(void)
                                                                   boolRelOpts[i].gen.lockmode));
                j++;
        }
+       for (i = 0; ternaryRelOpts[i].gen.name; i++)
+       {
+               Assert(DoLockModesConflict(ternaryRelOpts[i].gen.lockmode,
+                                                                  ternaryRelOpts[i].gen.lockmode));
+               j++;
+       }
+
        for (i = 0; intRelOpts[i].gen.name; i++)
        {
                Assert(DoLockModesConflict(intRelOpts[i].gen.lockmode,
@@ -649,6 +669,14 @@ initialize_reloptions(void)
                j++;
        }
 
+       for (i = 0; ternaryRelOpts[i].gen.name; i++)
+       {
+               relOpts[j] = &ternaryRelOpts[i].gen;
+               relOpts[j]->type = RELOPT_TYPE_TERNARY;
+               relOpts[j]->namelen = strlen(relOpts[j]->name);
+               j++;
+       }
+
        for (i = 0; intRelOpts[i].gen.name; i++)
        {
                relOpts[j] = &intRelOpts[i].gen;
@@ -809,6 +837,9 @@ allocate_reloption(bits32 kinds, int type, const char *name, const char *desc,
                case RELOPT_TYPE_BOOL:
                        size = sizeof(relopt_bool);
                        break;
+               case RELOPT_TYPE_TERNARY:
+                       size = sizeof(relopt_ternary);
+                       break;
                case RELOPT_TYPE_INT:
                        size = sizeof(relopt_int);
                        break;
@@ -892,6 +923,55 @@ add_local_bool_reloption(local_relopts *relopts, const char *name,
        add_local_reloption(relopts, (relopt_gen *) newoption, offset);
 }
 
+/*
+ * init_ternary_reloption
+ *             Allocate and initialize a new ternary reloption
+ */
+static relopt_ternary *
+init_ternary_reloption(bits32 kinds, const char *name, const char *desc,
+                                          LOCKMODE lockmode)
+{
+       relopt_ternary *newoption;
+
+       newoption = (relopt_ternary *)
+               allocate_reloption(kinds, RELOPT_TYPE_TERNARY, name, desc, lockmode);
+
+       return newoption;
+}
+
+/*
+ * add_ternary_reloption
+ *             Add a new ternary reloption
+ */
+void
+add_ternary_reloption(bits32 kinds, const char *name, const char *desc,
+                                         LOCKMODE lockmode)
+{
+       relopt_ternary *newoption;
+
+       newoption =
+               init_ternary_reloption(kinds, name, desc, lockmode);
+
+       add_reloption((relopt_gen *) newoption);
+}
+
+/*
+ * add_local_ternary_reloption
+ *             Add a new ternary local reloption
+ *
+ * 'offset' is offset of ternary-typed field.
+ */
+void
+add_local_ternary_reloption(local_relopts *relopts, const char *name,
+                                                       const char *desc, int offset)
+{
+       relopt_ternary *newoption;
+
+       newoption =
+               init_ternary_reloption(RELOPT_KIND_LOCAL, name, desc, 0);
+
+       add_local_reloption(relopts, (relopt_gen *) newoption, offset);
+}
 
 /*
  * init_real_reloption
@@ -1626,6 +1706,20 @@ parse_one_reloption(relopt_value *option, char *text_str, int text_len,
                                                                        option->gen->name, value)));
                        }
                        break;
+               case RELOPT_TYPE_TERNARY:
+                       {
+                               bool            b;
+
+                               parsed = parse_bool(value, &b);
+                               option->values.ternary_val = b ? PG_TERNARY_TRUE :
+                                       PG_TERNARY_FALSE;
+                               if (validate && !parsed)
+                                       ereport(ERROR,
+                                                       errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                                       errmsg("invalid value for boolean option \"%s\": %s",
+                                                                  option->gen->name, value));
+                       }
+                       break;
                case RELOPT_TYPE_INT:
                        {
                                relopt_int *optint = (relopt_int *) option->gen;
@@ -1789,17 +1883,6 @@ fillRelOptions(void *rdopts, Size basesize,
                                char       *itempos = ((char *) rdopts) + elems[j].offset;
                                char       *string_val;
 
-                               /*
-                                * If isset_offset is provided, store whether the reloption is
-                                * set there.
-                                */
-                               if (elems[j].isset_offset > 0)
-                               {
-                                       char       *setpos = ((char *) rdopts) + elems[j].isset_offset;
-
-                                       *(bool *) setpos = options[i].isset;
-                               }
-
                                switch (options[i].gen->type)
                                {
                                        case RELOPT_TYPE_BOOL:
@@ -1807,6 +1890,10 @@ fillRelOptions(void *rdopts, Size basesize,
                                                        options[i].values.bool_val :
                                                        ((relopt_bool *) options[i].gen)->default_val;
                                                break;
+                                       case RELOPT_TYPE_TERNARY:
+                                               *(pg_ternary *) itempos = options[i].isset ?
+                                                       options[i].values.ternary_val : PG_TERNARY_UNSET;
+                                               break;
                                        case RELOPT_TYPE_INT:
                                                *(int *) itempos = options[i].isset ?
                                                        options[i].values.int_val :
@@ -1923,8 +2010,8 @@ default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
                offsetof(StdRdOptions, parallel_workers)},
                {"vacuum_index_cleanup", RELOPT_TYPE_ENUM,
                offsetof(StdRdOptions, vacuum_index_cleanup)},
-               {"vacuum_truncate", RELOPT_TYPE_BOOL,
-               offsetof(StdRdOptions, vacuum_truncate), offsetof(StdRdOptions, vacuum_truncate_set)},
+               {"vacuum_truncate", RELOPT_TYPE_TERNARY,
+               offsetof(StdRdOptions, vacuum_truncate)},
                {"vacuum_max_eager_freeze_failure_rate", RELOPT_TYPE_REAL,
                offsetof(StdRdOptions, vacuum_max_eager_freeze_failure_rate)}
        };
@@ -2004,7 +2091,6 @@ build_local_reloptions(local_relopts *relopts, Datum options, bool validate)
                elems[i].optname = opt->option->name;
                elems[i].opttype = opt->option->type;
                elems[i].offset = opt->offset;
-               elems[i].isset_offset = 0;      /* not supported for local relopts yet */
 
                i++;
        }
index aa4fbec143f9434bed27fd2670f27d641ef2332d..03932f45c8ade610c44cd565d7b304e820490995 100644 (file)
@@ -2224,9 +2224,9 @@ vacuum_rel(Oid relid, RangeVar *relation, VacuumParams params,
        {
                StdRdOptions *opts = (StdRdOptions *) rel->rd_options;
 
-               if (opts && opts->vacuum_truncate_set)
+               if (opts && opts->vacuum_truncate != PG_TERNARY_UNSET)
                {
-                       if (opts->vacuum_truncate)
+                       if (opts->vacuum_truncate == PG_TERNARY_TRUE)
                                params.truncate = VACOPTVALUE_ENABLED;
                        else
                                params.truncate = VACOPTVALUE_DISABLED;
index 2f08e1b0cf02e64d7c371235e564739eb84e3287..a3f6f5a3990b95f89f18023cce91ae604f58a40f 100644 (file)
@@ -29,6 +29,7 @@
 typedef enum relopt_type
 {
        RELOPT_TYPE_BOOL,
+       RELOPT_TYPE_TERNARY,            /* on, off, unset */
        RELOPT_TYPE_INT,
        RELOPT_TYPE_REAL,
        RELOPT_TYPE_ENUM,
@@ -80,6 +81,7 @@ typedef struct relopt_value
        union
        {
                bool            bool_val;
+               pg_ternary      ternary_val;
                int                     int_val;
                double          real_val;
                int                     enum_val;
@@ -94,6 +96,12 @@ typedef struct relopt_bool
        bool            default_val;
 } relopt_bool;
 
+typedef struct relopt_ternary
+{
+       relopt_gen      gen;
+       /* ternaries have no default_val: otherwise they'd just be bools */
+} relopt_ternary;
+
 typedef struct relopt_int
 {
        relopt_gen      gen;
@@ -152,19 +160,6 @@ typedef struct
        const char *optname;            /* option's name */
        relopt_type opttype;            /* option's datatype */
        int                     offset;                 /* offset of field in result struct */
-
-       /*
-        * isset_offset is an optional offset of a field in the result struct that
-        * stores whether the option is explicitly set for the relation or if it
-        * just picked up the default value.  In most cases, this can be
-        * accomplished by giving the reloption a special out-of-range default
-        * value (e.g., some integer reloptions use -2), but this isn't always
-        * possible.  For example, a Boolean reloption cannot be given an
-        * out-of-range default, so we need another way to discover the source of
-        * its value.  This offset is only used if given a value greater than
-        * zero.
-        */
-       int                     isset_offset;
 } relopt_parse_elt;
 
 /* Local reloption definition */
@@ -195,6 +190,8 @@ typedef struct local_relopts
 extern relopt_kind add_reloption_kind(void);
 extern void add_bool_reloption(bits32 kinds, const char *name, const char *desc,
                                                           bool default_val, LOCKMODE lockmode);
+extern void add_ternary_reloption(bits32 kinds, const char *name,
+                                                                 const char *desc, LOCKMODE lockmode);
 extern void add_int_reloption(bits32 kinds, const char *name, const char *desc,
                                                          int default_val, int min_val, int max_val,
                                                          LOCKMODE lockmode);
@@ -214,6 +211,9 @@ extern void register_reloptions_validator(local_relopts *relopts,
 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);
 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);
index 7d93fbce7094a1a6db3aac5479f5e7ec1d4c7558..8b92f453e7ab329c9dd201ef12af5fcd1a8baac7 100644 (file)
@@ -543,6 +543,20 @@ Float8GetDatum(float8 X)
  * ----------------------------------------------------------------
  */
 
+/*
+ * pg_ternary
+ *             Boolean value with an extra "unset" value
+ *
+ * This enum can be used for values that want to distinguish between true,
+ * false, and unset.
+*/
+typedef enum pg_ternary
+{
+       PG_TERNARY_FALSE = 0,
+       PG_TERNARY_TRUE = 1,
+       PG_TERNARY_UNSET = -1
+} pg_ternary;
+
 /*
  * NON_EXEC_STATIC: It's sometimes useful to define a variable or function
  * that is normally static but extern when using EXEC_BACKEND (see
index d03ab247788b5cfd41828a0e61164a20a102a45e..236830f6b93f16ed860ebe0fcf6d4ba924e6c2ae 100644 (file)
@@ -347,8 +347,7 @@ typedef struct StdRdOptions
        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 */
-       bool            vacuum_truncate;        /* enables vacuum to truncate a relation */
-       bool            vacuum_truncate_set;    /* whether vacuum_truncate is set */
+       pg_ternary      vacuum_truncate;        /* enables vacuum to truncate a relation */
 
        /*
         * Fraction of pages in a relation that vacuum can eagerly scan and fail
index 61510f02faea399eb6348f10b05e3e7e1e9b763a..604d823c2e4420ec16b4db243279c7760a8f5a3a 100644 (file)
@@ -5,7 +5,7 @@ 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
+- boolean & ternary
 - enum
 - integer
 - real
index 9eb8f0a6c63353d6b90d9772163a35ed81550746..31f8d2b816155a032b3107dd4ec79ae72236a5d6 100644 (file)
@@ -22,7 +22,7 @@
 PG_MODULE_MAGIC;
 
 /* parse table for fillRelOptions */
-static relopt_parse_elt di_relopt_tab[6];
+static relopt_parse_elt di_relopt_tab[8];
 
 /* Kind of relation options for dummy index */
 static relopt_kind di_relopt_kind;
@@ -40,6 +40,7 @@ typedef struct DummyIndexOptions
        int                     option_int;
        double          option_real;
        bool            option_bool;
+       pg_ternary      option_ternary_1;
        DummyAmEnum option_enum;
        int                     option_string_val_offset;
        int                     option_string_null_offset;
@@ -73,28 +74,41 @@ validate_string_option(const char *value)
 static void
 create_reloptions_table(void)
 {
+       int                     i = 0;
+
        di_relopt_kind = add_reloption_kind();
 
        add_int_reloption(di_relopt_kind, "option_int",
                                          "Integer option for dummy_index_am",
                                          10, -10, 100, AccessExclusiveLock);
-       di_relopt_tab[0].optname = "option_int";
-       di_relopt_tab[0].opttype = RELOPT_TYPE_INT;
-       di_relopt_tab[0].offset = offsetof(DummyIndexOptions, option_int);
+       di_relopt_tab[i].optname = "option_int";
+       di_relopt_tab[i].opttype = RELOPT_TYPE_INT;
+       di_relopt_tab[i].offset = offsetof(DummyIndexOptions, option_int);
+       i++;
 
        add_real_reloption(di_relopt_kind, "option_real",
                                           "Real option for dummy_index_am",
                                           3.1415, -10, 100, AccessExclusiveLock);
-       di_relopt_tab[1].optname = "option_real";
-       di_relopt_tab[1].opttype = RELOPT_TYPE_REAL;
-       di_relopt_tab[1].offset = offsetof(DummyIndexOptions, option_real);
+       di_relopt_tab[i].optname = "option_real";
+       di_relopt_tab[i].opttype = RELOPT_TYPE_REAL;
+       di_relopt_tab[i].offset = offsetof(DummyIndexOptions, option_real);
+       i++;
 
        add_bool_reloption(di_relopt_kind, "option_bool",
                                           "Boolean option for dummy_index_am",
                                           true, AccessExclusiveLock);
-       di_relopt_tab[2].optname = "option_bool";
-       di_relopt_tab[2].opttype = RELOPT_TYPE_BOOL;
-       di_relopt_tab[2].offset = offsetof(DummyIndexOptions, option_bool);
+       di_relopt_tab[i].optname = "option_bool";
+       di_relopt_tab[i].opttype = RELOPT_TYPE_BOOL;
+       di_relopt_tab[i].offset = offsetof(DummyIndexOptions, option_bool);
+       i++;
+
+       add_ternary_reloption(di_relopt_kind, "option_ternary_1",
+                                                 "One ternary option for dummy_index_am",
+                                                 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_enum_reloption(di_relopt_kind, "option_enum",
                                           "Enum option for dummy_index_am",
@@ -102,18 +116,20 @@ create_reloptions_table(void)
                                           DUMMY_AM_ENUM_ONE,
                                           "Valid values are \"one\" and \"two\".",
                                           AccessExclusiveLock);
-       di_relopt_tab[3].optname = "option_enum";
-       di_relopt_tab[3].opttype = RELOPT_TYPE_ENUM;
-       di_relopt_tab[3].offset = offsetof(DummyIndexOptions, option_enum);
+       di_relopt_tab[i].optname = "option_enum";
+       di_relopt_tab[i].opttype = RELOPT_TYPE_ENUM;
+       di_relopt_tab[i].offset = offsetof(DummyIndexOptions, option_enum);
+       i++;
 
        add_string_reloption(di_relopt_kind, "option_string_val",
                                                 "String option for dummy_index_am with non-NULL default",
                                                 "DefaultValue", &validate_string_option,
                                                 AccessExclusiveLock);
-       di_relopt_tab[4].optname = "option_string_val";
-       di_relopt_tab[4].opttype = RELOPT_TYPE_STRING;
-       di_relopt_tab[4].offset = offsetof(DummyIndexOptions,
+       di_relopt_tab[i].optname = "option_string_val";
+       di_relopt_tab[i].opttype = RELOPT_TYPE_STRING;
+       di_relopt_tab[i].offset = offsetof(DummyIndexOptions,
                                                                           option_string_val_offset);
+       i++;
 
        /*
         * String option for dummy_index_am with NULL default, and without
@@ -123,10 +139,11 @@ create_reloptions_table(void)
                                                 NULL,  /* description */
                                                 NULL, &validate_string_option,
                                                 AccessExclusiveLock);
-       di_relopt_tab[5].optname = "option_string_null";
-       di_relopt_tab[5].opttype = RELOPT_TYPE_STRING;
-       di_relopt_tab[5].offset = offsetof(DummyIndexOptions,
+       di_relopt_tab[i].optname = "option_string_null";
+       di_relopt_tab[i].opttype = RELOPT_TYPE_STRING;
+       di_relopt_tab[i].offset = offsetof(DummyIndexOptions,
                                                                           option_string_null_offset);
+       i++;
 }
 
 
index c873a80bb75d721a821a6eaf45a964e257a7490b..3b06d514995164f5b1892b9edde6860a1f2b6553 100644 (file)
@@ -18,6 +18,7 @@ SET client_min_messages TO 'notice';
 CREATE INDEX dummy_test_idx ON dummy_test_tab
   USING dummy_index_am (i) WITH (
   option_bool = false,
+  option_ternary_1,
   option_int = 5,
   option_real = 3.1,
   option_enum = 'two',
@@ -31,16 +32,18 @@ SELECT unnest(reloptions) FROM pg_class WHERE relname = 'dummy_test_idx';
          unnest         
 ------------------------
  option_bool=false
+ option_ternary_1=true
  option_int=5
  option_real=3.1
  option_enum=two
  option_string_val=null
  option_string_null=val
-(6 rows)
+(7 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_real = 3.2);
 ALTER INDEX dummy_test_idx SET (option_string_val = 'val2');
 ALTER INDEX dummy_test_idx SET (option_string_null = NULL);
@@ -53,15 +56,17 @@ SELECT unnest(reloptions) FROM pg_class WHERE relname = 'dummy_test_idx';
 -------------------------
  option_int=10
  option_bool=true
+ option_ternary_1=false
  option_real=3.2
  option_string_val=val2
  option_string_null=null
  option_enum=one
-(6 rows)
+(7 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_real);
 ALTER INDEX dummy_test_idx RESET (option_enum);
 ALTER INDEX dummy_test_idx RESET (option_string_val);
@@ -100,6 +105,21 @@ SELECT unnest(reloptions) FROM pg_class WHERE relname = 'dummy_test_idx';
 (1 row)
 
 ALTER INDEX dummy_test_idx RESET (option_bool);
+-- Ternary
+ALTER INDEX dummy_test_idx SET (option_ternary_1 = 4); -- error
+ERROR:  invalid value for boolean option "option_ternary_1": 4
+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
+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
+SELECT unnest(reloptions) FROM pg_class WHERE relname = 'dummy_test_idx';
+       unnest       
+--------------------
+ option_ternary_1=1
+(1 row)
+
+ALTER INDEX dummy_test_idx RESET (option_ternary_1);
 -- Float
 ALTER INDEX dummy_test_idx SET (option_real = 4); -- ok
 ALTER INDEX dummy_test_idx SET (option_real = true); -- error
index 6749d763e6aa4e43573a8e361029788753f6c97e..2cdff0820f65f810e455f00df6e47e886cb2889e 100644 (file)
@@ -18,6 +18,7 @@ SET client_min_messages TO 'notice';
 CREATE INDEX dummy_test_idx ON dummy_test_tab
   USING dummy_index_am (i) WITH (
   option_bool = false,
+  option_ternary_1,
   option_int = 5,
   option_real = 3.1,
   option_enum = 'two',
@@ -30,6 +31,7 @@ SELECT unnest(reloptions) FROM pg_class WHERE relname = 'dummy_test_idx';
 -- 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_real = 3.2);
 ALTER INDEX dummy_test_idx SET (option_string_val = 'val2');
 ALTER INDEX dummy_test_idx SET (option_string_null = NULL);
@@ -40,6 +42,7 @@ SELECT unnest(reloptions) FROM pg_class WHERE relname = 'dummy_test_idx';
 -- 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_real);
 ALTER INDEX dummy_test_idx RESET (option_enum);
 ALTER INDEX dummy_test_idx RESET (option_string_val);
@@ -60,6 +63,13 @@ ALTER INDEX dummy_test_idx SET (option_bool = 3.4); -- error
 ALTER INDEX dummy_test_idx SET (option_bool = 'val4'); -- error
 SELECT unnest(reloptions) FROM pg_class WHERE relname = 'dummy_test_idx';
 ALTER INDEX dummy_test_idx RESET (option_bool);
+-- Ternary
+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
+SELECT unnest(reloptions) FROM pg_class WHERE relname = 'dummy_test_idx';
+ALTER INDEX dummy_test_idx RESET (option_ternary_1);
 -- Float
 ALTER INDEX dummy_test_idx SET (option_real = 4); -- ok
 ALTER INDEX dummy_test_idx SET (option_real = true); -- error
index 9de19b4e3f13d3fb0a6db38411cb7f7fd04a2585..e3a974f26112e76ba44f993778c9e54471b73abe 100644 (file)
@@ -98,6 +98,24 @@ SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
  {fillfactor=13,autovacuum_enabled=false}
 (1 row)
 
+-- Tests for ternary options
+-- behave as boolean option: accept unassigned name and truncated value
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test(i INT) WITH (vacuum_truncate);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+       reloptions       
+------------------------
+ {vacuum_truncate=true}
+(1 row)
+
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test(i INT) WITH (vacuum_truncate=FaLS);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+       reloptions       
+------------------------
+ {vacuum_truncate=fals}
+(1 row)
+
 -- Test vacuum_truncate option
 DROP TABLE reloptions_test;
 CREATE TEMP TABLE reloptions_test(i INT NOT NULL, j text)
index 24fbe0b478d967ad746ed4333fbf189d807ec6f6..680c8bf8614857470b60f0f3aaec089e827dce07 100644 (file)
@@ -59,6 +59,17 @@ UPDATE pg_class
 ALTER TABLE reloptions_test RESET (illegal_option);
 SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
 
+-- Tests for ternary options
+
+-- behave as boolean option: accept unassigned name and truncated value
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test(i INT) WITH (vacuum_truncate);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+
+DROP TABLE reloptions_test;
+CREATE TABLE reloptions_test(i INT) WITH (vacuum_truncate=FaLS);
+SELECT reloptions FROM pg_class WHERE oid = 'reloptions_test'::regclass;
+
 -- Test vacuum_truncate option
 DROP TABLE reloptions_test;
 
index 3f3a888fd0ec58d30d75abade2d98b4d3e79d862..1c8610fd46c1c0a49e912f37ffedbe84a2161a85 100644 (file)
@@ -3952,6 +3952,7 @@ pg_sha512_ctx
 pg_snapshot
 pg_special_case
 pg_stack_base_t
+pg_ternary
 pg_time_t
 pg_time_usec_t
 pg_tz
@@ -4079,6 +4080,7 @@ relopt_kind
 relopt_parse_elt
 relopt_real
 relopt_string
+relopt_ternary
 relopt_type
 relopt_value
 relopts_validator