From b813c8dacacfb87c88cf53d36a07fc6ec3a241a5 Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Thu, 8 Jan 2009 19:34:41 +0000
Subject: [PATCH] A couple further reloptions improvements, per KaiGai Kohei:
 add a validation function to the string type and add a couple of macros for
 string handling.

In passing, fix an off-by-one bug of mine.
---
 src/backend/access/common/reloptions.c | 28 ++++++++++++++----
 src/include/access/reloptions.h        | 41 +++++++++++++++++++-------
 2 files changed, 53 insertions(+), 16 deletions(-)

diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 7690da0e671..600e6c7243d 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/access/common/reloptions.c,v 1.16 2009/01/06 14:47:37 alvherre Exp $
+ *	  $PostgreSQL: pgsql/src/backend/access/common/reloptions.c,v 1.17 2009/01/08 19:34:41 alvherre Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -336,9 +336,15 @@ add_real_reloption(int kind, char *name, char *desc, double default_val,
 /*
  * 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)
+add_string_reloption(int kind, char *name, char *desc, char *default_val,
+					 validate_string_relopt validator)
 {
 	MemoryContext	oldcxt;
 	relopt_string  *newoption;
@@ -359,6 +365,7 @@ add_string_reloption(int kind, char *name, char *desc, char *default_val)
 	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,6 +379,10 @@ add_string_reloption(int kind, char *name, char *desc, char *default_val)
 		newoption->default_isnull = true;
 	}
 
+	/* make sure the validator/default combination is sane */
+	if (newoption->validate_cb)
+		(newoption->validate_cb) (newoption->default_val, true);
+
 	MemoryContextSwitchTo(oldcxt);
 
 	add_reloption((relopt_gen *) newoption);
@@ -729,10 +740,15 @@ parse_one_reloption(relopt_value *option, char *text_str, int text_len,
 			}
 			break;
 		case RELOPT_TYPE_STRING:
-			option->values.string_val = value;
-			nofree = true;
-			parsed = true;
-			/* no validation possible */
+			{
+				relopt_string   *optstring = (relopt_string *) option->gen;
+
+				option->values.string_val = value;
+				nofree = true;
+				if (optstring->validate_cb)
+					(optstring->validate_cb) (value, validate);
+				parsed = true;
+			}
 			break;
 		default:
 			elog(ERROR, "unsupported reloption type %d", option->gen->type);
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index 5238c70904a..5672cb4dcbf 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -11,7 +11,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/access/reloptions.h,v 1.8 2009/01/06 14:47:37 alvherre Exp $
+ * $PostgreSQL: pgsql/src/include/access/reloptions.h,v 1.9 2009/01/08 19:34:41 alvherre Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -90,11 +90,14 @@ typedef struct relopt_real
 	double		max;
 } relopt_real;
 
+typedef void (*validate_string_relopt) (char *value, bool validate);
+
 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,7 +116,7 @@ typedef struct relopt_string
  * need this information.
  */
 #define HAVE_RELOPTION(optname, option) \
-	(pg_strncasecmp(option.gen->name, optname, option.gen->namelen) == 0)
+	(pg_strncasecmp(option.gen->name, optname, option.gen->namelen + 1) == 0)
 
 #define HANDLE_INT_RELOPTION(optname, var, option, wasset)			\
 	do {															\
@@ -141,7 +144,7 @@ typedef struct relopt_string
 		}															\
 	} while (0)
 
-#define HANDLE_REAL_RELOPTION(optname, var, option, wasset) 			\
+#define HANDLE_REAL_RELOPTION(optname, var, option, wasset) 		\
 	do {															\
 		if (HAVE_RELOPTION(optname, option))						\
 		{															\
@@ -166,23 +169,23 @@ typedef struct relopt_string
  * string options have been processed.
  */
 #define HANDLE_STRING_RELOPTION(optname, var, option, base, offset, wasset)	\
-	do {															\
+	do {														\
 		if (HAVE_RELOPTION(optname, option))						\
 		{															\
 			relopt_string *optstring = (relopt_string *) option.gen;\
-			char *string_val = NULL;								\
-																	\
+			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)										\
+			if (string_val == NULL)									\
 				var = 0;											\
 			else													\
 			{														\
-				strcpy((char *)(base) + (offset), string_val);		\
+				strcpy(((char *)(base)) + (offset), string_val);	\
 				var = (offset);										\
 				(offset) += strlen(string_val) + 1;					\
 			}														\
@@ -190,6 +193,24 @@ typedef struct relopt_string
 		}															\
 	} 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,7 +219,7 @@ extern void add_int_reloption(int kind, char *name, char *desc,
 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);
+					 char *default_val, validate_string_relopt validator);
 			
 extern Datum transformRelOptions(Datum oldOptions, List *defList,
 					bool ignoreOids, bool isReset);
-- 
GitLab