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 ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers