Change the reloptions machinery to use a table-based parser, and provide
authorAlvaro Herrera <alvherre@alvh.no-ip.org>
Mon, 5 Jan 2009 17:14:28 +0000 (17:14 +0000)
committerAlvaro Herrera <alvherre@alvh.no-ip.org>
Mon, 5 Jan 2009 17:14:28 +0000 (17:14 +0000)
a more complete framework for writing custom option processing routines
by user-defined access methods.

Catalog version bumped due to the general API changes, which are going to
affect user-defined "amoptions" routines.

src/backend/access/common/reloptions.c
src/backend/access/gin/ginutil.c
src/backend/access/gist/gistutil.c
src/backend/access/hash/hashutil.c
src/backend/access/nbtree/nbtutils.c
src/include/access/reloptions.h
src/include/catalog/catversion.h

index ec2abcb29845aec5d8e4138b08808ebe7eb9487d..3401667b9e41e626d7e6eafc6401e4dd6a5c7ac1 100644 (file)
@@ -8,13 +8,16 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/access/common/reloptions.c,v 1.12 2009/01/01 17:23:34 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/access/common/reloptions.c,v 1.13 2009/01/05 17:14:28 alvherre Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 #include "postgres.h"
 
+#include "access/gist_private.h"
+#include "access/hash.h"
+#include "access/nbtree.h"
 #include "access/reloptions.h"
 #include "catalog/pg_type.h"
 #include "commands/defrem.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
 #include "utils/guc.h"
+#include "utils/memutils.h"
 #include "utils/rel.h"
 
+/*
+ * Contents of pg_class.reloptions
+ *
+ * To add an option:
+ *
+ * (i) decide on a class (integer, real, bool, string), name, default value,
+ * upper and lower bounds (if applicable).
+ * (ii) add a record below.
+ * (iii) add it to StdRdOptions if appropriate
+ * (iv) add a block to the appropriate handling routine (probably
+ * default_reloptions)
+ * (v) don't forget to document the option
+ *
+ * Note that we don't handle "oids" in relOpts because it is handled by
+ * interpretOidsOption().
+ */
+
+static relopt_bool boolRelOpts[] =
+{
+       /* list terminator */
+       { { NULL } }
+};
+
+static relopt_int intRelOpts[] =
+{
+       {
+               {
+                       "fillfactor",
+                       "Packs table pages only to this percentage",
+                       RELOPT_KIND_HEAP
+               },
+               HEAP_DEFAULT_FILLFACTOR, HEAP_MIN_FILLFACTOR, 100
+       },
+       {
+               {
+                       "fillfactor",
+                       "Packs btree index pages only to this percentage",
+                       RELOPT_KIND_BTREE
+               },
+               BTREE_DEFAULT_FILLFACTOR, BTREE_MIN_FILLFACTOR, 100
+       },
+       {
+               {
+                       "fillfactor",
+                       "Packs hash index pages only to this percentage",
+                       RELOPT_KIND_HASH
+               },
+               HASH_DEFAULT_FILLFACTOR, HASH_MIN_FILLFACTOR, 100
+       },
+       {
+               {
+                       "fillfactor",
+                       "Packs gist index pages only to this percentage",
+                       RELOPT_KIND_GIST
+               },
+               GIST_DEFAULT_FILLFACTOR, GIST_MIN_FILLFACTOR, 100
+       },
+       /* list terminator */
+       { { NULL } }
+};
+
+static relopt_real realRelOpts[] =
+{
+       /* list terminator */
+       { { NULL } }
+};
+
+static relopt_string stringRelOpts[] = 
+{
+       /* list terminator */
+       { { NULL } }
+};
+
+static relopt_gen **relOpts = NULL;
+static int last_assigned_kind = RELOPT_KIND_LAST_DEFAULT + 1;
+
+static int             num_custom_options = 0;
+static relopt_gen **custom_options = NULL;
+static bool            need_initialization = true;
+
+static void initialize_reloptions(void);
+static void parse_one_reloption(relopt_value *option, char *text_str,
+                                       int text_len, bool validate);
+
+/*
+ * initialize_reloptions
+ *             initialization routine, must be called before parsing
+ *
+ * Initialize the relOpts array and fill each variable's type and name length.
+ */
+static void
+initialize_reloptions(void)
+{
+       int             i;
+       int             j = 0;
+
+       for (i = 0; boolRelOpts[i].gen.name; i++)
+               j++;
+       for (i = 0; intRelOpts[i].gen.name; i++)
+               j++;
+       for (i = 0; realRelOpts[i].gen.name; i++)
+               j++;
+       for (i = 0; stringRelOpts[i].gen.name; i++)
+               j++;
+       j += num_custom_options;
+
+       if (relOpts)
+               pfree(relOpts);
+       relOpts = MemoryContextAlloc(TopMemoryContext,
+                                                                (j + 1) * sizeof(relopt_gen *));
+
+       j = 0;
+       for (i = 0; boolRelOpts[i].gen.name; i++)
+       {
+               relOpts[j] = &boolRelOpts[i].gen;
+               relOpts[j]->type = RELOPT_TYPE_BOOL;
+               relOpts[j]->namelen = strlen(relOpts[j]->name);
+               j++;
+       }
+
+       for (i = 0; intRelOpts[i].gen.name; i++)
+       {
+               relOpts[j] = &intRelOpts[i].gen;
+               relOpts[j]->type = RELOPT_TYPE_INT;
+               relOpts[j]->namelen = strlen(relOpts[j]->name);
+               j++;
+       }
+
+       for (i = 0; realRelOpts[i].gen.name; i++)
+       {
+               relOpts[j] = &realRelOpts[i].gen;
+               relOpts[j]->type = RELOPT_TYPE_REAL;
+               relOpts[j]->namelen = strlen(relOpts[j]->name);
+               j++;
+       }
+
+       for (i = 0; stringRelOpts[i].gen.name; i++)
+       {
+               relOpts[j] = &stringRelOpts[i].gen;
+               relOpts[j]->type = RELOPT_TYPE_STRING;
+               relOpts[j]->namelen = strlen(relOpts[j]->name);
+               j++;
+       }
+
+       for (i = 0; i < num_custom_options; i++)
+       {
+               relOpts[j] = custom_options[i];
+               j++;
+       }
+
+       /* add a list terminator */
+       relOpts[j] = NULL;
+}
+
+/*
+ * add_reloption_kind
+ *             Create a new relopt_kind value, to be used in custom reloptions by
+ *             user-defined AMs.
+ */
+int
+add_reloption_kind(void)
+{
+       if (last_assigned_kind >= RELOPT_KIND_MAX)
+               ereport(ERROR,
+                               (errmsg("user-defined relation parameter types limit exceeded")));
+
+       return last_assigned_kind++;
+}
+
+/*
+ * add_reloption
+ *             Add an already-created custom reloption to the list, and recompute the
+ *             main parser table.
+ */
+static void
+add_reloption(relopt_gen *newoption)
+{
+       static int              max_custom_options = 0;
+
+       if (num_custom_options >= max_custom_options)
+       {
+               MemoryContext   oldcxt;
+
+               oldcxt = MemoryContextSwitchTo(TopMemoryContext);
+
+               if (max_custom_options == 0)
+               {
+                       max_custom_options = 8;
+                       custom_options = palloc(max_custom_options * sizeof(relopt_gen *));
+               }
+               else
+               {
+                       max_custom_options *= 2;
+                       custom_options = repalloc(custom_options,
+                                                                         max_custom_options * sizeof(relopt_gen *));
+               }
+               MemoryContextSwitchTo(oldcxt);
+       }
+       custom_options[num_custom_options++] = newoption;
+
+       need_initialization = true;
+}
+
+/*
+ * allocate_reloption
+ *             Allocate a new reloption and initialize the type-agnostic fields
+ *             (for types other than string)
+ */
+static relopt_gen *
+allocate_reloption(int kind, int type, char *name, char *desc)
+{
+       MemoryContext   oldcxt;
+       size_t                  size;
+       relopt_gen         *newoption;
+
+       Assert(type != RELOPT_TYPE_STRING);
+
+       oldcxt = MemoryContextSwitchTo(TopMemoryContext);
+
+       switch (type)
+       {
+               case RELOPT_TYPE_BOOL:
+                       size = sizeof(relopt_bool);
+                       break;
+               case RELOPT_TYPE_INT:
+                       size = sizeof(relopt_int);
+                       break;
+               case RELOPT_TYPE_REAL:
+                       size = sizeof(relopt_real);
+                       break;
+               default:
+                       elog(ERROR, "unsupported option type");
+                       return NULL;    /* keep compiler quiet */
+       }
+
+       newoption = palloc(size);
+
+       newoption->name = pstrdup(name);
+       if (desc)
+               newoption->desc = pstrdup(desc);
+       else
+               newoption->desc = NULL;
+       newoption->kind = kind;
+       newoption->namelen = strlen(name);
+       newoption->type = type;
+
+       MemoryContextSwitchTo(oldcxt);
+
+       return newoption;
+}
+
+/*
+ * add_bool_reloption
+ *             Add a new boolean reloption
+ */
+void
+add_bool_reloption(int kind, char *name, char *desc, bool default_val)
+{
+       relopt_bool        *newoption;
+
+       newoption = (relopt_bool *) allocate_reloption(kind, RELOPT_TYPE_BOOL,
+                                                                                                  name, desc);
+       newoption->default_val = default_val;
+
+       add_reloption((relopt_gen *) newoption);
+}
+
+/*
+ * add_int_reloption
+ *             Add a new integer reloption
+ */
+void
+add_int_reloption(int kind, char *name, char *desc, int default_val,
+                                 int min_val, int max_val)
+{
+       relopt_int         *newoption;
+
+       newoption = (relopt_int *) allocate_reloption(kind, RELOPT_TYPE_INT,
+                                                                                                 name, desc);
+       newoption->default_val = default_val;
+       newoption->min = min_val;
+       newoption->max = max_val;
+
+       add_reloption((relopt_gen *) newoption);
+}
+
+/*
+ * add_real_reloption
+ *             Add a new float reloption
+ */
+void
+add_real_reloption(int kind, char *name, char *desc, double default_val,
+                                 double min_val, double max_val)
+{
+       relopt_real        *newoption;
+
+       newoption = (relopt_real *) allocate_reloption(kind, RELOPT_TYPE_REAL,
+                                                                                                  name, desc);
+       newoption->default_val = default_val;
+       newoption->min = min_val;
+       newoption->max = max_val;
+
+       add_reloption((relopt_gen *) newoption);
+}
+
+/*
+ * add_string_reloption
+ *             Add a new string reloption
+ */
+void
+add_string_reloption(int kind, char *name, char *desc, char *default_val)
+{
+       MemoryContext   oldcxt;
+       relopt_string  *newoption;
+       int                             default_len = 0;
+
+       oldcxt = MemoryContextSwitchTo(TopMemoryContext);
+
+       if (default_val)
+               default_len = strlen(default_val);
+
+       newoption = palloc0(sizeof(relopt_string) + default_len);
+
+       newoption->gen.name = pstrdup(name);
+       if (desc)
+               newoption->gen.desc = pstrdup(desc);
+       else
+               newoption->gen.desc = NULL;
+       newoption->gen.kind = kind;
+       newoption->gen.namelen = strlen(name);
+       newoption->gen.type = RELOPT_TYPE_STRING;
+       if (default_val)
+       {
+               strcpy(newoption->default_val, default_val);
+               newoption->default_len = default_len;
+               newoption->default_isnull = false;
+       }
+       else
+       {
+               newoption->default_val[0] = '\0';
+               newoption->default_len = 0;
+               newoption->default_isnull = true;
+       }
+
+       MemoryContextSwitchTo(oldcxt);
+
+       add_reloption((relopt_gen *) newoption);
+}
 
 /*
  * Transform a relation options list (list of DefElem) into the text array
@@ -198,147 +550,244 @@ untransformRelOptions(Datum options)
 /*
  * Interpret reloptions that are given in text-array format.
  *
- *     options: array of "keyword=value" strings, as built by transformRelOptions
- *     numkeywords: number of legal keywords
- *     keywords: the allowed keywords
- *     values: output area
- *     validate: if true, throw error for unrecognized keywords.
+ * options is a reloption text array as constructed by transformRelOptions.
+ * kind specifies the family of options to be processed.
+ *
+ * The return value is a relopt_value * array on which the options actually
+ * set in the options array are marked with isset=true.  The length of this
+ * array is returned in *numrelopts.  Options not set are also present in the
+ * array; this is so that the caller can easily locate the default values.
  *
- * The keywords and values arrays must both be of length numkeywords.
- * The values entry corresponding to a keyword is set to a palloc'd string
- * containing the corresponding value, or NULL if the keyword does not appear.
+ * If there are no options of the given kind, numrelopts is set to 0 and NULL
+ * is returned.
+ *
+ * Note: values of type int, bool and real are allocated as part of the
+ * returned array.  Values of type string are allocated separately and must
+ * be freed by the caller.
  */
-void
-parseRelOptions(Datum options, int numkeywords, const char *const * keywords,
-                               char **values, bool validate)
+relopt_value *
+parseRelOptions(Datum options, bool validate, relopt_kind kind,
+                               int *numrelopts)
 {
-       ArrayType  *array;
-       Datum      *optiondatums;
-       int                     noptions;
+       relopt_value *reloptions;
+       int                     numoptions = 0;
        int                     i;
+       int                     j;
 
-       /* Initialize to "all defaulted" */
-       MemSet(values, 0, numkeywords * sizeof(char *));
+       if (need_initialization)
+               initialize_reloptions();
 
-       /* Done if no options */
-       if (!PointerIsValid(DatumGetPointer(options)))
-               return;
+       /* Build a list of expected options, based on kind */
 
-       array = DatumGetArrayTypeP(options);
+       for (i = 0; relOpts[i]; i++)
+               if (relOpts[i]->kind == kind)
+                       numoptions++;
 
-       Assert(ARR_ELEMTYPE(array) == TEXTOID);
+       if (numoptions == 0)
+       {
+               *numrelopts = 0;
+               return NULL;
+       }
 
-       deconstruct_array(array, TEXTOID, -1, false, 'i',
-                                         &optiondatums, NULL, &noptions);
+       reloptions = palloc(numoptions * sizeof(relopt_value));
 
-       for (i = 0; i < noptions; i++)
+       for (i = 0, j = 0; relOpts[i]; i++)
+       {
+               if (relOpts[i]->kind == kind)
+               {
+                       reloptions[j].gen = relOpts[i];
+                       reloptions[j].isset = false;
+                       j++;
+               }
+       }
+
+       /* Done if no options */
+       if (PointerIsValid(DatumGetPointer(options)))
        {
-               text       *optiontext = DatumGetTextP(optiondatums[i]);
-               char       *text_str = VARDATA(optiontext);
-               int                     text_len = VARSIZE(optiontext) - VARHDRSZ;
-               int                     j;
+               ArrayType  *array;
+               Datum      *optiondatums;
+               int                     noptions;
+
+               array = DatumGetArrayTypeP(options);
+
+               Assert(ARR_ELEMTYPE(array) == TEXTOID);
+
+               deconstruct_array(array, TEXTOID, -1, false, 'i',
+                                                 &optiondatums, NULL, &noptions);
 
-               /* Search for a match in keywords */
-               for (j = 0; j < numkeywords; j++)
+               for (i = 0; i < noptions; i++)
                {
-                       int                     kw_len = strlen(keywords[j]);
+                       text       *optiontext = DatumGetTextP(optiondatums[i]);
+                       char       *text_str = VARDATA(optiontext);
+                       int                     text_len = VARSIZE(optiontext) - VARHDRSZ;
+                       int                     j;
 
-                       if (text_len > kw_len && text_str[kw_len] == '=' &&
-                               pg_strncasecmp(text_str, keywords[j], kw_len) == 0)
+                       /* Search for a match in reloptions */
+                       for (j = 0; j < numoptions; j++)
                        {
-                               char       *value;
-                               int                     value_len;
+                               int                     kw_len = reloptions[j].gen->namelen;
 
-                               if (values[j] && validate)
-                                       ereport(ERROR,
-                                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                                 errmsg("parameter \"%s\" specified more than once",
-                                                                keywords[j])));
-                               value_len = text_len - kw_len - 1;
-                               value = (char *) palloc(value_len + 1);
-                               memcpy(value, text_str + kw_len + 1, value_len);
-                               value[value_len] = '\0';
-                               values[j] = value;
-                               break;
+                               if (text_len > kw_len && text_str[kw_len] == '=' &&
+                                       pg_strncasecmp(text_str, reloptions[j].gen->name,
+                                                                  kw_len) == 0)
+                               {
+                                       parse_one_reloption(&reloptions[j], text_str, text_len,
+                                                                               validate);
+                                       break;
+                               }
+                       }
+
+                       if (j >= numoptions && validate)
+                       {
+                               char       *s;
+                               char       *p;
+
+                               s = TextDatumGetCString(optiondatums[i]);
+                               p = strchr(s, '=');
+                               if (p)
+                                       *p = '\0';
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                                errmsg("unrecognized parameter \"%s\"", s)));
                        }
-               }
-               if (j >= numkeywords && validate)
-               {
-                       char       *s;
-                       char       *p;
-
-                       s = TextDatumGetCString(optiondatums[i]);
-                       p = strchr(s, '=');
-                       if (p)
-                               *p = '\0';
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                        errmsg("unrecognized parameter \"%s\"", s)));
                }
        }
+
+       *numrelopts = numoptions;
+       return reloptions;
 }
 
+/*
+ * Subroutine for parseRelOptions, to parse and validate a single option's
+ * value
+ */
+static void
+parse_one_reloption(relopt_value *option, char *text_str, int text_len,
+                                       bool validate)
+{
+       char       *value;
+       int                     value_len;
+       bool            parsed;
+       bool            nofree = false;
+
+       if (option->isset && validate)
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                errmsg("parameter \"%s\" specified more than once",
+                                               option->gen->name)));
+
+       value_len = text_len - option->gen->namelen - 1;
+       value = (char *) palloc(value_len + 1);
+       memcpy(value, text_str + option->gen->namelen + 1, value_len);
+       value[value_len] = '\0';
+
+       switch (option->gen->type)
+       {
+               case RELOPT_TYPE_BOOL:
+                       {
+                               parsed = parse_bool(value, &option->values.bool_val);
+                               if (validate && !parsed)
+                                       ereport(ERROR,
+                                                       (errmsg("invalid value for boolean option \"%s\": %s",
+                                                                       option->gen->name, value)));
+                       }
+                       break;
+               case RELOPT_TYPE_INT:
+                       {
+                               relopt_int      *optint = (relopt_int *) option->gen;
+
+                               parsed = parse_int(value, &option->values.int_val, 0, NULL);
+                               if (validate && !parsed)
+                                       ereport(ERROR,
+                                                       (errmsg("invalid value for integer option \"%s\": %s",
+                                                                       option->gen->name, value)));
+                               if (validate && (option->values.int_val < optint->min ||
+                                                                option->values.int_val > optint->max))
+                                       ereport(ERROR,
+                                                       (errmsg("value %s out of bounds for option \"%s\"",
+                                                                       value, option->gen->name),
+                                                        errdetail("Valid values are between \"%d\" and \"%d\".",
+                                                                          optint->min, optint->max)));
+                       }
+                       break;
+               case RELOPT_TYPE_REAL:
+                       {
+                               relopt_real     *optreal = (relopt_real *) option->gen;
+
+                               parsed = parse_real(value, &option->values.real_val);
+                               if (validate && !parsed)
+                                       ereport(ERROR,
+                                                       (errmsg("invalid value for floating point option \"%s\": %s",
+                                                                       option->gen->name, value)));
+                               if (validate && (option->values.real_val < optreal->min ||
+                                                                option->values.real_val > optreal->max))
+                                       ereport(ERROR,
+                                                       (errmsg("value %s out of bounds for option \"%s\"",
+                                                                       value, option->gen->name),
+                                                        errdetail("Valid values are between \"%f\" and \"%f\".",
+                                                                          optreal->min, optreal->max)));
+                       }
+                       break;
+               case RELOPT_TYPE_STRING:
+                       option->values.string_val = value;
+                       nofree = true;
+                       parsed = true;
+                       /* no validation possible */
+                       break;
+               default:
+                       elog(ERROR, "unsupported reloption type %d", option->gen->type);
+                       break;
+       }
+
+       if (parsed)
+               option->isset = true;
+       if (!nofree)
+               pfree(value);
+}
 
 /*
- * Parse reloptions for anything using StdRdOptions (ie, fillfactor only)
+ * Option parser for anything that uses StdRdOptions (i.e. fillfactor only)
  */
 bytea *
-default_reloptions(Datum reloptions, bool validate,
-                                  int minFillfactor, int defaultFillfactor)
+default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
 {
-       static const char *const default_keywords[1] = {"fillfactor"};
-       char       *values[1];
-       int                     fillfactor;
-       StdRdOptions *result;
+       relopt_value   *options;
+       StdRdOptions   *rdopts;
+       StdRdOptions    lopts;
+       int                             numoptions;
+       int                             len;
+       int                             i;
 
-       parseRelOptions(reloptions, 1, default_keywords, values, validate);
+       options = parseRelOptions(reloptions, validate, kind, &numoptions);
 
-       /*
-        * If no options, we can just return NULL rather than doing anything.
-        * (defaultFillfactor is thus not used, but we require callers to pass it
-        * anyway since we would need it if more options were added.)
-        */
-       if (values[0] == NULL)
+       /* if none set, we're done */
+       if (numoptions == 0)
                return NULL;
 
-       if (!parse_int(values[0], &fillfactor, 0, NULL))
-       {
-               if (validate)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                        errmsg("fillfactor must be an integer: \"%s\"",
-                                                       values[0])));
-               return NULL;
-       }
+       MemSet(&lopts, 0, sizeof(StdRdOptions));
 
-       if (fillfactor < minFillfactor || fillfactor > 100)
+       for (i = 0; i < numoptions; i++)
        {
-               if (validate)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                        errmsg("fillfactor=%d is out of range (should be between %d and 100)",
-                                                       fillfactor, minFillfactor)));
-               return NULL;
+               HANDLE_INT_RELOPTION("fillfactor", lopts.fillfactor, options[i]);
        }
 
-       result = (StdRdOptions *) palloc(sizeof(StdRdOptions));
-       SET_VARSIZE(result, sizeof(StdRdOptions));
+       pfree(options);
 
-       result->fillfactor = fillfactor;
+       len = sizeof(StdRdOptions);
+       rdopts = palloc(len);
+       memcpy(rdopts, &lopts, len);
+       SET_VARSIZE(rdopts, len);
 
-       return (bytea *) result;
+       return (bytea *) rdopts;
 }
 
-
 /*
  * Parse options for heaps (and perhaps someday toast tables).
  */
 bytea *
 heap_reloptions(char relkind, Datum reloptions, bool validate)
 {
-       return default_reloptions(reloptions, validate,
-                                                         HEAP_MIN_FILLFACTOR,
-                                                         HEAP_DEFAULT_FILLFACTOR);
+       return default_reloptions(reloptions, validate, RELOPT_KIND_HEAP);
 }
 
 
index 1886aa266914f3db707ea790a2c6d6003ec23325..222ea677883f7f730365ff37511eb4d4f47a6619 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *                     $PostgreSQL: pgsql/src/backend/access/gin/ginutil.c,v 1.19 2009/01/01 17:23:34 momjian Exp $
+ *                     $PostgreSQL: pgsql/src/backend/access/gin/ginutil.c,v 1.20 2009/01/05 17:14:28 alvherre Exp $
  *-------------------------------------------------------------------------
  */
 
@@ -317,16 +317,7 @@ ginoptions(PG_FUNCTION_ARGS)
        bool            validate = PG_GETARG_BOOL(1);
        bytea      *result;
 
-       /*
-        * It's not clear that fillfactor is useful for GIN, but for the moment
-        * we'll accept it anyway.  (It won't do anything...)
-        */
-#define GIN_MIN_FILLFACTOR                     10
-#define GIN_DEFAULT_FILLFACTOR         100
-
-       result = default_reloptions(reloptions, validate,
-                                                               GIN_MIN_FILLFACTOR,
-                                                               GIN_DEFAULT_FILLFACTOR);
+       result = default_reloptions(reloptions, validate, RELOPT_KIND_GIN);
        if (result)
                PG_RETURN_BYTEA_P(result);
        PG_RETURN_NULL();
index 0d66492601f41c28531566f3628e9913bed69ff1..fa1e3088ad4fde2726ae3cd7abbe74ddb34ef34e 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *                     $PostgreSQL: pgsql/src/backend/access/gist/gistutil.c,v 1.32 2009/01/01 17:23:35 momjian Exp $
+ *                     $PostgreSQL: pgsql/src/backend/access/gist/gistutil.c,v 1.33 2009/01/05 17:14:28 alvherre Exp $
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
@@ -670,9 +670,8 @@ gistoptions(PG_FUNCTION_ARGS)
        bool            validate = PG_GETARG_BOOL(1);
        bytea      *result;
 
-       result = default_reloptions(reloptions, validate,
-                                                               GIST_MIN_FILLFACTOR,
-                                                               GIST_DEFAULT_FILLFACTOR);
+       result = default_reloptions(reloptions, validate, RELOPT_KIND_GIST);
+
        if (result)
                PG_RETURN_BYTEA_P(result);
        PG_RETURN_NULL();
index 4261f0c75e1299436390f59d1ad2c6a986e84e50..42e79376f8fc16cf60e6fcb9d3330582fde93a3b 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/access/hash/hashutil.c,v 1.58 2009/01/01 17:23:35 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/access/hash/hashutil.c,v 1.59 2009/01/05 17:14:28 alvherre Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -224,9 +224,8 @@ hashoptions(PG_FUNCTION_ARGS)
        bool            validate = PG_GETARG_BOOL(1);
        bytea      *result;
 
-       result = default_reloptions(reloptions, validate,
-                                                               HASH_MIN_FILLFACTOR,
-                                                               HASH_DEFAULT_FILLFACTOR);
+       result = default_reloptions(reloptions, validate, RELOPT_KIND_HASH);
+
        if (result)
                PG_RETURN_BYTEA_P(result);
        PG_RETURN_NULL();
index 3963ce847cf4a48e5921b6d3bc98ea66293214b2..1649307251cd8b0fdcb95f1af005c119509c45f1 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/access/nbtree/nbtutils.c,v 1.92 2009/01/01 17:23:36 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/access/nbtree/nbtutils.c,v 1.93 2009/01/05 17:14:28 alvherre Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1402,9 +1402,7 @@ btoptions(PG_FUNCTION_ARGS)
        bool            validate = PG_GETARG_BOOL(1);
        bytea      *result;
 
-       result = default_reloptions(reloptions, validate,
-                                                               BTREE_MIN_FILLFACTOR,
-                                                               BTREE_DEFAULT_FILLFACTOR);
+       result = default_reloptions(reloptions, validate, RELOPT_KIND_BTREE);
        if (result)
                PG_RETURN_BYTEA_P(result);
        PG_RETURN_NULL();
index ee1f9f2d574e3718b41fc409eda3fe3a953adaba..8a2e2286becc0fef59988e415939a18be1addf62 100644 (file)
@@ -11,7 +11,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/access/reloptions.h,v 1.6 2009/01/01 17:23:56 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/access/reloptions.h,v 1.7 2009/01/05 17:14:28 alvherre Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 #include "nodes/pg_list.h"
 
+/* types supported by reloptions */
+typedef enum relopt_type
+{
+       RELOPT_TYPE_BOOL,
+       RELOPT_TYPE_INT,
+       RELOPT_TYPE_REAL,
+       RELOPT_TYPE_STRING
+} relopt_type;
+
+/* kinds supported by reloptions */
+typedef enum relopt_kind
+{
+       RELOPT_KIND_HEAP,
+       /* XXX do we need a separate kind for TOAST tables? */
+       RELOPT_KIND_BTREE,
+       RELOPT_KIND_HASH,
+       RELOPT_KIND_GIN,
+       RELOPT_KIND_GIST,
+       /* if you add a new kind, make sure you update "last_default" too */
+       RELOPT_KIND_LAST_DEFAULT = RELOPT_KIND_GIST,
+       RELOPT_KIND_MAX = 255
+} relopt_kind;
+
+/* generic struct to hold shared data */
+typedef struct relopt_gen
+{
+       const char *name;       /* must be first (used as list termination marker) */
+       const char *desc;
+       relopt_kind     kind;
+       int                     namelen;
+       relopt_type     type;
+} relopt_gen;
+
+/* holds a parsed value */
+typedef struct relopt_value
+{
+       relopt_gen *gen;
+       bool            isset;
+       union
+       {
+               bool    bool_val;
+               int             int_val;
+               double  real_val;
+               char   *string_val;     /* allocated separately */
+       } values;
+} relopt_value;
+
+/* reloptions records for specific variable types */
+typedef struct relopt_bool
+{
+       relopt_gen      gen;
+       bool            default_val;
+} relopt_bool;
+       
+typedef struct relopt_int
+{
+       relopt_gen      gen;
+       int                     default_val;
+       int                     min;
+       int                     max;
+} relopt_int;
+
+typedef struct relopt_real
+{
+       relopt_gen      gen;
+       double          default_val;
+       double          min;
+       double          max;
+} relopt_real;
+
+typedef struct relopt_string
+{
+       relopt_gen      gen;
+       int                     default_len;
+       bool            default_isnull;
+       char            default_val[1]; /* variable length */
+} relopt_string;
+
+/*
+ * These macros exist for the convenience of amoptions writers.  See
+ * default_reloptions for an example of the intended usage.  Beware of
+ * multiple evaluation of arguments!
+ *
+ * Most of the time there's no need to call HAVE_RELOPTION manually, but it's
+ * possible that an amoptions routine needs to walk the array with a different
+ * purpose (say, to compute the size of a struct to allocate beforehand.)
+ */
+#define HAVE_RELOPTION(optname, option) \
+       (pg_strncasecmp(option.gen->name, optname, option.gen->namelen) == 0)
+
+#define HANDLE_INT_RELOPTION(optname, var, option)                                     \
+       do {                                                                                                                    \
+               if (HAVE_RELOPTION(optname, option))                                            \
+               {                                                                                                                       \
+                       if (option.isset)                                                                               \
+                               var = option.values.int_val;                                            \
+                       else                                                                                                    \
+                               var = ((relopt_int *) option.gen)->default_val;         \
+                       continue;                                                                                               \
+               }                                                                                                                       \
+       } while (0)
+
+#define HANDLE_BOOL_RELOPTION(optname, var, option)                            \
+       do {                                                                                                                    \
+               if (HAVE_RELOPTION(optname, option))                                            \
+               {                                                                                                                       \
+                       if (option.isset)                                                                               \
+                               var = option.values.bool_val;                                           \
+                       else                                                                                                    \
+                               var = ((relopt_bool *) option.gen)->default_val;        \
+                       continue;                                                                                               \
+               }                                                                                                                       \
+       } while (0)
+
+#define HANDLE_REAL_RELOPTION(optname, var, option)                            \
+       do {                                                                                                                    \
+               if (HAVE_RELOPTION(optname, option))                                            \
+               {                                                                                                                       \
+                       if (option.isset)                                                                               \
+                               var = option.values.real_val;                                           \
+                       else                                                                                                    \
+                               var = ((relopt_real *) option.gen)->default_val;        \
+                       continue;                                                                                               \
+               }                                                                                                                       \
+       } while (0)
+
+/* Note that this assumes that the variable is already allocated! */
+#define HANDLE_STRING_RELOPTION(optname, var, option)                          \
+       do {                                                                                                                    \
+               if (HAVE_RELOPTION(optname, option))                                            \
+               {                                                                                                                       \
+                       relopt_string *optstring = (relopt_string *) option.gen;\
+                       if (optstring->default_isnull)                                                  \
+                               var[0] = '\0';                                                                          \
+                       else                                                                                                    \
+                               strcpy(var,                                                                                     \
+                                          option.isset ? option.values.string_val :    \
+                                          optstring->default_val);                                             \
+                       continue;                                                                                               \
+               }                                                                                                                       \
+       } while (0)
+
+extern int add_reloption_kind(void);
+extern void add_bool_reloption(int kind, char *name, char *desc,
+                                  bool default_val);
+extern void add_int_reloption(int kind, char *name, char *desc,
+                                 int default_val, int min_val, int max_val);
+extern void add_real_reloption(int kind, char *name, char *desc,
+                                  double default_val, double min_val, double max_val);
+extern void add_string_reloption(int kind, char *name, char *desc,
+                                        char *default_val);
+                       
 extern Datum transformRelOptions(Datum oldOptions, List *defList,
                                        bool ignoreOids, bool isReset);
-
 extern List *untransformRelOptions(Datum options);
-
-extern void parseRelOptions(Datum options, int numkeywords,
-                               const char *const * keywords,
-                               char **values, bool validate);
+extern relopt_value *parseRelOptions(Datum options, bool validate,
+                               relopt_kind kind, int *numrelopts);
 
 extern bytea *default_reloptions(Datum reloptions, bool validate,
-                                  int minFillfactor, int defaultFillfactor);
-
+                                  relopt_kind kind);
 extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
-
 extern bytea *index_reloptions(RegProcedure amoptions, Datum reloptions,
-                                bool validate);
+                               bool validate);
 
 #endif   /* RELOPTIONS_H */
index 9a11d875a2ff9432d0edd05ec93f2d7ea9134c2c..13e47f0399cf3e3bab73a567f6cca7cd497a5952 100644 (file)
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.516 2009/01/01 17:23:56 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.517 2009/01/05 17:14:28 alvherre Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     200812301
+#define CATALOG_VERSION_NO     200901051
 
 #endif