aboutsummaryrefslogtreecommitdiff
path: root/src/backend/access/common/reloptions.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/access/common/reloptions.c')
-rw-r--r--src/backend/access/common/reloptions.c326
1 files changed, 326 insertions, 0 deletions
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
new file mode 100644
index 00000000000..8506070f101
--- /dev/null
+++ b/src/backend/access/common/reloptions.c
@@ -0,0 +1,326 @@
+/*-------------------------------------------------------------------------
+ *
+ * reloptions.c
+ * Core support for relation options (pg_class.reloptions)
+ *
+ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $PostgreSQL: pgsql/src/backend/access/common/reloptions.c,v 1.1 2006/07/03 22:45:36 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/reloptions.h"
+#include "catalog/pg_type.h"
+#include "commands/defrem.h"
+#include "utils/array.h"
+#include "utils/builtins.h"
+#include "utils/rel.h"
+
+
+/*
+ * Transform a relation options list (list of DefElem) into the text array
+ * format that is kept in pg_class.reloptions.
+ *
+ * This is used for three cases: CREATE TABLE/INDEX, ALTER TABLE SET, and
+ * ALTER TABLE RESET. In the ALTER cases, oldOptions is the existing
+ * reloptions value (possibly NULL), and we replace or remove entries
+ * as needed.
+ *
+ * If ignoreOids is true, then we should ignore any occurrence of "oids"
+ * in the list (it will be or has been handled by interpretOidsOption()).
+ *
+ * Note that this is not responsible for determining whether the options
+ * are valid.
+ *
+ * Both oldOptions and the result are text arrays (or NULL for "default"),
+ * but we declare them as Datums to avoid including array.h in reloptions.h.
+ */
+Datum
+transformRelOptions(Datum oldOptions, List *defList,
+ bool ignoreOids, bool isReset)
+{
+ Datum result;
+ ArrayBuildState *astate;
+ ListCell *cell;
+
+ /* no change if empty list */
+ if (defList == NIL)
+ return oldOptions;
+
+ /* We build new array using accumArrayResult */
+ astate = NULL;
+
+ /* Copy any oldOptions that aren't to be replaced */
+ if (oldOptions != (Datum) 0)
+ {
+ ArrayType *array = DatumGetArrayTypeP(oldOptions);
+ Datum *oldoptions;
+ int noldoptions;
+ int i;
+
+ Assert(ARR_ELEMTYPE(array) == TEXTOID);
+
+ deconstruct_array(array, TEXTOID, -1, false, 'i',
+ &oldoptions, NULL, &noldoptions);
+
+ for (i = 0; i < noldoptions; i++)
+ {
+ text *oldoption = DatumGetTextP(oldoptions[i]);
+ char *text_str = (char *) VARATT_DATA(oldoption);
+ int text_len = VARATT_SIZE(oldoption) - VARHDRSZ;
+
+ /* Search for a match in defList */
+ foreach(cell, defList)
+ {
+ DefElem *def = lfirst(cell);
+ int kw_len = strlen(def->defname);
+
+ if (text_len > kw_len && text_str[kw_len] == '=' &&
+ pg_strncasecmp(text_str, def->defname, kw_len) == 0)
+ break;
+ }
+ if (!cell)
+ {
+ /* No match, so keep old option */
+ astate = accumArrayResult(astate, oldoptions[i],
+ false, TEXTOID,
+ CurrentMemoryContext);
+ }
+ }
+ }
+
+ /*
+ * If CREATE/SET, add new options to array; if RESET, just check that
+ * the user didn't say RESET (option=val). (Must do this because the
+ * grammar doesn't enforce it.)
+ */
+ foreach(cell, defList)
+ {
+ DefElem *def = lfirst(cell);
+
+ if (isReset)
+ {
+ if (def->arg != NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("RESET must not include values for parameters")));
+ }
+ else
+ {
+ text *t;
+ const char *value;
+ Size len;
+
+ if (ignoreOids && pg_strcasecmp(def->defname, "oids") == 0)
+ continue;
+
+ /*
+ * Flatten the DefElem into a text string like "name=arg".
+ * If we have just "name", assume "name=true" is meant.
+ */
+ if (def->arg != NULL)
+ value = defGetString(def);
+ else
+ value = "true";
+ len = VARHDRSZ + strlen(def->defname) + 1 + strlen(value);
+ /* +1 leaves room for sprintf's trailing null */
+ t = (text *) palloc(len + 1);
+ VARATT_SIZEP(t) = len;
+ sprintf((char *) VARATT_DATA(t), "%s=%s", def->defname, value);
+
+ astate = accumArrayResult(astate, PointerGetDatum(t),
+ false, TEXTOID,
+ CurrentMemoryContext);
+ }
+ }
+
+ if (astate)
+ result = makeArrayResult(astate, CurrentMemoryContext);
+ else
+ result = (Datum) 0;
+
+ return result;
+}
+
+
+/*
+ * 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 (options == (Datum) 0)
+ 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 = (char *) VARATT_DATA(optiontext);
+ int text_len = VARATT_SIZE(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("duplicate parameter \"%s\"",
+ 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 = DatumGetCString(DirectFunctionCall1(textout, 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];
+ int32 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;
+
+ fillfactor = pg_atoi(values[0], sizeof(int32), 0);
+ 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));
+ VARATT_SIZEP(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);
+}
+
+
+/*
+ * Parse options for indexes.
+ *
+ * amoptions Oid of option parser
+ * reloptions options as text[] datum
+ * validate error flag
+ */
+bytea *
+index_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
+{
+ FmgrInfo flinfo;
+ FunctionCallInfoData fcinfo;
+ Datum result;
+
+ Assert(RegProcedureIsValid(amoptions));
+
+ /* Assume function is strict */
+ if (reloptions == (Datum) 0)
+ return NULL;
+
+ /* Can't use OidFunctionCallN because we might get a NULL result */
+ fmgr_info(amoptions, &flinfo);
+
+ InitFunctionCallInfoData(fcinfo, &flinfo, 2, NULL, NULL);
+
+ fcinfo.arg[0] = reloptions;
+ fcinfo.arg[1] = BoolGetDatum(validate);
+ fcinfo.argnull[0] = false;
+ fcinfo.argnull[1] = false;
+
+ result = FunctionCallInvoke(&fcinfo);
+
+ if (fcinfo.isnull || DatumGetPointer(result) == NULL)
+ return NULL;
+
+ return DatumGetByteaP(result);
+}