From ba748f7a11ef884277b61d1708a17a44acfd1736 Mon Sep 17 00:00:00 2001 From: Alvaro Herrera Date: Mon, 5 Jan 2009 17:14:28 +0000 Subject: [PATCH] Change the reloptions machinery to use a table-based parser, and provide 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 | 661 +++++++++++++++++++++---- src/backend/access/gin/ginutil.c | 13 +- src/backend/access/gist/gistutil.c | 7 +- src/backend/access/hash/hashutil.c | 7 +- src/backend/access/nbtree/nbtutils.c | 6 +- src/include/access/reloptions.h | 167 ++++++- src/include/catalog/catversion.h | 4 +- 7 files changed, 724 insertions(+), 141 deletions(-) diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c index ec2abcb298..3401667b9e 100644 --- a/src/backend/access/common/reloptions.c +++ b/src/backend/access/common/reloptions.c @@ -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" @@ -22,8 +25,357 @@ #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,137 +550,236 @@ 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 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. + * 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. */ -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(); + + /* 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))) - 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++) + 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; - /* Search for a match in keywords */ - for (j = 0; j < numkeywords; j++) + array = DatumGetArrayTypeP(options); + + Assert(ARR_ELEMTYPE(array) == TEXTOID); + + deconstruct_array(array, TEXTOID, -1, false, 'i', + &optiondatums, NULL, &noptions); + + 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; +} /* - * Parse reloptions for anything using StdRdOptions (ie, fillfactor only) + * Subroutine for parseRelOptions, to parse and validate a single option's + * value */ -bytea * -default_reloptions(Datum reloptions, bool validate, - int minFillfactor, int defaultFillfactor) +static void +parse_one_reloption(relopt_value *option, char *text_str, int text_len, + bool validate) { - static const char *const default_keywords[1] = {"fillfactor"}; - char *values[1]; - int fillfactor; - StdRdOptions *result; + char *value; + int value_len; + bool parsed; + bool nofree = false; - parseRelOptions(reloptions, 1, default_keywords, values, validate); + if (option->isset && validate) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("parameter \"%s\" specified more than once", + option->gen->name))); - /* - * 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; + 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'; - if (!parse_int(values[0], &fillfactor, 0, NULL)) + switch (option->gen->type) { - if (validate) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("fillfactor must be an integer: \"%s\"", - values[0]))); - return NULL; + 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 (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; + 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). @@ -336,9 +787,7 @@ default_reloptions(Datum reloptions, bool validate, 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); } diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c index 1886aa2669..222ea67788 100644 --- a/src/backend/access/gin/ginutil.c +++ b/src/backend/access/gin/ginutil.c @@ -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(); diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c index 0d66492601..fa1e3088ad 100644 --- a/src/backend/access/gist/gistutil.c +++ b/src/backend/access/gist/gistutil.c @@ -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(); diff --git a/src/backend/access/hash/hashutil.c b/src/backend/access/hash/hashutil.c index 4261f0c75e..42e79376f8 100644 --- a/src/backend/access/hash/hashutil.c +++ b/src/backend/access/hash/hashutil.c @@ -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(); diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c index 3963ce847c..1649307251 100644 --- a/src/backend/access/nbtree/nbtutils.c +++ b/src/backend/access/nbtree/nbtutils.c @@ -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(); diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h index ee1f9f2d57..8a2e2286be 100644 --- a/src/include/access/reloptions.h +++ b/src/include/access/reloptions.h @@ -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 $ * *------------------------------------------------------------------------- */ @@ -20,21 +20,168 @@ #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 */ diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 9a11d875a2..13e47f0399 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -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