mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-12-27 08:39:28 +08:00
Change the reloptions machinery to use a table-based parser, and provide
a more complete framework for writing custom option processing routines by user-defined access methods. Catalog version bumped due to the general API changes, which are going to affect user-defined "amoptions" routines.
This commit is contained in:
parent
b0a6ad70a1
commit
ba748f7a11
@ -8,13 +8,16 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/access/common/reloptions.c,v 1.12 2009/01/01 17:23:34 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/access/common/reloptions.c,v 1.13 2009/01/05 17:14:28 alvherre Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#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,8 +25,357 @@
|
||||
#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 initialize_reloptions(void);
|
||||
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.
|
||||
*/
|
||||
static 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)
|
||||
{
|
||||
if (last_assigned_kind >= RELOPT_KIND_MAX)
|
||||
ereport(ERROR,
|
||||
(errmsg("user-defined relation parameter types limit exceeded")));
|
||||
|
||||
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;
|
||||
int default_len = 0;
|
||||
|
||||
oldcxt = MemoryContextSwitchTo(TopMemoryContext);
|
||||
|
||||
if (default_val)
|
||||
default_len = strlen(default_val);
|
||||
|
||||
newoption = palloc0(sizeof(relopt_string) + default_len);
|
||||
|
||||
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;
|
||||
if (default_val)
|
||||
{
|
||||
strcpy(newoption->default_val, default_val);
|
||||
newoption->default_len = default_len;
|
||||
newoption->default_isnull = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
newoption->default_val[0] = '\0';
|
||||
newoption->default_len = 0;
|
||||
newoption->default_isnull = true;
|
||||
}
|
||||
|
||||
MemoryContextSwitchTo(oldcxt);
|
||||
|
||||
add_reloption((relopt_gen *) newoption);
|
||||
}
|
||||
|
||||
/*
|
||||
* Transform a relation options list (list of DefElem) into the text array
|
||||
@ -198,137 +550,236 @@ untransformRelOptions(Datum options)
|
||||
/*
|
||||
* 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.
|
||||
* options is a reloption text array as constructed by transformRelOptions.
|
||||
* kind specifies the family of options to be processed.
|
||||
*
|
||||
* 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.
|
||||
* 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.
|
||||
*/
|
||||
void
|
||||
parseRelOptions(Datum options, int numkeywords, const char *const * keywords,
|
||||
char **values, bool validate)
|
||||
relopt_value *
|
||||
parseRelOptions(Datum options, bool validate, relopt_kind kind,
|
||||
int *numrelopts)
|
||||
{
|
||||
ArrayType *array;
|
||||
Datum *optiondatums;
|
||||
int noptions;
|
||||
relopt_value *reloptions;
|
||||
int numoptions = 0;
|
||||
int i;
|
||||
int j;
|
||||
|
||||
/* Initialize to "all defaulted" */
|
||||
MemSet(values, 0, numkeywords * sizeof(char *));
|
||||
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)))
|
||||
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++)
|
||||
if (PointerIsValid(DatumGetPointer(options)))
|
||||
{
|
||||
text *optiontext = DatumGetTextP(optiondatums[i]);
|
||||
char *text_str = VARDATA(optiontext);
|
||||
int text_len = VARSIZE(optiontext) - VARHDRSZ;
|
||||
int j;
|
||||
ArrayType *array;
|
||||
Datum *optiondatums;
|
||||
int noptions;
|
||||
|
||||
/* Search for a match in keywords */
|
||||
for (j = 0; j < numkeywords; j++)
|
||||
array = DatumGetArrayTypeP(options);
|
||||
|
||||
Assert(ARR_ELEMTYPE(array) == TEXTOID);
|
||||
|
||||
deconstruct_array(array, TEXTOID, -1, false, 'i',
|
||||
&optiondatums, NULL, &noptions);
|
||||
|
||||
for (i = 0; i < noptions; i++)
|
||||
{
|
||||
int kw_len = strlen(keywords[j]);
|
||||
text *optiontext = DatumGetTextP(optiondatums[i]);
|
||||
char *text_str = VARDATA(optiontext);
|
||||
int text_len = VARSIZE(optiontext) - VARHDRSZ;
|
||||
int j;
|
||||
|
||||
if (text_len > kw_len && text_str[kw_len] == '=' &&
|
||||
pg_strncasecmp(text_str, keywords[j], kw_len) == 0)
|
||||
/* Search for a match in reloptions */
|
||||
for (j = 0; j < numoptions; j++)
|
||||
{
|
||||
char *value;
|
||||
int value_len;
|
||||
int kw_len = reloptions[j].gen->namelen;
|
||||
|
||||
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 (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)));
|
||||
}
|
||||
}
|
||||
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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*numrelopts = numoptions;
|
||||
return reloptions;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse reloptions for anything using StdRdOptions (ie, fillfactor only)
|
||||
* Subroutine for parseRelOptions, to parse and validate a single option's
|
||||
* value
|
||||
*/
|
||||
bytea *
|
||||
default_reloptions(Datum reloptions, bool validate,
|
||||
int minFillfactor, int defaultFillfactor)
|
||||
static void
|
||||
parse_one_reloption(relopt_value *option, char *text_str, int text_len,
|
||||
bool validate)
|
||||
{
|
||||
static const char *const default_keywords[1] = {"fillfactor"};
|
||||
char *values[1];
|
||||
int fillfactor;
|
||||
StdRdOptions *result;
|
||||
char *value;
|
||||
int value_len;
|
||||
bool parsed;
|
||||
bool nofree = false;
|
||||
|
||||
parseRelOptions(reloptions, 1, default_keywords, values, validate);
|
||||
if (option->isset && validate)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("parameter \"%s\" specified more than once",
|
||||
option->gen->name)));
|
||||
|
||||
/*
|
||||
* 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;
|
||||
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';
|
||||
|
||||
if (!parse_int(values[0], &fillfactor, 0, NULL))
|
||||
switch (option->gen->type)
|
||||
{
|
||||
if (validate)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("fillfactor must be an integer: \"%s\"",
|
||||
values[0])));
|
||||
return NULL;
|
||||
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 (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;
|
||||
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).
|
||||
@ -336,9 +787,7 @@ default_reloptions(Datum reloptions, bool validate,
|
||||
bytea *
|
||||
heap_reloptions(char relkind, Datum reloptions, bool validate)
|
||||
{
|
||||
return default_reloptions(reloptions, validate,
|
||||
HEAP_MIN_FILLFACTOR,
|
||||
HEAP_DEFAULT_FILLFACTOR);
|
||||
return default_reloptions(reloptions, validate, RELOPT_KIND_HEAP);
|
||||
}
|
||||
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/access/gin/ginutil.c,v 1.19 2009/01/01 17:23:34 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/access/gin/ginutil.c,v 1.20 2009/01/05 17:14:28 alvherre Exp $
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
@ -317,16 +317,7 @@ ginoptions(PG_FUNCTION_ARGS)
|
||||
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);
|
||||
result = default_reloptions(reloptions, validate, RELOPT_KIND_GIN);
|
||||
if (result)
|
||||
PG_RETURN_BYTEA_P(result);
|
||||
PG_RETURN_NULL();
|
||||
|
@ -8,7 +8,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/access/gist/gistutil.c,v 1.32 2009/01/01 17:23:35 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/access/gist/gistutil.c,v 1.33 2009/01/05 17:14:28 alvherre Exp $
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "postgres.h"
|
||||
@ -670,9 +670,8 @@ gistoptions(PG_FUNCTION_ARGS)
|
||||
bool validate = PG_GETARG_BOOL(1);
|
||||
bytea *result;
|
||||
|
||||
result = default_reloptions(reloptions, validate,
|
||||
GIST_MIN_FILLFACTOR,
|
||||
GIST_DEFAULT_FILLFACTOR);
|
||||
result = default_reloptions(reloptions, validate, RELOPT_KIND_GIST);
|
||||
|
||||
if (result)
|
||||
PG_RETURN_BYTEA_P(result);
|
||||
PG_RETURN_NULL();
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/access/hash/hashutil.c,v 1.58 2009/01/01 17:23:35 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/access/hash/hashutil.c,v 1.59 2009/01/05 17:14:28 alvherre Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -224,9 +224,8 @@ hashoptions(PG_FUNCTION_ARGS)
|
||||
bool validate = PG_GETARG_BOOL(1);
|
||||
bytea *result;
|
||||
|
||||
result = default_reloptions(reloptions, validate,
|
||||
HASH_MIN_FILLFACTOR,
|
||||
HASH_DEFAULT_FILLFACTOR);
|
||||
result = default_reloptions(reloptions, validate, RELOPT_KIND_HASH);
|
||||
|
||||
if (result)
|
||||
PG_RETURN_BYTEA_P(result);
|
||||
PG_RETURN_NULL();
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/access/nbtree/nbtutils.c,v 1.92 2009/01/01 17:23:36 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/access/nbtree/nbtutils.c,v 1.93 2009/01/05 17:14:28 alvherre Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -1402,9 +1402,7 @@ btoptions(PG_FUNCTION_ARGS)
|
||||
bool validate = PG_GETARG_BOOL(1);
|
||||
bytea *result;
|
||||
|
||||
result = default_reloptions(reloptions, validate,
|
||||
BTREE_MIN_FILLFACTOR,
|
||||
BTREE_DEFAULT_FILLFACTOR);
|
||||
result = default_reloptions(reloptions, validate, RELOPT_KIND_BTREE);
|
||||
if (result)
|
||||
PG_RETURN_BYTEA_P(result);
|
||||
PG_RETURN_NULL();
|
||||
|
@ -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.6 2009/01/01 17:23:56 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/include/access/reloptions.h,v 1.7 2009/01/05 17:14:28 alvherre Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -20,21 +20,168 @@
|
||||
|
||||
#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;
|
||||
bool default_isnull;
|
||||
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. Beware of
|
||||
* multiple evaluation of arguments!
|
||||
*
|
||||
* 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) \
|
||||
do { \
|
||||
if (HAVE_RELOPTION(optname, option)) \
|
||||
{ \
|
||||
if (option.isset) \
|
||||
var = option.values.int_val; \
|
||||
else \
|
||||
var = ((relopt_int *) option.gen)->default_val; \
|
||||
continue; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define HANDLE_BOOL_RELOPTION(optname, var, option) \
|
||||
do { \
|
||||
if (HAVE_RELOPTION(optname, option)) \
|
||||
{ \
|
||||
if (option.isset) \
|
||||
var = option.values.bool_val; \
|
||||
else \
|
||||
var = ((relopt_bool *) option.gen)->default_val; \
|
||||
continue; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define HANDLE_REAL_RELOPTION(optname, var, option) \
|
||||
do { \
|
||||
if (HAVE_RELOPTION(optname, option)) \
|
||||
{ \
|
||||
if (option.isset) \
|
||||
var = option.values.real_val; \
|
||||
else \
|
||||
var = ((relopt_real *) option.gen)->default_val; \
|
||||
continue; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/* Note that this assumes that the variable is already allocated! */
|
||||
#define HANDLE_STRING_RELOPTION(optname, var, option) \
|
||||
do { \
|
||||
if (HAVE_RELOPTION(optname, option)) \
|
||||
{ \
|
||||
relopt_string *optstring = (relopt_string *) option.gen;\
|
||||
if (optstring->default_isnull) \
|
||||
var[0] = '\0'; \
|
||||
else \
|
||||
strcpy(var, \
|
||||
option.isset ? option.values.string_val : \
|
||||
optstring->default_val); \
|
||||
continue; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
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 void parseRelOptions(Datum options, int numkeywords,
|
||||
const char *const * keywords,
|
||||
char **values, bool validate);
|
||||
extern relopt_value *parseRelOptions(Datum options, bool validate,
|
||||
relopt_kind kind, int *numrelopts);
|
||||
|
||||
extern bytea *default_reloptions(Datum reloptions, bool validate,
|
||||
int minFillfactor, int defaultFillfactor);
|
||||
|
||||
relopt_kind kind);
|
||||
extern bytea *heap_reloptions(char relkind, Datum reloptions, bool validate);
|
||||
|
||||
extern bytea *index_reloptions(RegProcedure amoptions, Datum reloptions,
|
||||
bool validate);
|
||||
bool validate);
|
||||
|
||||
#endif /* RELOPTIONS_H */
|
||||
|
@ -37,7 +37,7 @@
|
||||
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.516 2009/01/01 17:23:56 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.517 2009/01/05 17:14:28 alvherre Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -53,6 +53,6 @@
|
||||
*/
|
||||
|
||||
/* yyyymmddN */
|
||||
#define CATALOG_VERSION_NO 200812301
|
||||
#define CATALOG_VERSION_NO 200901051
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user