Alvaro Herrera wrote: > Tom Lane wrote: > > Alvaro Herrera <alvhe...@commandprompt.com> writes: > > > > I'm intending to have a new routine which would reserve a value at > > > runtime. This value would be later be passed by the AM to create new > > > options on the table. > > > > What do you mean by "at runtime"? Surely the value would have to remain > > stable across database restarts, since it's going to relate to stuff > > that is in catalog entries. > > No, there's no need for the value to be stable across restart; what's > stored in catalogs is the option name, which is linked to the kind > number only in the parser table.
So this is an updated patch. This now allows a user-defined AM to create new reloptions and pass them down to the parser for parsing and checking. I also attach a proof-of-concept patch that adds three new options to btree (which do nothing apart from logging a message at insert time). This patch demonstrates the coding pattern that a user-defined AM should follow to add and use new storage options. The main thing I find slightly hateful about this patch is that the code to translate from the returned relopt_value array and the fixed struct is rather verbose; and that the AM needs to duplicate the code in default_reloptions. I don't find it ugly enough to warrant objecting to the patch as a whole however. The neat thing about this code is that the parsing and searching is done only once, when the relcache entry is loaded. Later accesses to the option values themselves is just a struct access, and thus plenty quick. -- Alvaro Herrera http://www.CommandPrompt.com/ The PostgreSQL Company - Command Prompt, Inc.
Index: src/backend/access/common/reloptions.c =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/access/common/reloptions.c,v retrieving revision 1.11 diff -c -p -r1.11 reloptions.c *** src/backend/access/common/reloptions.c 23 Jul 2008 17:29:53 -0000 1.11 --- src/backend/access/common/reloptions.c 30 Dec 2008 14:33:15 -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,307 ---- #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), 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_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 void parse_one_reloption(relopt_value *option, char *text_str, + int text_len, bool validate); + + /* + * initialize_reloptions + * initialization routine, must be called at backend start + * + * 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++; + 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; 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; + + initialize_reloptions(); + } + + /* + * allocate_reloption + * Allocate a new reloption and initialize the type-agnostic fields + */ + static relopt_gen * + allocate_reloption(int kind, int type, char *name, char *desc) + { + MemoryContext oldcxt; + size_t size; + relopt_gen *newoption; + + 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"); + } + + 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); + } /* * 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); } --- 476,700 ---- /* * Interpret reloptions that are given in text-array format. * ! * 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. ! * ! * options is the array as constructed by transformRelOptions. ! * kind specifies the family of options to be processed. */ ! relopt_value * ! parseRelOptions(Datum options, bool validate, relopt_kind kind, ! int *numrelopts) { ! relopt_value *reloptions; ! int numoptions = 0; int i; + int j; ! /* Build a list of expected options, based on kind */ ! for (i = 0; relOpts[i]; i++) ! if (relOpts[i]->kind == kind) ! numoptions++; ! 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; + + 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; + } + + if (parsed) + option->isset = true; + 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++) { ! if (pg_strncasecmp(options[i].gen->name, "fillfactor", ! options[i].gen->namelen) == 0) ! { ! if (options[i].isset) ! lopts.fillfactor = options[i].values.int_val; ! else ! lopts.fillfactor = ((relopt_int *) options[i].gen)->default_val; ! continue; ! } } ! pfree(options); ! len = offsetof(StdRdOptions, fillfactor) + sizeof(int); ! 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/Code/cvs/pgsql/src/backend/access/gin/ginutil.c,v retrieving revision 1.18 diff -c -p -r1.18 ginutil.c *** src/backend/access/gin/ginutil.c 3 Nov 2008 20:47:48 -0000 1.18 --- src/backend/access/gin/ginutil.c 22 Dec 2008 16:23:59 -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/Code/cvs/pgsql/src/backend/access/gist/gistutil.c,v retrieving revision 1.31 diff -c -p -r1.31 gistutil.c *** src/backend/access/gist/gistutil.c 30 Sep 2008 10:52:10 -0000 1.31 --- src/backend/access/gist/gistutil.c 19 Dec 2008 23:39:48 -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/Code/cvs/pgsql/src/backend/access/hash/hashutil.c,v retrieving revision 1.57 diff -c -p -r1.57 hashutil.c *** src/backend/access/hash/hashutil.c 15 Sep 2008 18:43:41 -0000 1.57 --- src/backend/access/hash/hashutil.c 19 Dec 2008 23:39:48 -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/Code/cvs/pgsql/src/backend/access/nbtree/nbtutils.c,v retrieving revision 1.91 diff -c -p -r1.91 nbtutils.c *** src/backend/access/nbtree/nbtutils.c 19 Jun 2008 00:46:03 -0000 1.91 --- src/backend/access/nbtree/nbtutils.c 30 Dec 2008 14:45:56 -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,1409 ---- 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/backend/utils/init/postinit.c =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/utils/init/postinit.c,v retrieving revision 1.186 diff -c -p -r1.186 postinit.c *** src/backend/utils/init/postinit.c 23 Sep 2008 09:20:36 -0000 1.186 --- src/backend/utils/init/postinit.c 19 Dec 2008 23:39:48 -0000 *************** *** 19,24 **** --- 19,25 ---- #include <unistd.h> #include "access/heapam.h" + #include "access/reloptions.h" #include "access/xact.h" #include "catalog/catalog.h" #include "catalog/namespace.h" *************** InitPostgres(const char *in_dbname, Oid *** 645,650 **** --- 646,655 ---- if (!bootstrap) pgstat_bestart(); + /* initialize the relation options subsystem */ + if (!bootstrap) + initialize_reloptions(); + /* close the transaction we started above */ if (!bootstrap) CommitTransactionCommand(); Index: src/include/access/reloptions.h =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/include/access/reloptions.h,v retrieving revision 1.5 diff -c -p -r1.5 reloptions.h *** src/include/access/reloptions.h 1 Jan 2008 19:45:56 -0000 1.5 --- src/include/access/reloptions.h 29 Dec 2008 23:13:40 -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,113 ---- #include "nodes/pg_list.h" ! /* types supported by reloptions */ ! typedef enum relopt_type ! { ! RELOPT_TYPE_BOOL, ! RELOPT_TYPE_INT, ! RELOPT_TYPE_REAL ! } 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; ! } 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; ! ! 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 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 */
*** src/backend/access/nbtree/nbtinsert.c 3 Nov 2008 20:47:48 -0000 1.168 --- src/backend/access/nbtree/nbtinsert.c 30 Dec 2008 14:29:21 -0000 *************** *** 565,570 **** --- 565,581 ---- OffsetNumber firstright = InvalidOffsetNumber; Size itemsz; + { + BtOptions *options = (BtOptions *) rel->rd_options; + + if (options) + { + elog(LOG, "the testbool option is %s", options->testbool ? "true" : "false"); + elog(LOG, "the testint option is %d", options->testint); + elog(LOG, "the testreal option is %f", options->testreal); + } + } + page = BufferGetPage(buf); lpageop = (BTPageOpaque) PageGetSpecialPointer(page); *** src/backend/access/nbtree/nbtutils.c 19 Jun 2008 00:46:03 -0000 1.91 --- src/backend/access/nbtree/nbtutils.c 30 Dec 2008 14:26:17 -0000 *************** *** 1401,1409 **** Datum reloptions = PG_GETARG_DATUM(0); 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(); --- 1401,1481 ---- Datum reloptions = PG_GETARG_DATUM(0); bool validate = PG_GETARG_BOOL(1); bytea *result; + relopt_value *options; + BtOptions *rdopts; + BtOptions lopts; + int numoptions; + int len; + int i; + static bool initialized = false; + + if (!initialized) + { + add_bool_reloption(RELOPT_KIND_BTREE, "testbool", NULL, false); + add_int_reloption(RELOPT_KIND_BTREE, "testint", NULL, 25, 10, 100); + add_real_reloption(RELOPT_KIND_BTREE, "testreal", NULL, 3.14, 2.78, 42.0); + initialized = true; + } + + options = parseRelOptions(reloptions, validate, RELOPT_KIND_BTREE, &numoptions); + + /* if none set, we're done */ + if (numoptions == 0) + result = NULL; + else + { + MemSet(&lopts, 0, sizeof(BtOptions)); + + for (i = 0; i < numoptions; i++) + { + if (pg_strncasecmp(options[i].gen->name, "fillfactor", + options[i].gen->namelen) == 0) + { + if (options[i].isset) + lopts.fillfactor = options[i].values.int_val; + else + lopts.fillfactor = ((relopt_int *) options[i].gen)->default_val; + continue; + } + if (pg_strncasecmp(options[i].gen->name, "testbool", + options[i].gen->namelen) == 0) + { + if (options[i].isset) + lopts.testbool = options[i].values.bool_val; + else + lopts.testbool = ((relopt_bool *) options[i].gen)->default_val; + continue; + } + if (pg_strncasecmp(options[i].gen->name, "testint", + options[i].gen->namelen) == 0) + { + if (options[i].isset) + lopts.testint = options[i].values.int_val; + else + lopts.testint = ((relopt_int *) options[i].gen)->default_val; + continue; + } + if (pg_strncasecmp(options[i].gen->name, "testreal", + options[i].gen->namelen) == 0) + { + if (options[i].isset) + lopts.testreal = options[i].values.real_val; + else + lopts.testreal = ((relopt_real *) options[i].gen)->default_val; + continue; + } + } + + pfree(options); + + len = sizeof(BtOptions); + rdopts = palloc(len); + memcpy(rdopts, &lopts, len); + SET_VARSIZE(rdopts, len); + + result = (bytea *) rdopts; + } if (result) PG_RETURN_BYTEA_P(result); PG_RETURN_NULL(); *** src/include/access/nbtree.h 13 Jul 2008 20:45:47 -0000 1.121 --- src/include/access/nbtree.h 30 Dec 2008 14:26:28 -0000 *************** *** 595,598 **** --- 595,608 ---- extern void btree_xlog_cleanup(void); extern bool btree_safe_restartpoint(void); + /* must follow StdRdOptions conventions */ + typedef struct BtOptions + { + int32 vl_len_; + int fillfactor; + bool testbool; + int testint; + double testreal; + } BtOptions; + #endif /* NBTREE_H */
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers