KaiGai Kohei wrote:
> Alvaro Herrera wrote:

>>> 3. Why the "StdRdOptions lopts;" is necessary?
>>
>> It is like this because the autovacuum patch adds a few more options and
>> I want to have the chance to not allocate the part belonging to
>> autovacuum when none of the options are present.
>
> We can return NULL immediately without any allocation, when numoptions=0.
> Does it give us any pains?
> http://code.google.com/p/sepgsql/source/browse/trunk/sepgsql/src/backend/access/common/reloptions.c#765

That's not what I mean -- the problem is that some tables can have only
fillfactor allocated, and I didn't want to allocate the whole struct
just for fillfactor.  The technique I was using (which was to check the
length of the struct) is not going to work now with string reloptions
though, so it's not much of an issue.

> I thought you intend to apply validation checks in parse_one_reloption()
> invoked from parseRelOptions(), but now we have no checks in string
> reloptions.
> In my personal preference, it is more simple design parse_one_reloption()
> invoke a function pointer for validation checks.

Agreed, it seems better.  The attached patch adds that, a macro you
originally requested and another one, and it also fixes an off-by-one
bug I discovered while testing all of this.  I also attach the testing
patch I play with to check that this all works nicely.

Oh, the patch also removes a bunch of "continue" statements that, as far
as I can tell, no longer work after the macros were wrapped in
do { ... } while (0) :-(  I don't see any nice way to put the facility
back.

Thanks for all the input.

-- 
Alvaro Herrera                                http://www.CommandPrompt.com/
PostgreSQL Replication, Consulting, Custom Development, 24x7 support
*** src/backend/access/common/reloptions.c	6 Jan 2009 14:47:37 -0000	1.16
--- src/backend/access/common/reloptions.c	7 Jan 2009 13:35:42 -0000
***************
*** 336,344 ****
  /*
   * 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;
--- 336,350 ----
  /*
   * add_string_reloption
   *		Add a new string reloption
+  *
+  * "validator" is an optional function pointer that can be used to test the
+  * validity of the values.  It must elog(ERROR) when the argument string is
+  * not acceptable for the variable.  Note that the default value must pass
+  * the validation.
   */
  void
! add_string_reloption(int kind, char *name, char *desc, char *default_val,
! 					 validate_string_relopt validator)
  {
  	MemoryContext	oldcxt;
  	relopt_string  *newoption;
***************
*** 359,364 ****
--- 365,371 ----
  	newoption->gen.kind = kind;
  	newoption->gen.namelen = strlen(name);
  	newoption->gen.type = RELOPT_TYPE_STRING;
+ 	newoption->validate_cb = validator;
  	if (default_val)
  	{
  		strcpy(newoption->default_val, default_val);
***************
*** 372,377 ****
--- 379,388 ----
  		newoption->default_isnull = true;
  	}
  
+ 	/* make sure the validator/default combination is sane */
+ 	if (newoption->validate_cb)
+ 		(newoption->validate_cb) (newoption->default_val);
+ 
  	MemoryContextSwitchTo(oldcxt);
  
  	add_reloption((relopt_gen *) newoption);
***************
*** 729,738 ****
  			}
  			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);
--- 740,754 ----
  			}
  			break;
  		case RELOPT_TYPE_STRING:
! 			{
! 				relopt_string   *optstring = (relopt_string *) option->gen;
! 
! 				option->values.string_val = value;
! 				nofree = true;
! 				if (optstring->validate_cb)
! 					(optstring->validate_cb) (value);
! 				parsed = true;
! 			}
  			break;
  		default:
  			elog(ERROR, "unsupported reloption type %d", option->gen->type);
*** src/include/access/reloptions.h	6 Jan 2009 14:47:37 -0000	1.8
--- src/include/access/reloptions.h	7 Jan 2009 13:31:25 -0000
***************
*** 90,100 ****
--- 90,103 ----
  	double		max;
  } relopt_real;
  
+ typedef void (*validate_string_relopt) (char *);
+ 
  typedef struct relopt_string
  {
  	relopt_gen	gen;
  	int			default_len;
  	bool		default_isnull;
+ 	validate_string_relopt	validate_cb;
  	char		default_val[1];	/* variable length */
  } relopt_string;
  
***************
*** 113,119 ****
   * need this information.
   */
  #define HAVE_RELOPTION(optname, option) \
! 	(pg_strncasecmp(option.gen->name, optname, option.gen->namelen) == 0)
  
  #define HANDLE_INT_RELOPTION(optname, var, option, wasset)			\
  	do {															\
--- 116,122 ----
   * need this information.
   */
  #define HAVE_RELOPTION(optname, option) \
! 	(pg_strncasecmp(option.gen->name, optname, option.gen->namelen + 1) == 0)
  
  #define HANDLE_INT_RELOPTION(optname, var, option, wasset)			\
  	do {															\
***************
*** 124,130 ****
  			else													\
  				var = ((relopt_int *) option.gen)->default_val; 	\
  			(wasset) != NULL ? *(wasset) = option.isset : (dummyret)NULL; \
- 			continue;												\
  		}															\
  	} while (0)
  
--- 127,132 ----
***************
*** 137,143 ****
  			else													\
  				var = ((relopt_bool *) option.gen)->default_val;	\
  			(wasset) != NULL ? *(wasset) = option.isset : (dummyret) NULL; \
- 			continue;												\
  		}															\
  	} while (0)
  
--- 139,144 ----
***************
*** 150,156 ****
  			else													\
  				var = ((relopt_real *) option.gen)->default_val;	\
  			(wasset) != NULL ? *(wasset) = option.isset : (dummyret) NULL; \
- 			continue;												\
  		}															\
  	} while (0)
  
--- 151,156 ----
***************
*** 170,195 ****
  		if (HAVE_RELOPTION(optname, option))						\
  		{															\
  			relopt_string *optstring = (relopt_string *) option.gen;\
! 			char *string_val = NULL;								\
  																	\
  			if (option.isset)										\
  				string_val = option.values.string_val;				\
  			else if (!optstring->default_isnull)					\
  				string_val = optstring->default_val;				\
  			(wasset) != NULL ? *(wasset) = option.isset : (dummyret) NULL; \
  																	\
! 			if (!string_val)										\
  				var = 0;											\
  			else													\
  			{														\
! 				strcpy((char *)(base) + (offset), string_val);		\
  				var = (offset);										\
  				(offset) += strlen(string_val) + 1;					\
  			}														\
- 			continue;												\
  		}															\
  	} while (0)
  
  extern int add_reloption_kind(void);
  extern void add_bool_reloption(int kind, char *name, char *desc,
  				   bool default_val);
--- 170,214 ----
  		if (HAVE_RELOPTION(optname, option))						\
  		{															\
  			relopt_string *optstring = (relopt_string *) option.gen;\
! 			char *string_val;										\
  																	\
  			if (option.isset)										\
  				string_val = option.values.string_val;				\
  			else if (!optstring->default_isnull)					\
  				string_val = optstring->default_val;				\
+ 			else													\
+ 				string_val = NULL;									\
  			(wasset) != NULL ? *(wasset) = option.isset : (dummyret) NULL; \
  																	\
! 			if (string_val == NULL)									\
  				var = 0;											\
  			else													\
  			{														\
! 				strcpy(((char *)(base)) + (offset), string_val);	\
  				var = (offset);										\
  				(offset) += strlen(string_val) + 1;					\
  			}														\
  		}															\
  	} while (0)
  
+ /*
+  * For use during amoptions: get the strlen of a string option
+  * (either default or the user defined value)
+  */
+ #define GET_STRING_RELOPTION_LEN(option) \
+ 	((option).isset ? strlen((option).values.string_val) : \
+ 	 ((relopt_string *) (option).gen)->default_len)
+ 
+ /*
+  * For use by code reading options already parsed: get a pointer to the string
+  * value itself.  "optstruct" is the StdRdOption struct or equivalent, "member"
+  * is the struct member corresponding to the string option
+  */
+ #define GET_STRING_RELOPTION(optstruct, member) \
+ 	((optstruct)->member == 0 ? NULL : \
+ 	 (char *)(optstruct) + (optstruct)->member)                       
+ 
+ 
  extern int add_reloption_kind(void);
  extern void add_bool_reloption(int kind, char *name, char *desc,
  				   bool default_val);
***************
*** 198,204 ****
  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);
--- 217,223 ----
  extern void add_real_reloption(int kind, char *name, char *desc,
  				   double default_val, double min_val, double max_val);
  extern void add_string_reloption(int kind, char *name, char *desc,
! 					 char *default_val, validate_string_relopt validator);
  			
  extern Datum transformRelOptions(Datum oldOptions, List *defList,
  					bool ignoreOids, bool isReset);
Index: src/backend/access/common/reloptions.c
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/access/common/reloptions.c,v
retrieving revision 1.16
diff -c -p -r1.16 reloptions.c
Index: src/backend/access/nbtree/nbtinsert.c
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/access/nbtree/nbtinsert.c,v
retrieving revision 1.169
diff -c -p -r1.169 nbtinsert.c
*** src/backend/access/nbtree/nbtinsert.c	1 Jan 2009 17:23:35 -0000	1.169
--- src/backend/access/nbtree/nbtinsert.c	7 Jan 2009 03:21:31 -0000
***************
*** 17,22 ****
--- 17,23 ----
  
  #include "access/heapam.h"
  #include "access/nbtree.h"
+ #include "access/reloptions.h"
  #include "access/transam.h"
  #include "miscadmin.h"
  #include "storage/bufmgr.h"
***************
*** 565,570 ****
--- 566,590 ----
  	OffsetNumber firstright = InvalidOffsetNumber;
  	Size		itemsz;
  
+ 	{
+ 		BtOptions *options = (BtOptions *) rel->rd_options;
+ 
+ 		if (options)
+ 		{
+ 			char	*str;
+ 
+ 			str = GET_STRING_RELOPTION(options, teststring);
+ 			elog(LOG, "the teststring option is %s",
+ 				 str ? str : "empty");
+ 			str = GET_STRING_RELOPTION(options, teststring2);
+ 			elog(LOG, "the teststring2 option is %s",
+ 				 str ? str : "empty");
+ 			str = GET_STRING_RELOPTION(options, teststring3);
+ 			elog(LOG, "the teststring3 option is %s",
+ 				 str ? str : "empty");
+ 		}
+ 	}
+ 
  	page = BufferGetPage(buf);
  	lpageop = (BTPageOpaque) PageGetSpecialPointer(page);
  
Index: src/backend/access/nbtree/nbtutils.c
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/access/nbtree/nbtutils.c,v
retrieving revision 1.93
diff -c -p -r1.93 nbtutils.c
*** src/backend/access/nbtree/nbtutils.c	5 Jan 2009 17:14:28 -0000	1.93
--- src/backend/access/nbtree/nbtutils.c	7 Jan 2009 13:31:23 -0000
***************
*** 1395,1408 ****
  		Assert(found);
  }
  
  Datum
  btoptions(PG_FUNCTION_ARGS)
  {
  	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();
--- 1395,1490 ----
  		Assert(found);
  }
  
+ void
+ validate_teststring(char *string);
+ 
+ void
+ validate_teststring(char *string)
+ {
+ 	if (string == NULL)
+ 		elog(ERROR, "teststring must not be null!");
+ 	if (strlen(string) != 3)
+ 		elog(ERROR, "teststring must be of length 3, but it's \"%s\"", string);
+ }
+ 
  Datum
  btoptions(PG_FUNCTION_ARGS)
  {
  	Datum		reloptions = PG_GETARG_DATUM(0);
  	bool		validate = PG_GETARG_BOOL(1);
  	bytea	   *result;
+ 	relopt_value *options;
+ 	int			numoptions;
+ 	int			i;
+ 	static	bool initialized = false;
+ 
+ 	if (!initialized)
+ 	{
+ 		add_string_reloption(RELOPT_KIND_BTREE, "teststring", NULL,
+ 							 "foo", validate_teststring);
+ 		add_string_reloption(RELOPT_KIND_BTREE, "teststring2", NULL,
+ 							 "", NULL);
+ 		add_string_reloption(RELOPT_KIND_BTREE, "teststring3", NULL,
+ 							 NULL, NULL);
+ 		initialized = true;
+ 	}
+ 
+ 	options = parseRelOptions(reloptions, validate, RELOPT_KIND_BTREE,
+ 							  &numoptions);
+ 
+ 	/* if none set, we're done */
+ 	if (numoptions == 0)
+ 		result = NULL;
+ 	else
+ 	{
+ 		BtOptions *rdopts;
+ 		int		tstrlen;
+ 		int		t2strlen;
+ 		int		t3strlen;
+ 		int		offset;
+ 
+ 		for (i = 0; i < numoptions; i++)
+ 		{
+ 			if (HAVE_RELOPTION("teststring", options[i]))
+ 			{
+ 				tstrlen = GET_STRING_RELOPTION_LEN(options[i]);
+ 				continue;
+ 			}
+ 			if (HAVE_RELOPTION("teststring2", options[i]))
+ 			{
+ 				t2strlen = GET_STRING_RELOPTION_LEN(options[i]);
+ 				continue;
+ 			}
+ 			if (HAVE_RELOPTION("teststring3", options[i]))
+ 			{
+ 				t3strlen = GET_STRING_RELOPTION_LEN(options[i]);
+ 				continue;
+ 			}
+ 		}
+ 
+ 		rdopts = palloc0(sizeof(BtOptions) + tstrlen + t2strlen + t3strlen + 4);
+ 		offset = sizeof(BtOptions);
+ 
+ 		for (i = 0; i < numoptions; i++)
+ 		{
+ 			HANDLE_INT_RELOPTION("fillfactor", rdopts->fillfactor, options[i],
+ 								 (char *) NULL);
+ 			HANDLE_STRING_RELOPTION("teststring", rdopts->teststring,
+ 									options[i], rdopts, offset,
+ 									(char *) NULL);
+ 			HANDLE_STRING_RELOPTION("teststring2", rdopts->teststring2,
+ 									options[i], rdopts, offset,
+ 									(char *) NULL);
+ 			HANDLE_STRING_RELOPTION("teststring3", rdopts->teststring3,
+ 									options[i], rdopts, offset,
+ 									(char *) NULL);
+ 		}
+ 
+ 		pfree(options);
+ 		SET_VARSIZE(rdopts, offset);
+ 		result = (bytea *) rdopts;
+ 	}
  
  	if (result)
  		PG_RETURN_BYTEA_P(result);
  	PG_RETURN_NULL();
Index: src/include/access/nbtree.h
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/include/access/nbtree.h,v
retrieving revision 1.123
diff -c -p -r1.123 nbtree.h
*** src/include/access/nbtree.h	1 Jan 2009 17:23:56 -0000	1.123
--- src/include/access/nbtree.h	6 Jan 2009 20:35:32 -0000
***************
*** 595,598 ****
--- 595,609 ----
  extern void btree_xlog_cleanup(void);
  extern bool btree_safe_restartpoint(void);
  
+ /* must follow StdRdOptions conventions */
+ typedef struct BtOptions
+ {
+ 	int32	vl_len_;
+ 	int		fillfactor;
+ 	int		teststring;
+ 	int		teststring2;
+ 	int		teststring3;
+ 	/* variable length struct -- string is allocated here */
+ } BtOptions;
+ 
  #endif   /* NBTREE_H */
Index: src/include/access/reloptions.h
===================================================================
RCS file: /home/alvherre/Code/cvs/pgsql/src/include/access/reloptions.h,v
retrieving revision 1.8
diff -c -p -r1.8 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