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 (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers