diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c index 600e6c7243..46e2d00bc6 100644 --- a/src/backend/access/common/reloptions.c +++ b/src/backend/access/common/reloptions.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/common/reloptions.c,v 1.17 2009/01/08 19:34:41 alvherre Exp $ + * $PostgreSQL: pgsql/src/backend/access/common/reloptions.c,v 1.18 2009/01/12 21:02:14 alvherre Exp $ * *------------------------------------------------------------------------- */ @@ -33,11 +33,12 @@ * * 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 + * (i) decide on a type (integer, real, bool, string), name, default value, + * upper and lower bounds (if applicable); for strings, consider a validation + * routine. + * (ii) add a record below (or use add__reloption). + * (iii) add it to the appropriate options struct (perhaps StdRdOptions) + * (iv) add it to the appropriate handling routine (perhaps * default_reloptions) * (v) don't forget to document the option * @@ -381,7 +382,7 @@ add_string_reloption(int kind, char *name, char *desc, char *default_val, /* make sure the validator/default combination is sane */ if (newoption->validate_cb) - (newoption->validate_cb) (newoption->default_val, true); + (newoption->validate_cb) (newoption->default_val); MemoryContextSwitchTo(oldcxt); @@ -745,8 +746,8 @@ parse_one_reloption(relopt_value *option, char *text_str, int text_len, option->values.string_val = value; nofree = true; - if (optstring->validate_cb) - (optstring->validate_cb) (value, validate); + if (validate && optstring->validate_cb) + (optstring->validate_cb) (value); parsed = true; } break; @@ -762,6 +763,110 @@ parse_one_reloption(relopt_value *option, char *text_str, int text_len, pfree(value); } +/* + * Given the result from parseRelOptions, allocate a struct that's of the + * specified base size plus any extra space that's needed for string variables. + * + * "base" should be sizeof(struct) of the reloptions struct (StdRdOptions or + * equivalent). + */ +void * +allocateReloptStruct(Size base, relopt_value *options, int numoptions) +{ + Size size = base; + int i; + + for (i = 0; i < numoptions; i++) + if (options[i].gen->type == RELOPT_TYPE_STRING) + size += GET_STRING_RELOPTION_LEN(options[i]) + 1; + + return palloc0(size); +} + +/* + * Given the result of parseRelOptions and a parsing table, fill in the + * struct (previously allocated with allocateReloptStruct) with the parsed + * values. + * + * rdopts is the pointer to the allocated struct to be filled; basesize is + * the sizeof(struct) that was passed to allocateReloptStruct. options and + * numoptions are parseRelOptions' output. elems and numelems is the array + * of elements to be parsed. Note that when validate is true, it is expected + * that all options are also in elems. + */ +void +fillRelOptions(void *rdopts, Size basesize, relopt_value *options, + int numoptions, bool validate, relopt_parse_elt *elems, + int numelems) +{ + int i; + int offset = basesize; + + for (i = 0; i < numoptions; i++) + { + int j; + bool found = false; + + for (j = 0; j < numelems; j++) + { + if (pg_strcasecmp(options[i].gen->name, elems[j].optname) == 0) + { + relopt_string *optstring; + char *itempos = ((char *) rdopts) + elems[j].offset; + char *string_val; + + switch (options[i].gen->type) + { + case RELOPT_TYPE_BOOL: + *(bool *) itempos = options[i].isset ? + options[i].values.bool_val : + ((relopt_bool *) options[i].gen)->default_val; + break; + case RELOPT_TYPE_INT: + *(int *) itempos = options[i].isset ? + options[i].values.int_val : + ((relopt_int *) options[i].gen)->default_val; + break; + case RELOPT_TYPE_REAL: + *(double *) itempos = options[i].isset ? + options[i].values.real_val : + ((relopt_real *) options[i].gen)->default_val; + break; + case RELOPT_TYPE_STRING: + optstring = (relopt_string *) options[i].gen; + if (options[i].isset) + string_val = options[i].values.string_val; + else if (!optstring->default_isnull) + string_val = optstring->default_val; + else + string_val = NULL; + + if (string_val == NULL) + *(int *) itempos = 0; + else + { + strcpy((char *) rdopts + offset, string_val); + *(int *) itempos = offset; + offset += strlen(string_val) + 1; + } + break; + default: + elog(ERROR, "unrecognized reloption type %c", + options[i].gen->type); + break; + } + found = true; + break; + } + } + if (validate && !found) + elog(ERROR, "storate parameter \"%s\" not found in parse table", + options[i].gen->name); + } + SET_VARSIZE(rdopts, offset); +} + + /* * Option parser for anything that uses StdRdOptions (i.e. fillfactor only) */ @@ -770,10 +875,10 @@ default_reloptions(Datum reloptions, bool validate, relopt_kind kind) { relopt_value *options; StdRdOptions *rdopts; - StdRdOptions lopts; int numoptions; - int len; - int i; + relopt_parse_elt tab[] = { + {"fillfactor", RELOPT_TYPE_INT, offsetof(StdRdOptions, fillfactor)} + }; options = parseRelOptions(reloptions, validate, kind, &numoptions); @@ -781,21 +886,13 @@ default_reloptions(Datum reloptions, bool validate, relopt_kind kind) if (numoptions == 0) return NULL; - MemSet(&lopts, 0, sizeof(StdRdOptions)); + rdopts = allocateReloptStruct(sizeof(StdRdOptions), options, numoptions); - for (i = 0; i < numoptions; i++) - { - HANDLE_INT_RELOPTION("fillfactor", lopts.fillfactor, options[i], - (char *) NULL); - } + fillRelOptions((void *) rdopts, sizeof(StdRdOptions), options, numoptions, + validate, tab, lengthof(tab)); pfree(options); - len = sizeof(StdRdOptions); - rdopts = palloc(len); - memcpy(rdopts, &lopts, len); - SET_VARSIZE(rdopts, len); - return (bytea *) rdopts; } diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h index 5672cb4dcb..56f7589c1a 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.9 2009/01/08 19:34:41 alvherre Exp $ + * $PostgreSQL: pgsql/src/include/access/reloptions.h,v 1.10 2009/01/12 21:02:15 alvherre Exp $ * *------------------------------------------------------------------------- */ @@ -73,7 +73,7 @@ typedef struct relopt_bool relopt_gen gen; bool default_val; } relopt_bool; - + typedef struct relopt_int { relopt_gen gen; @@ -90,7 +90,8 @@ typedef struct relopt_real double max; } relopt_real; -typedef void (*validate_string_relopt) (char *value, bool validate); +/* validation routines for strings */ +typedef void (*validate_string_relopt) (char *value); typedef struct relopt_string { @@ -98,68 +99,88 @@ typedef struct relopt_string int default_len; bool default_isnull; validate_string_relopt validate_cb; - char default_val[1]; /* variable length */ + char default_val[1]; /* variable length, zero-terminated */ } relopt_string; +/* This is the input type for fillRelOptions */ +typedef struct +{ + char *optname; + relopt_type opttype; + int offset; +} relopt_parse_elt; + + /* - * 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.) + * These macros exist for the convenience of amoptions writers (but consider + * using fillRelOptions, which is a lot simpler). Beware of multiple + * evaluation of arguments! * * The last argument in the HANDLE_*_RELOPTION macros allows the caller to * determine whether the option was set (true), or its value acquired from * defaults (false); it can be passed as (char *) NULL if the caller does not * need this information. + * + * optname is the option name (a string), var is the variable + * on which the value should be stored (e.g. StdRdOptions->fillfactor), and + * option is a relopt_value pointer. + * + * The normal way to use this is to loop on the relopt_value array returned by + * parseRelOptions: + * for (i = 0; options[i].gen->name; i++) + * { + * if (HAVE_RELOPTION("fillfactor", options[i]) + * { + * HANDLE_INT_RELOPTION("fillfactor", rdopts->fillfactor, options[i], &isset); + * continue; + * } + * if (HAVE_RELOPTION("default_row_acl", options[i]) + * { + * ... + * } + * ... + * if (validate) + * ereport(ERROR, + * (errmsg("unknown option"))); + * } + * + * Note that this is more or less the same that fillRelOptions does, so only + * use this if you need to do something non-standard within some options' + * block. */ #define HAVE_RELOPTION(optname, option) \ (pg_strncasecmp(option.gen->name, optname, option.gen->namelen + 1) == 0) -#define HANDLE_INT_RELOPTION(optname, var, option, wasset) \ - do { \ - if (HAVE_RELOPTION(optname, option)) \ - { \ - if (option.isset) \ - var = option.values.int_val; \ - else \ - var = ((relopt_int *) option.gen)->default_val; \ - (wasset) != NULL ? *(wasset) = option.isset : (dummyret)NULL; \ - continue; \ - } \ +#define HANDLE_INT_RELOPTION(optname, var, option, wasset) \ + do { \ + if (option.isset) \ + var = option.values.int_val; \ + else \ + var = ((relopt_int *) option.gen)->default_val; \ + (wasset) != NULL ? *(wasset) = option.isset : (dummyret)NULL; \ } while (0) #define HANDLE_BOOL_RELOPTION(optname, var, option, wasset) \ do { \ - if (HAVE_RELOPTION(optname, option)) \ - { \ - if (option.isset) \ - var = option.values.bool_val; \ - else \ - var = ((relopt_bool *) option.gen)->default_val; \ - (wasset) != NULL ? *(wasset) = option.isset : (dummyret) NULL; \ - continue; \ - } \ + if (option.isset) \ + var = option.values.bool_val; \ + else \ + var = ((relopt_bool *) option.gen)->default_val; \ + (wasset) != NULL ? *(wasset) = option.isset : (dummyret) NULL; \ } while (0) -#define HANDLE_REAL_RELOPTION(optname, var, option, wasset) \ - do { \ - if (HAVE_RELOPTION(optname, option)) \ - { \ - if (option.isset) \ - var = option.values.real_val; \ - else \ - var = ((relopt_real *) option.gen)->default_val; \ - (wasset) != NULL ? *(wasset) = option.isset : (dummyret) NULL; \ - continue; \ - } \ +#define HANDLE_REAL_RELOPTION(optname, var, option, wasset) \ + do { \ + if (option.isset) \ + var = option.values.real_val; \ + else \ + var = ((relopt_real *) option.gen)->default_val; \ + (wasset) != NULL ? *(wasset) = option.isset : (dummyret) NULL; \ } while (0) /* * Note that this assumes that the variable is already allocated at the tail of - * reloptions structure (StdRdOptions or other). + * reloptions structure (StdRdOptions or equivalent). * * "base" is a pointer to the reloptions structure, and "offset" is an integer * variable that must be initialized to sizeof(reloptions structure). This @@ -170,27 +191,23 @@ typedef struct relopt_string */ #define HANDLE_STRING_RELOPTION(optname, var, option, base, offset, wasset) \ do { \ - if (HAVE_RELOPTION(optname, option)) \ - { \ - relopt_string *optstring = (relopt_string *) option.gen;\ - char *string_val; \ - if (option.isset) \ - string_val = option.values.string_val; \ - else if (!optstring->default_isnull) \ - string_val = optstring->default_val; \ - else \ - string_val = NULL; \ - (wasset) != NULL ? *(wasset) = option.isset : (dummyret) NULL; \ - if (string_val == NULL) \ - var = 0; \ - else \ - { \ - strcpy(((char *)(base)) + (offset), string_val); \ - var = (offset); \ - (offset) += strlen(string_val) + 1; \ - } \ - continue; \ - } \ + relopt_string *optstring = (relopt_string *) option.gen;\ + char *string_val; \ + if (option.isset) \ + string_val = option.values.string_val; \ + else if (!optstring->default_isnull) \ + string_val = optstring->default_val; \ + else \ + string_val = NULL; \ + (wasset) != NULL ? *(wasset) = option.isset : (dummyret) NULL; \ + if (string_val == NULL) \ + var = 0; \ + else \ + { \ + strcpy(((char *)(base)) + (offset), string_val); \ + var = (offset); \ + (offset) += strlen(string_val) + 1; \ + } \ } while (0) /* @@ -208,7 +225,7 @@ typedef struct relopt_string */ #define GET_STRING_RELOPTION(optstruct, member) \ ((optstruct)->member == 0 ? NULL : \ - (char *)(optstruct) + (optstruct)->member) + (char *)(optstruct) + (optstruct)->member) extern int add_reloption_kind(void); @@ -220,12 +237,17 @@ 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, validate_string_relopt validator); - + 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 void *allocateReloptStruct(Size base, relopt_value *options, + int numoptions); +extern void fillRelOptions(void *rdopts, Size basesize, relopt_value *options, + int numoptions, bool validate, relopt_parse_elt *elems, + int nelems); extern bytea *default_reloptions(Datum reloptions, bool validate, relopt_kind kind);