Alvaro Herrera wrote:
> Okay, it was basically fine except for the attached minor correction.
> Warning: I intend to commit this patch fairly soon!
This is the patch in its final form. I have included a few macros to
simplify the writing of amoptions routines.
--
Alvaro Herrera http://www.CommandPrompt.com/
PostgreSQL Replication, Consulting, Custom Development, 24x7 support
Index: src/backend/access/common/reloptions.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/access/common/reloptions.c,v
retrieving revision 1.12
diff -c -p -r1.12 reloptions.c
*** src/backend/access/common/reloptions.c 1 Jan 2009 17:23:34 -0000 1.12
--- src/backend/access/common/reloptions.c 4 Jan 2009 21:59:41 -0000
***************
*** 15,20 ****
--- 15,23 ----
#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"
***************
*** 22,29 ****
--- 25,362 ----
#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 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.
+ */
+ 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)
+ {
+ 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;
+
+ oldcxt = MemoryContextSwitchTo(TopMemoryContext);
+
+ newoption = palloc0(sizeof(relopt_string) + strlen(default_val));
+
+ 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;
+ strcpy(newoption->default_val, default_val);
+ newoption->default_len = strlen(default_val);
+
+ MemoryContextSwitchTo(oldcxt);
+
+ add_reloption((relopt_gen *) newoption);
+ }
/*
* Transform a relation options list (list of DefElem) into the text array
*************** untransformRelOptions(Datum options)
*** 198,344 ****
/*
* 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.
! *
! * 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.
*/
! void
! parseRelOptions(Datum options, int numkeywords, const char *const * keywords,
! char **values, bool validate)
{
! ArrayType *array;
! Datum *optiondatums;
! int noptions;
int i;
! /* Initialize to "all defaulted" */
! MemSet(values, 0, numkeywords * sizeof(char *));
! /* Done if no options */
! if (!PointerIsValid(DatumGetPointer(options)))
! return;
! array = DatumGetArrayTypeP(options);
! Assert(ARR_ELEMTYPE(array) == TEXTOID);
! deconstruct_array(array, TEXTOID, -1, false, 'i',
! &optiondatums, NULL, &noptions);
! for (i = 0; i < noptions; i++)
{
! text *optiontext = DatumGetTextP(optiondatums[i]);
! char *text_str = VARDATA(optiontext);
! int text_len = VARSIZE(optiontext) - VARHDRSZ;
! int j;
! /* Search for a match in keywords */
! for (j = 0; j < numkeywords; j++)
{
! int kw_len = strlen(keywords[j]);
! if (text_len > kw_len && text_str[kw_len] == '=' &&
! pg_strncasecmp(text_str, keywords[j], kw_len) == 0)
{
! char *value;
! int value_len;
! 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 (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)));
}
}
}
/*
! * Parse reloptions for anything using StdRdOptions (ie, fillfactor only)
*/
bytea *
! default_reloptions(Datum reloptions, bool validate,
! int minFillfactor, int defaultFillfactor)
{
! static const char *const default_keywords[1] = {"fillfactor"};
! char *values[1];
! int fillfactor;
! StdRdOptions *result;
! parseRelOptions(reloptions, 1, default_keywords, values, validate);
! /*
! * 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)
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;
! }
! if (fillfactor < minFillfactor || fillfactor > 100)
{
! 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;
}
! result = (StdRdOptions *) palloc(sizeof(StdRdOptions));
! SET_VARSIZE(result, sizeof(StdRdOptions));
! result->fillfactor = fillfactor;
! return (bytea *) result;
}
-
/*
* 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);
}
--- 531,774 ----
/*
* Interpret reloptions that are given in text-array format.
*
! * options is a reloption text array as constructed by transformRelOptions.
! * kind specifies the family of options to be processed.
! *
! * The return value is a relopt_value * array on which the options actually
! * set in the options array are marked with isset=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.
! *
! * 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.
*/
! relopt_value *
! parseRelOptions(Datum options, bool validate, relopt_kind kind,
! int *numrelopts)
{
! relopt_value *reloptions;
! int numoptions = 0;
int i;
+ int j;
! if (need_initialization)
! initialize_reloptions();
! /* Build a list of expected options, based on kind */
! for (i = 0; relOpts[i]; i++)
! if (relOpts[i]->kind == kind)
! numoptions++;
! if (numoptions == 0)
! {
! *numrelopts = 0;
! return NULL;
! }
! reloptions = palloc(numoptions * sizeof(relopt_value));
! 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)))
{
! ArrayType *array;
! Datum *optiondatums;
! int noptions;
!
! array = DatumGetArrayTypeP(options);
!
! Assert(ARR_ELEMTYPE(array) == TEXTOID);
! deconstruct_array(array, TEXTOID, -1, false, 'i',
! &optiondatums, NULL, &noptions);
!
! for (i = 0; i < noptions; i++)
{
! text *optiontext = DatumGetTextP(optiondatums[i]);
! char *text_str = VARDATA(optiontext);
! int text_len = VARSIZE(optiontext) - VARHDRSZ;
! int j;
! /* Search for a match in reloptions */
! for (j = 0; j < numoptions; j++)
{
! int kw_len = reloptions[j].gen->namelen;
! 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)));
! }
}
}
+
+ *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);
+ }
/*
! * Option parser for anything that uses StdRdOptions (i.e. fillfactor only)
*/
bytea *
! default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
{
! relopt_value *options;
! StdRdOptions *rdopts;
! StdRdOptions lopts;
! int numoptions;
! int len;
! int i;
! options = parseRelOptions(reloptions, validate, kind, &numoptions);
! /* if none set, we're done */
! if (numoptions == 0)
return NULL;
! MemSet(&lopts, 0, sizeof(StdRdOptions));
! for (i = 0; i < numoptions; i++)
{
! HANDLE_INT_RELOPTION("fillfactor", lopts.fillfactor, options[i]);
}
! pfree(options);
! len = sizeof(StdRdOptions);
! rdopts = palloc(len);
! memcpy(rdopts, &lopts, len);
! SET_VARSIZE(rdopts, len);
! 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, RELOPT_KIND_HEAP);
}
Index: src/backend/access/gin/ginutil.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/access/gin/ginutil.c,v
retrieving revision 1.19
diff -c -p -r1.19 ginutil.c
*** src/backend/access/gin/ginutil.c 1 Jan 2009 17:23:34 -0000 1.19
--- src/backend/access/gin/ginutil.c 3 Jan 2009 16:44:06 -0000
*************** ginoptions(PG_FUNCTION_ARGS)
*** 317,332 ****
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);
if (result)
PG_RETURN_BYTEA_P(result);
PG_RETURN_NULL();
--- 317,323 ----
bool validate = PG_GETARG_BOOL(1);
bytea *result;
! result = default_reloptions(reloptions, validate, RELOPT_KIND_GIN);
if (result)
PG_RETURN_BYTEA_P(result);
PG_RETURN_NULL();
Index: src/backend/access/gist/gistutil.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/access/gist/gistutil.c,v
retrieving revision 1.32
diff -c -p -r1.32 gistutil.c
*** src/backend/access/gist/gistutil.c 1 Jan 2009 17:23:35 -0000 1.32
--- src/backend/access/gist/gistutil.c 3 Jan 2009 16:44:06 -0000
*************** gistoptions(PG_FUNCTION_ARGS)
*** 670,678 ****
bool validate = PG_GETARG_BOOL(1);
bytea *result;
! result = default_reloptions(reloptions, validate,
! GIST_MIN_FILLFACTOR,
! GIST_DEFAULT_FILLFACTOR);
if (result)
PG_RETURN_BYTEA_P(result);
PG_RETURN_NULL();
--- 670,677 ----
bool validate = PG_GETARG_BOOL(1);
bytea *result;
! result = default_reloptions(reloptions, validate, RELOPT_KIND_GIST);
!
if (result)
PG_RETURN_BYTEA_P(result);
PG_RETURN_NULL();
Index: src/backend/access/hash/hashutil.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/access/hash/hashutil.c,v
retrieving revision 1.58
diff -c -p -r1.58 hashutil.c
*** src/backend/access/hash/hashutil.c 1 Jan 2009 17:23:35 -0000 1.58
--- src/backend/access/hash/hashutil.c 3 Jan 2009 16:44:06 -0000
*************** hashoptions(PG_FUNCTION_ARGS)
*** 224,232 ****
bool validate = PG_GETARG_BOOL(1);
bytea *result;
! result = default_reloptions(reloptions, validate,
! HASH_MIN_FILLFACTOR,
! HASH_DEFAULT_FILLFACTOR);
if (result)
PG_RETURN_BYTEA_P(result);
PG_RETURN_NULL();
--- 224,231 ----
bool validate = PG_GETARG_BOOL(1);
bytea *result;
! result = default_reloptions(reloptions, validate, RELOPT_KIND_HASH);
!
if (result)
PG_RETURN_BYTEA_P(result);
PG_RETURN_NULL();
Index: src/backend/access/nbtree/nbtutils.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/backend/access/nbtree/nbtutils.c,v
retrieving revision 1.92
diff -c -p -r1.92 nbtutils.c
*** src/backend/access/nbtree/nbtutils.c 1 Jan 2009 17:23:36 -0000 1.92
--- src/backend/access/nbtree/nbtutils.c 4 Jan 2009 21:42:50 -0000
*************** btoptions(PG_FUNCTION_ARGS)
*** 1402,1410 ****
bool validate = PG_GETARG_BOOL(1);
bytea *result;
! result = default_reloptions(reloptions, validate,
! BTREE_MIN_FILLFACTOR,
! BTREE_DEFAULT_FILLFACTOR);
if (result)
PG_RETURN_BYTEA_P(result);
PG_RETURN_NULL();
--- 1402,1408 ----
bool validate = PG_GETARG_BOOL(1);
bytea *result;
! result = default_reloptions(reloptions, validate, RELOPT_KIND_BTREE);
if (result)
PG_RETURN_BYTEA_P(result);
PG_RETURN_NULL();
Index: src/include/access/reloptions.h
===================================================================
RCS file: /home/alvherre/cvs/pgsql/src/include/access/reloptions.h,v
retrieving revision 1.6
diff -c -p -r1.6 reloptions.h
*** src/include/access/reloptions.h 1 Jan 2009 17:23:56 -0000 1.6
--- src/include/access/reloptions.h 4 Jan 2009 21:52:17 -0000
***************
*** 20,40 ****
#include "nodes/pg_list.h"
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 bytea *default_reloptions(Datum reloptions, bool validate,
! int minFillfactor, int defaultFillfactor);
!
extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
-
extern bytea *index_reloptions(RegProcedure amoptions, Datum reloptions,
! bool validate);
#endif /* RELOPTIONS_H */
--- 20,175 ----
#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;
+ 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.
+ *
+ * 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) \
+ if (HAVE_RELOPTION(optname, option)) \
+ { \
+ if (option.isset) \
+ var = option.values.int_val; \
+ else \
+ var = ((relopt_int *) option.gen)->default_val; \
+ continue; \
+ }
+
+ #define HANDLE_BOOL_RELOPTION(optname, var, option) \
+ if (HAVE_RELOPTION(optname, option)) \
+ { \
+ if (option.isset) \
+ var = option.values.bool_val; \
+ else \
+ var = ((relopt_bool *) option.gen)->default_val; \
+ continue; \
+ }
+
+ #define HANDLE_REAL_RELOPTION(optname, var, option) \
+ if (HAVE_RELOPTION(optname, option)) \
+ { \
+ if (option.isset) \
+ var = option.values.real_val; \
+ else \
+ var = ((relopt_real *) option.gen)->default_val; \
+ continue; \
+ }
+
+ /* Note that this assumes that the variable is already allocated! */
+ #define HANDLE_STRING_RELOPTION(optname, var, option) \
+ if (HAVE_RELOPTION(optname, option)) \
+ { \
+ strcpy(var, \
+ option.isset ? option.values.string_val : \
+ ((relopt_string *) option.gen)->default_val); \
+ continue; \
+ }
+
+ extern void initialize_reloptions(void);
+
+ 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 relopt_value *parseRelOptions(Datum options, bool validate,
! relopt_kind kind, int *numrelopts);
extern bytea *default_reloptions(Datum reloptions, bool validate,
! relopt_kind kind);
extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
extern bytea *index_reloptions(RegProcedure amoptions, Datum reloptions,
! bool validate);
#endif /* RELOPTIONS_H */
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers