Simplify the writing of amoptions routines by introducing a convenience

fillRelOptions routine that stores the parsed values in the struct using a
table-based approach.  Per Tom suggestion.  Also remove the "continue"
in HANDLE_*_RELOPTION macros, which were useless and in spirit they were
assuming too much of how the macros were going to be used.  (Note that these
macros are now unused, but the intention is to introduce some usage in a
future autovacuum patch, which is why they weren't completely removed.)

Also, do not call the string validation routine when not validating.  It seems
less error-prone this way, per commentary on the amoptions SGML docs.
This commit is contained in:
Alvaro Herrera 2009-01-12 21:02:15 +00:00
parent 8a4505013d
commit 8ebe1e356c
2 changed files with 208 additions and 89 deletions

View File

@ -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_<type>_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;
}

View 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.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);