Hi, Here's a patch for improving the general reloptions mechanism. What this patch does is add a table-based option parser. This allows adding new options very easily, and stops the business of having to pass the minimum and default fillfactor each time you want the reloptions processed. (This approach would not scale very well; each new reloption requires more parameters to default_reloptions, and at the same time we are forcing external AMs to support fillfactor, which they may very well do not. For example GIN was already passing useless values pointlessly.)
I kept StdRdOptions as a fixed struct, which fixes the previous complain about speed. The new code parses the array and stores the values into the fixed struct. The only new thing in this area is that default_reloptions has to walk the returned array of relopt_gen to store the values in the struct. So in order to add a new option, it is necessary to patch both the options table (intRelOpts, etc) *and* default_reloptions. This is a bit ugly but it's the only way I found to keep both generality and speed. Right now, external AMs cannot do anything much apart from fillfactor, but it is very simple to add a routine to register a new "reloption kind" and another to register options in the table. That and a new *_reloptions routine (which needs to be registered in pg_am) would allow an AM to create whatever options it needs. Note that the only types supported are int, bool, real. We don't support strings. I don't see this as a problem, but shout if you disagree. (In the current patch, the bool and real lists are empty. The autovacuum patch adds items to both.) The patch to add the autovacuum options on top of this is very light on reloptions.c but heavy on lots of other places, which is why I'm submitting this separately. -- 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 19 Dec 2008 21:54:32 -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,157 ---- #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 const relopt_bool boolRelOpts[] = + { + /* list terminator */ + { { NULL } } + }; + + static const 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 const relopt_real realRelOpts[] = + { + /* list terminator */ + { { NULL } } + }; + + static relopt_gen *relOpts = NULL; + + static void parse_one_reloption(relopt_gen *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++; + + 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++; + } + + /* add a list terminator */ + relOpts[j].name = NULL; + } /* * Transform a relation options list (list of DefElem) into the text array *************** transformRelOptions(Datum oldOptions, Li *** 73,81 **** for (i = 0; i < noldoptions; i++) { ! text *oldoption = DatumGetTextP(oldoptions[i]); ! char *text_str = VARDATA(oldoption); ! int text_len = VARSIZE(oldoption) - VARHDRSZ; /* Search for a match in defList */ foreach(cell, defList) --- 201,208 ---- for (i = 0; i < noldoptions; i++) { ! char *text_str = TextDatumGetCString(oldoptions[i]); ! int text_len = strlen(text_str); /* Search for a match in defList */ foreach(cell, defList) *************** untransformRelOptions(Datum options) *** 198,332 **** /* * 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; } --- 325,553 ---- /* * Interpret reloptions that are given in text-array format. * ! * The return value is a relopt_gen * array on which the options actually ! * set in the options array have been marked with isset=true. The ! * length of this array is returned in *numrelopts. ! * ! * options is the array as constructed by transformRelOptions. ! * kind specifies the family of options to be processed. ! * ! * XXX Note that this function is not reentrant -- the return array contains ! * pointers to the global relOpts array which have been modified in place. ! * (It is possible to fix this by copying the entries, but right now there ! * is no point in doing so.) */ ! relopt_gen ** ! parseRelOptions(Datum options, bool validate, relopt_kind kind, ! int *numrelopts) { ! relopt_gen **reloptions = NULL; ! int maxopts = 0; ! int numoptions = 0; int i; ! /* Build a list of expected options, based on kind */ ! for (i = 0; relOpts[i].name; i++) ! { ! if (relOpts[i].kind == kind) ! { ! /* add this option to the list */ ! if (numoptions >= maxopts) ! { ! if (maxopts == 0) ! maxopts = 16; ! else ! maxopts *= 2; ! if (reloptions == NULL) ! reloptions = palloc(maxopts * sizeof(relopt_gen *)); ! else ! reloptions = repalloc(reloptions, maxopts * sizeof(relopt_gen *)); ! } ! reloptions[numoptions] = &relOpts[i]; ! numoptions++; ! } ! } ! /* initialize to all unset */ ! for (i = 0; i < numoptions; i++) ! reloptions[i]->isset = false; ! /* 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]->namelen; ! if (text_len > kw_len && text_str[kw_len] == '=' && ! pg_strncasecmp(text_str, reloptions[j]->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_gen *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->name))); + + value_len = text_len - option->namelen - 1; + value = (char *) palloc(value_len + 1); + memcpy(value, text_str + option->namelen + 1, value_len); + value[value_len] = '\0'; + + switch (option->type) + { + case RELOPT_TYPE_BOOL: + { + parsed = parse_bool(value, + &((relopt_bool *) option)->parsed_val); + if (validate && !parsed) + ereport(ERROR, + (errmsg("invalid value for boolean option \"%s\": %s", + option->name, value))); + } + break; + case RELOPT_TYPE_INT: + { + relopt_int *optint = (relopt_int *) option; + + parsed = parse_int(value, &optint->parsed_val, + 0, NULL); + if (validate && !parsed) + ereport(ERROR, + (errmsg("invalid value for integer option \"%s\": %s", + option->name, value))); + if (validate && (optint->parsed_val < optint->min || + optint->parsed_val > optint->max)) + ereport(ERROR, + (errmsg("value %s out of bounds for option \"%s\"", + value, option->name), + errdetail("Valid values are between \"%d\" and \"%d\".", + optint->min, optint->max))); + } + break; + case RELOPT_TYPE_REAL: + { + relopt_real *optreal = (relopt_real *) option; + + parsed = parse_real(value, + &optreal->parsed_val); + if (validate && !parsed) + ereport(ERROR, + (errmsg("invalid value for floating point option \"%s\": %s", + option->name, value))); + if (validate && (optreal->parsed_val < optreal->min || + optreal->parsed_val > optreal->max)) + ereport(ERROR, + (errmsg("value %s out of bounds for option \"%s\"", + value, option->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_gen **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 (!options[i]->isset) ! continue; ! ! if (pg_strncasecmp(options[i]->name, "fillfactor", options[i]->namelen) == 0) ! { ! lopts.fillfactor = ((relopt_int *) options[i])->parsed_val; ! break; ! } } ! pfree(options); ! len = offsetof(StdRdOptions, fillfactor) + sizeof(int); ! rdopts = palloc(len); ! memcpy(rdopts, &lopts, len); ! SET_VARSIZE(rdopts, len); ! return (bytea *) rdopts; } *************** default_reloptions(Datum reloptions, boo *** 336,344 **** bytea * heap_reloptions(char relkind, Datum reloptions, bool validate) { ! return default_reloptions(reloptions, validate, ! HEAP_MIN_FILLFACTOR, ! HEAP_DEFAULT_FILLFACTOR); } --- 557,563 ---- 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 19 Dec 2008 17:20:40 -0000 *************** extractEntriesSU(GinState *ginstate, Off *** 313,332 **** Datum ginoptions(PG_FUNCTION_ARGS) { ! Datum reloptions = PG_GETARG_DATUM(0); ! 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(); --- 313,324 ---- Datum ginoptions(PG_FUNCTION_ARGS) { ! Datum reloptions = PG_GETARG_DATUM(0); ! 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 17:20:40 -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 17:20:40 -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/nbtsort.c =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/access/nbtree/nbtsort.c,v retrieving revision 1.118 diff -c -p -r1.118 nbtsort.c *** src/backend/access/nbtree/nbtsort.c 30 Sep 2008 10:52:10 -0000 1.118 --- src/backend/access/nbtree/nbtsort.c 19 Dec 2008 17:20:40 -0000 *************** _bt_pagestate(BTWriteState *wstate, uint *** 340,346 **** state->btps_full = (BLCKSZ * (100 - BTREE_NONLEAF_FILLFACTOR) / 100); else state->btps_full = RelationGetTargetPageFreeSpace(wstate->index, ! BTREE_DEFAULT_FILLFACTOR); /* no parent level, yet */ state->btps_next = NULL; --- 340,346 ---- state->btps_full = (BLCKSZ * (100 - BTREE_NONLEAF_FILLFACTOR) / 100); else state->btps_full = RelationGetTargetPageFreeSpace(wstate->index, ! BTREE_DEFAULT_FILLFACTOR); /* no parent level, yet */ state->btps_next = 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 19 Dec 2008 17:20:40 -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/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 17:20:40 -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 19 Dec 2008 17:20: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,92 ---- #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 ! } 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; ! bool isset; ! } relopt_gen; ! ! /* reloptions records for specific variable types */ ! typedef struct relopt_bool ! { ! relopt_gen gen; ! bool default_val; ! bool parsed_val; ! } relopt_bool; ! ! typedef struct relopt_int ! { ! relopt_gen gen; ! int default_val; ! int min; ! int max; ! int parsed_val; ! } relopt_int; ! ! typedef struct relopt_real ! { ! relopt_gen gen; ! double default_val; ! double min; ! double max; ! double parsed_val; ! } relopt_real; + extern void initialize_reloptions(void); + extern Datum transformRelOptions(Datum oldOptions, List *defList, + bool ignoreOids, bool isReset); extern List *untransformRelOptions(Datum options); ! extern relopt_gen **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