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

Reply via email to