Header And Logo

PostgreSQL
| The world's most advanced open source database.

reloptions.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * reloptions.c
00004  *    Core support for relation options (pg_class.reloptions)
00005  *
00006  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00007  * Portions Copyright (c) 1994, Regents of the University of California
00008  *
00009  *
00010  * IDENTIFICATION
00011  *    src/backend/access/common/reloptions.c
00012  *
00013  *-------------------------------------------------------------------------
00014  */
00015 
00016 #include "postgres.h"
00017 
00018 #include "access/gist_private.h"
00019 #include "access/hash.h"
00020 #include "access/htup_details.h"
00021 #include "access/nbtree.h"
00022 #include "access/reloptions.h"
00023 #include "access/spgist.h"
00024 #include "catalog/pg_type.h"
00025 #include "commands/defrem.h"
00026 #include "commands/tablespace.h"
00027 #include "nodes/makefuncs.h"
00028 #include "utils/array.h"
00029 #include "utils/attoptcache.h"
00030 #include "utils/builtins.h"
00031 #include "utils/guc.h"
00032 #include "utils/memutils.h"
00033 #include "utils/rel.h"
00034 
00035 /*
00036  * Contents of pg_class.reloptions
00037  *
00038  * To add an option:
00039  *
00040  * (i) decide on a type (integer, real, bool, string), name, default value,
00041  * upper and lower bounds (if applicable); for strings, consider a validation
00042  * routine.
00043  * (ii) add a record below (or use add_<type>_reloption).
00044  * (iii) add it to the appropriate options struct (perhaps StdRdOptions)
00045  * (iv) add it to the appropriate handling routine (perhaps
00046  * default_reloptions)
00047  * (v) don't forget to document the option
00048  *
00049  * Note that we don't handle "oids" in relOpts because it is handled by
00050  * interpretOidsOption().
00051  */
00052 
00053 static relopt_bool boolRelOpts[] =
00054 {
00055     {
00056         {
00057             "autovacuum_enabled",
00058             "Enables autovacuum in this relation",
00059             RELOPT_KIND_HEAP | RELOPT_KIND_TOAST
00060         },
00061         true
00062     },
00063     {
00064         {
00065             "fastupdate",
00066             "Enables \"fast update\" feature for this GIN index",
00067             RELOPT_KIND_GIN
00068         },
00069         true
00070     },
00071     {
00072         {
00073             "security_barrier",
00074             "View acts as a row security barrier",
00075             RELOPT_KIND_VIEW
00076         },
00077         false
00078     },
00079     /* list terminator */
00080     {{NULL}}
00081 };
00082 
00083 static relopt_int intRelOpts[] =
00084 {
00085     {
00086         {
00087             "fillfactor",
00088             "Packs table pages only to this percentage",
00089             RELOPT_KIND_HEAP
00090         },
00091         HEAP_DEFAULT_FILLFACTOR, HEAP_MIN_FILLFACTOR, 100
00092     },
00093     {
00094         {
00095             "fillfactor",
00096             "Packs btree index pages only to this percentage",
00097             RELOPT_KIND_BTREE
00098         },
00099         BTREE_DEFAULT_FILLFACTOR, BTREE_MIN_FILLFACTOR, 100
00100     },
00101     {
00102         {
00103             "fillfactor",
00104             "Packs hash index pages only to this percentage",
00105             RELOPT_KIND_HASH
00106         },
00107         HASH_DEFAULT_FILLFACTOR, HASH_MIN_FILLFACTOR, 100
00108     },
00109     {
00110         {
00111             "fillfactor",
00112             "Packs gist index pages only to this percentage",
00113             RELOPT_KIND_GIST
00114         },
00115         GIST_DEFAULT_FILLFACTOR, GIST_MIN_FILLFACTOR, 100
00116     },
00117     {
00118         {
00119             "fillfactor",
00120             "Packs spgist index pages only to this percentage",
00121             RELOPT_KIND_SPGIST
00122         },
00123         SPGIST_DEFAULT_FILLFACTOR, SPGIST_MIN_FILLFACTOR, 100
00124     },
00125     {
00126         {
00127             "autovacuum_vacuum_threshold",
00128             "Minimum number of tuple updates or deletes prior to vacuum",
00129             RELOPT_KIND_HEAP | RELOPT_KIND_TOAST
00130         },
00131         -1, 0, INT_MAX
00132     },
00133     {
00134         {
00135             "autovacuum_analyze_threshold",
00136             "Minimum number of tuple inserts, updates or deletes prior to analyze",
00137             RELOPT_KIND_HEAP
00138         },
00139         -1, 0, INT_MAX
00140     },
00141     {
00142         {
00143             "autovacuum_vacuum_cost_delay",
00144             "Vacuum cost delay in milliseconds, for autovacuum",
00145             RELOPT_KIND_HEAP | RELOPT_KIND_TOAST
00146         },
00147         -1, 0, 100
00148     },
00149     {
00150         {
00151             "autovacuum_vacuum_cost_limit",
00152             "Vacuum cost amount available before napping, for autovacuum",
00153             RELOPT_KIND_HEAP | RELOPT_KIND_TOAST
00154         },
00155         -1, 1, 10000
00156     },
00157     {
00158         {
00159             "autovacuum_freeze_min_age",
00160             "Minimum age at which VACUUM should freeze a table row, for autovacuum",
00161             RELOPT_KIND_HEAP | RELOPT_KIND_TOAST
00162         },
00163         -1, 0, 1000000000
00164     },
00165     {
00166         {
00167             "autovacuum_freeze_max_age",
00168             "Age at which to autovacuum a table to prevent transaction ID wraparound",
00169             RELOPT_KIND_HEAP | RELOPT_KIND_TOAST
00170         },
00171         -1, 100000000, 2000000000
00172     },
00173     {
00174         {
00175             "autovacuum_freeze_table_age",
00176             "Age at which VACUUM should perform a full table sweep to replace old Xid values with FrozenXID",
00177             RELOPT_KIND_HEAP | RELOPT_KIND_TOAST
00178         }, -1, 0, 2000000000
00179     },
00180     /* list terminator */
00181     {{NULL}}
00182 };
00183 
00184 static relopt_real realRelOpts[] =
00185 {
00186     {
00187         {
00188             "autovacuum_vacuum_scale_factor",
00189             "Number of tuple updates or deletes prior to vacuum as a fraction of reltuples",
00190             RELOPT_KIND_HEAP | RELOPT_KIND_TOAST
00191         },
00192         -1, 0.0, 100.0
00193     },
00194     {
00195         {
00196             "autovacuum_analyze_scale_factor",
00197             "Number of tuple inserts, updates or deletes prior to analyze as a fraction of reltuples",
00198             RELOPT_KIND_HEAP
00199         },
00200         -1, 0.0, 100.0
00201     },
00202     {
00203         {
00204             "seq_page_cost",
00205             "Sets the planner's estimate of the cost of a sequentially fetched disk page.",
00206             RELOPT_KIND_TABLESPACE
00207         },
00208         -1, 0.0, DBL_MAX
00209     },
00210     {
00211         {
00212             "random_page_cost",
00213             "Sets the planner's estimate of the cost of a nonsequentially fetched disk page.",
00214             RELOPT_KIND_TABLESPACE
00215         },
00216         -1, 0.0, DBL_MAX
00217     },
00218     {
00219         {
00220             "n_distinct",
00221             "Sets the planner's estimate of the number of distinct values appearing in a column (excluding child relations).",
00222             RELOPT_KIND_ATTRIBUTE
00223         },
00224         0, -1.0, DBL_MAX
00225     },
00226     {
00227         {
00228             "n_distinct_inherited",
00229             "Sets the planner's estimate of the number of distinct values appearing in a column (including child relations).",
00230             RELOPT_KIND_ATTRIBUTE
00231         },
00232         0, -1.0, DBL_MAX
00233     },
00234     /* list terminator */
00235     {{NULL}}
00236 };
00237 
00238 static relopt_string stringRelOpts[] =
00239 {
00240     {
00241         {
00242             "buffering",
00243             "Enables buffering build for this GiST index",
00244             RELOPT_KIND_GIST
00245         },
00246         4,
00247         false,
00248         gistValidateBufferingOption,
00249         "auto"
00250     },
00251     /* list terminator */
00252     {{NULL}}
00253 };
00254 
00255 static relopt_gen **relOpts = NULL;
00256 static bits32 last_assigned_kind = RELOPT_KIND_LAST_DEFAULT;
00257 
00258 static int  num_custom_options = 0;
00259 static relopt_gen **custom_options = NULL;
00260 static bool need_initialization = true;
00261 
00262 static void initialize_reloptions(void);
00263 static void parse_one_reloption(relopt_value *option, char *text_str,
00264                     int text_len, bool validate);
00265 
00266 /*
00267  * initialize_reloptions
00268  *      initialization routine, must be called before parsing
00269  *
00270  * Initialize the relOpts array and fill each variable's type and name length.
00271  */
00272 static void
00273 initialize_reloptions(void)
00274 {
00275     int         i;
00276     int         j;
00277 
00278     j = 0;
00279     for (i = 0; boolRelOpts[i].gen.name; i++)
00280         j++;
00281     for (i = 0; intRelOpts[i].gen.name; i++)
00282         j++;
00283     for (i = 0; realRelOpts[i].gen.name; i++)
00284         j++;
00285     for (i = 0; stringRelOpts[i].gen.name; i++)
00286         j++;
00287     j += num_custom_options;
00288 
00289     if (relOpts)
00290         pfree(relOpts);
00291     relOpts = MemoryContextAlloc(TopMemoryContext,
00292                                  (j + 1) * sizeof(relopt_gen *));
00293 
00294     j = 0;
00295     for (i = 0; boolRelOpts[i].gen.name; i++)
00296     {
00297         relOpts[j] = &boolRelOpts[i].gen;
00298         relOpts[j]->type = RELOPT_TYPE_BOOL;
00299         relOpts[j]->namelen = strlen(relOpts[j]->name);
00300         j++;
00301     }
00302 
00303     for (i = 0; intRelOpts[i].gen.name; i++)
00304     {
00305         relOpts[j] = &intRelOpts[i].gen;
00306         relOpts[j]->type = RELOPT_TYPE_INT;
00307         relOpts[j]->namelen = strlen(relOpts[j]->name);
00308         j++;
00309     }
00310 
00311     for (i = 0; realRelOpts[i].gen.name; i++)
00312     {
00313         relOpts[j] = &realRelOpts[i].gen;
00314         relOpts[j]->type = RELOPT_TYPE_REAL;
00315         relOpts[j]->namelen = strlen(relOpts[j]->name);
00316         j++;
00317     }
00318 
00319     for (i = 0; stringRelOpts[i].gen.name; i++)
00320     {
00321         relOpts[j] = &stringRelOpts[i].gen;
00322         relOpts[j]->type = RELOPT_TYPE_STRING;
00323         relOpts[j]->namelen = strlen(relOpts[j]->name);
00324         j++;
00325     }
00326 
00327     for (i = 0; i < num_custom_options; i++)
00328     {
00329         relOpts[j] = custom_options[i];
00330         j++;
00331     }
00332 
00333     /* add a list terminator */
00334     relOpts[j] = NULL;
00335 
00336     /* flag the work is complete */
00337     need_initialization = false;
00338 }
00339 
00340 /*
00341  * add_reloption_kind
00342  *      Create a new relopt_kind value, to be used in custom reloptions by
00343  *      user-defined AMs.
00344  */
00345 relopt_kind
00346 add_reloption_kind(void)
00347 {
00348     /* don't hand out the last bit so that the enum's behavior is portable */
00349     if (last_assigned_kind >= RELOPT_KIND_MAX)
00350         ereport(ERROR,
00351                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
00352             errmsg("user-defined relation parameter types limit exceeded")));
00353     last_assigned_kind <<= 1;
00354     return (relopt_kind) last_assigned_kind;
00355 }
00356 
00357 /*
00358  * add_reloption
00359  *      Add an already-created custom reloption to the list, and recompute the
00360  *      main parser table.
00361  */
00362 static void
00363 add_reloption(relopt_gen *newoption)
00364 {
00365     static int  max_custom_options = 0;
00366 
00367     if (num_custom_options >= max_custom_options)
00368     {
00369         MemoryContext oldcxt;
00370 
00371         oldcxt = MemoryContextSwitchTo(TopMemoryContext);
00372 
00373         if (max_custom_options == 0)
00374         {
00375             max_custom_options = 8;
00376             custom_options = palloc(max_custom_options * sizeof(relopt_gen *));
00377         }
00378         else
00379         {
00380             max_custom_options *= 2;
00381             custom_options = repalloc(custom_options,
00382                                   max_custom_options * sizeof(relopt_gen *));
00383         }
00384         MemoryContextSwitchTo(oldcxt);
00385     }
00386     custom_options[num_custom_options++] = newoption;
00387 
00388     need_initialization = true;
00389 }
00390 
00391 /*
00392  * allocate_reloption
00393  *      Allocate a new reloption and initialize the type-agnostic fields
00394  *      (for types other than string)
00395  */
00396 static relopt_gen *
00397 allocate_reloption(bits32 kinds, int type, char *name, char *desc)
00398 {
00399     MemoryContext oldcxt;
00400     size_t      size;
00401     relopt_gen *newoption;
00402 
00403     oldcxt = MemoryContextSwitchTo(TopMemoryContext);
00404 
00405     switch (type)
00406     {
00407         case RELOPT_TYPE_BOOL:
00408             size = sizeof(relopt_bool);
00409             break;
00410         case RELOPT_TYPE_INT:
00411             size = sizeof(relopt_int);
00412             break;
00413         case RELOPT_TYPE_REAL:
00414             size = sizeof(relopt_real);
00415             break;
00416         case RELOPT_TYPE_STRING:
00417             size = sizeof(relopt_string);
00418             break;
00419         default:
00420             elog(ERROR, "unsupported option type");
00421             return NULL;        /* keep compiler quiet */
00422     }
00423 
00424     newoption = palloc(size);
00425 
00426     newoption->name = pstrdup(name);
00427     if (desc)
00428         newoption->desc = pstrdup(desc);
00429     else
00430         newoption->desc = NULL;
00431     newoption->kinds = kinds;
00432     newoption->namelen = strlen(name);
00433     newoption->type = type;
00434 
00435     MemoryContextSwitchTo(oldcxt);
00436 
00437     return newoption;
00438 }
00439 
00440 /*
00441  * add_bool_reloption
00442  *      Add a new boolean reloption
00443  */
00444 void
00445 add_bool_reloption(bits32 kinds, char *name, char *desc, bool default_val)
00446 {
00447     relopt_bool *newoption;
00448 
00449     newoption = (relopt_bool *) allocate_reloption(kinds, RELOPT_TYPE_BOOL,
00450                                                    name, desc);
00451     newoption->default_val = default_val;
00452 
00453     add_reloption((relopt_gen *) newoption);
00454 }
00455 
00456 /*
00457  * add_int_reloption
00458  *      Add a new integer reloption
00459  */
00460 void
00461 add_int_reloption(bits32 kinds, char *name, char *desc, int default_val,
00462                   int min_val, int max_val)
00463 {
00464     relopt_int *newoption;
00465 
00466     newoption = (relopt_int *) allocate_reloption(kinds, RELOPT_TYPE_INT,
00467                                                   name, desc);
00468     newoption->default_val = default_val;
00469     newoption->min = min_val;
00470     newoption->max = max_val;
00471 
00472     add_reloption((relopt_gen *) newoption);
00473 }
00474 
00475 /*
00476  * add_real_reloption
00477  *      Add a new float reloption
00478  */
00479 void
00480 add_real_reloption(bits32 kinds, char *name, char *desc, double default_val,
00481                    double min_val, double max_val)
00482 {
00483     relopt_real *newoption;
00484 
00485     newoption = (relopt_real *) allocate_reloption(kinds, RELOPT_TYPE_REAL,
00486                                                    name, desc);
00487     newoption->default_val = default_val;
00488     newoption->min = min_val;
00489     newoption->max = max_val;
00490 
00491     add_reloption((relopt_gen *) newoption);
00492 }
00493 
00494 /*
00495  * add_string_reloption
00496  *      Add a new string reloption
00497  *
00498  * "validator" is an optional function pointer that can be used to test the
00499  * validity of the values.  It must elog(ERROR) when the argument string is
00500  * not acceptable for the variable.  Note that the default value must pass
00501  * the validation.
00502  */
00503 void
00504 add_string_reloption(bits32 kinds, char *name, char *desc, char *default_val,
00505                      validate_string_relopt validator)
00506 {
00507     relopt_string *newoption;
00508 
00509     /* make sure the validator/default combination is sane */
00510     if (validator)
00511         (validator) (default_val);
00512 
00513     newoption = (relopt_string *) allocate_reloption(kinds, RELOPT_TYPE_STRING,
00514                                                      name, desc);
00515     newoption->validate_cb = validator;
00516     if (default_val)
00517     {
00518         newoption->default_val = MemoryContextStrdup(TopMemoryContext,
00519                                                      default_val);
00520         newoption->default_len = strlen(default_val);
00521         newoption->default_isnull = false;
00522     }
00523     else
00524     {
00525         newoption->default_val = "";
00526         newoption->default_len = 0;
00527         newoption->default_isnull = true;
00528     }
00529 
00530     add_reloption((relopt_gen *) newoption);
00531 }
00532 
00533 /*
00534  * Transform a relation options list (list of DefElem) into the text array
00535  * format that is kept in pg_class.reloptions, including only those options
00536  * that are in the passed namespace.  The output values do not include the
00537  * namespace.
00538  *
00539  * This is used for three cases: CREATE TABLE/INDEX, ALTER TABLE SET, and
00540  * ALTER TABLE RESET.  In the ALTER cases, oldOptions is the existing
00541  * reloptions value (possibly NULL), and we replace or remove entries
00542  * as needed.
00543  *
00544  * If ignoreOids is true, then we should ignore any occurrence of "oids"
00545  * in the list (it will be or has been handled by interpretOidsOption()).
00546  *
00547  * Note that this is not responsible for determining whether the options
00548  * are valid, but it does check that namespaces for all the options given are
00549  * listed in validnsps.  The NULL namespace is always valid and need not be
00550  * explicitly listed.  Passing a NULL pointer means that only the NULL
00551  * namespace is valid.
00552  *
00553  * Both oldOptions and the result are text arrays (or NULL for "default"),
00554  * but we declare them as Datums to avoid including array.h in reloptions.h.
00555  */
00556 Datum
00557 transformRelOptions(Datum oldOptions, List *defList, char *namspace,
00558                     char *validnsps[], bool ignoreOids, bool isReset)
00559 {
00560     Datum       result;
00561     ArrayBuildState *astate;
00562     ListCell   *cell;
00563 
00564     /* no change if empty list */
00565     if (defList == NIL)
00566         return oldOptions;
00567 
00568     /* We build new array using accumArrayResult */
00569     astate = NULL;
00570 
00571     /* Copy any oldOptions that aren't to be replaced */
00572     if (PointerIsValid(DatumGetPointer(oldOptions)))
00573     {
00574         ArrayType  *array = DatumGetArrayTypeP(oldOptions);
00575         Datum      *oldoptions;
00576         int         noldoptions;
00577         int         i;
00578 
00579         Assert(ARR_ELEMTYPE(array) == TEXTOID);
00580 
00581         deconstruct_array(array, TEXTOID, -1, false, 'i',
00582                           &oldoptions, NULL, &noldoptions);
00583 
00584         for (i = 0; i < noldoptions; i++)
00585         {
00586             text       *oldoption = DatumGetTextP(oldoptions[i]);
00587             char       *text_str = VARDATA(oldoption);
00588             int         text_len = VARSIZE(oldoption) - VARHDRSZ;
00589 
00590             /* Search for a match in defList */
00591             foreach(cell, defList)
00592             {
00593                 DefElem    *def = (DefElem *) lfirst(cell);
00594                 int         kw_len;
00595 
00596                 /* ignore if not in the same namespace */
00597                 if (namspace == NULL)
00598                 {
00599                     if (def->defnamespace != NULL)
00600                         continue;
00601                 }
00602                 else if (def->defnamespace == NULL)
00603                     continue;
00604                 else if (pg_strcasecmp(def->defnamespace, namspace) != 0)
00605                     continue;
00606 
00607                 kw_len = strlen(def->defname);
00608                 if (text_len > kw_len && text_str[kw_len] == '=' &&
00609                     pg_strncasecmp(text_str, def->defname, kw_len) == 0)
00610                     break;
00611             }
00612             if (!cell)
00613             {
00614                 /* No match, so keep old option */
00615                 astate = accumArrayResult(astate, oldoptions[i],
00616                                           false, TEXTOID,
00617                                           CurrentMemoryContext);
00618             }
00619         }
00620     }
00621 
00622     /*
00623      * If CREATE/SET, add new options to array; if RESET, just check that the
00624      * user didn't say RESET (option=val).  (Must do this because the grammar
00625      * doesn't enforce it.)
00626      */
00627     foreach(cell, defList)
00628     {
00629         DefElem    *def = (DefElem *) lfirst(cell);
00630 
00631         if (isReset)
00632         {
00633             if (def->arg != NULL)
00634                 ereport(ERROR,
00635                         (errcode(ERRCODE_SYNTAX_ERROR),
00636                     errmsg("RESET must not include values for parameters")));
00637         }
00638         else
00639         {
00640             text       *t;
00641             const char *value;
00642             Size        len;
00643 
00644             /*
00645              * Error out if the namespace is not valid.  A NULL namespace is
00646              * always valid.
00647              */
00648             if (def->defnamespace != NULL)
00649             {
00650                 bool        valid = false;
00651                 int         i;
00652 
00653                 if (validnsps)
00654                 {
00655                     for (i = 0; validnsps[i]; i++)
00656                     {
00657                         if (pg_strcasecmp(def->defnamespace,
00658                                           validnsps[i]) == 0)
00659                         {
00660                             valid = true;
00661                             break;
00662                         }
00663                     }
00664                 }
00665 
00666                 if (!valid)
00667                     ereport(ERROR,
00668                             (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
00669                              errmsg("unrecognized parameter namespace \"%s\"",
00670                                     def->defnamespace)));
00671             }
00672 
00673             if (ignoreOids && pg_strcasecmp(def->defname, "oids") == 0)
00674                 continue;
00675 
00676             /* ignore if not in the same namespace */
00677             if (namspace == NULL)
00678             {
00679                 if (def->defnamespace != NULL)
00680                     continue;
00681             }
00682             else if (def->defnamespace == NULL)
00683                 continue;
00684             else if (pg_strcasecmp(def->defnamespace, namspace) != 0)
00685                 continue;
00686 
00687             /*
00688              * Flatten the DefElem into a text string like "name=arg". If we
00689              * have just "name", assume "name=true" is meant.  Note: the
00690              * namespace is not output.
00691              */
00692             if (def->arg != NULL)
00693                 value = defGetString(def);
00694             else
00695                 value = "true";
00696             len = VARHDRSZ + strlen(def->defname) + 1 + strlen(value);
00697             /* +1 leaves room for sprintf's trailing null */
00698             t = (text *) palloc(len + 1);
00699             SET_VARSIZE(t, len);
00700             sprintf(VARDATA(t), "%s=%s", def->defname, value);
00701 
00702             astate = accumArrayResult(astate, PointerGetDatum(t),
00703                                       false, TEXTOID,
00704                                       CurrentMemoryContext);
00705         }
00706     }
00707 
00708     if (astate)
00709         result = makeArrayResult(astate, CurrentMemoryContext);
00710     else
00711         result = (Datum) 0;
00712 
00713     return result;
00714 }
00715 
00716 
00717 /*
00718  * Convert the text-array format of reloptions into a List of DefElem.
00719  * This is the inverse of transformRelOptions().
00720  */
00721 List *
00722 untransformRelOptions(Datum options)
00723 {
00724     List       *result = NIL;
00725     ArrayType  *array;
00726     Datum      *optiondatums;
00727     int         noptions;
00728     int         i;
00729 
00730     /* Nothing to do if no options */
00731     if (!PointerIsValid(DatumGetPointer(options)))
00732         return result;
00733 
00734     array = DatumGetArrayTypeP(options);
00735 
00736     Assert(ARR_ELEMTYPE(array) == TEXTOID);
00737 
00738     deconstruct_array(array, TEXTOID, -1, false, 'i',
00739                       &optiondatums, NULL, &noptions);
00740 
00741     for (i = 0; i < noptions; i++)
00742     {
00743         char       *s;
00744         char       *p;
00745         Node       *val = NULL;
00746 
00747         s = TextDatumGetCString(optiondatums[i]);
00748         p = strchr(s, '=');
00749         if (p)
00750         {
00751             *p++ = '\0';
00752             val = (Node *) makeString(pstrdup(p));
00753         }
00754         result = lappend(result, makeDefElem(pstrdup(s), val));
00755     }
00756 
00757     return result;
00758 }
00759 
00760 /*
00761  * Extract and parse reloptions from a pg_class tuple.
00762  *
00763  * This is a low-level routine, expected to be used by relcache code and
00764  * callers that do not have a table's relcache entry (e.g. autovacuum).  For
00765  * other uses, consider grabbing the rd_options pointer from the relcache entry
00766  * instead.
00767  *
00768  * tupdesc is pg_class' tuple descriptor.  amoptions is the amoptions regproc
00769  * in the case of the tuple corresponding to an index, or InvalidOid otherwise.
00770  */
00771 bytea *
00772 extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, Oid amoptions)
00773 {
00774     bytea      *options;
00775     bool        isnull;
00776     Datum       datum;
00777     Form_pg_class classForm;
00778 
00779     datum = fastgetattr(tuple,
00780                         Anum_pg_class_reloptions,
00781                         tupdesc,
00782                         &isnull);
00783     if (isnull)
00784         return NULL;
00785 
00786     classForm = (Form_pg_class) GETSTRUCT(tuple);
00787 
00788     /* Parse into appropriate format; don't error out here */
00789     switch (classForm->relkind)
00790     {
00791         case RELKIND_RELATION:
00792         case RELKIND_TOASTVALUE:
00793         case RELKIND_VIEW:
00794         case RELKIND_MATVIEW:
00795             options = heap_reloptions(classForm->relkind, datum, false);
00796             break;
00797         case RELKIND_INDEX:
00798             options = index_reloptions(amoptions, datum, false);
00799             break;
00800         case RELKIND_FOREIGN_TABLE:
00801             options = NULL;
00802             break;
00803         default:
00804             Assert(false);      /* can't get here */
00805             options = NULL;     /* keep compiler quiet */
00806             break;
00807     }
00808 
00809     return options;
00810 }
00811 
00812 /*
00813  * Interpret reloptions that are given in text-array format.
00814  *
00815  * options is a reloption text array as constructed by transformRelOptions.
00816  * kind specifies the family of options to be processed.
00817  *
00818  * The return value is a relopt_value * array on which the options actually
00819  * set in the options array are marked with isset=true.  The length of this
00820  * array is returned in *numrelopts.  Options not set are also present in the
00821  * array; this is so that the caller can easily locate the default values.
00822  *
00823  * If there are no options of the given kind, numrelopts is set to 0 and NULL
00824  * is returned.
00825  *
00826  * Note: values of type int, bool and real are allocated as part of the
00827  * returned array.  Values of type string are allocated separately and must
00828  * be freed by the caller.
00829  */
00830 relopt_value *
00831 parseRelOptions(Datum options, bool validate, relopt_kind kind,
00832                 int *numrelopts)
00833 {
00834     relopt_value *reloptions;
00835     int         numoptions = 0;
00836     int         i;
00837     int         j;
00838 
00839     if (need_initialization)
00840         initialize_reloptions();
00841 
00842     /* Build a list of expected options, based on kind */
00843 
00844     for (i = 0; relOpts[i]; i++)
00845         if (relOpts[i]->kinds & kind)
00846             numoptions++;
00847 
00848     if (numoptions == 0)
00849     {
00850         *numrelopts = 0;
00851         return NULL;
00852     }
00853 
00854     reloptions = palloc(numoptions * sizeof(relopt_value));
00855 
00856     for (i = 0, j = 0; relOpts[i]; i++)
00857     {
00858         if (relOpts[i]->kinds & kind)
00859         {
00860             reloptions[j].gen = relOpts[i];
00861             reloptions[j].isset = false;
00862             j++;
00863         }
00864     }
00865 
00866     /* Done if no options */
00867     if (PointerIsValid(DatumGetPointer(options)))
00868     {
00869         ArrayType  *array;
00870         Datum      *optiondatums;
00871         int         noptions;
00872 
00873         array = DatumGetArrayTypeP(options);
00874 
00875         Assert(ARR_ELEMTYPE(array) == TEXTOID);
00876 
00877         deconstruct_array(array, TEXTOID, -1, false, 'i',
00878                           &optiondatums, NULL, &noptions);
00879 
00880         for (i = 0; i < noptions; i++)
00881         {
00882             text       *optiontext = DatumGetTextP(optiondatums[i]);
00883             char       *text_str = VARDATA(optiontext);
00884             int         text_len = VARSIZE(optiontext) - VARHDRSZ;
00885             int         j;
00886 
00887             /* Search for a match in reloptions */
00888             for (j = 0; j < numoptions; j++)
00889             {
00890                 int         kw_len = reloptions[j].gen->namelen;
00891 
00892                 if (text_len > kw_len && text_str[kw_len] == '=' &&
00893                     pg_strncasecmp(text_str, reloptions[j].gen->name,
00894                                    kw_len) == 0)
00895                 {
00896                     parse_one_reloption(&reloptions[j], text_str, text_len,
00897                                         validate);
00898                     break;
00899                 }
00900             }
00901 
00902             if (j >= numoptions && validate)
00903             {
00904                 char       *s;
00905                 char       *p;
00906 
00907                 s = TextDatumGetCString(optiondatums[i]);
00908                 p = strchr(s, '=');
00909                 if (p)
00910                     *p = '\0';
00911                 ereport(ERROR,
00912                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
00913                          errmsg("unrecognized parameter \"%s\"", s)));
00914             }
00915         }
00916     }
00917 
00918     *numrelopts = numoptions;
00919     return reloptions;
00920 }
00921 
00922 /*
00923  * Subroutine for parseRelOptions, to parse and validate a single option's
00924  * value
00925  */
00926 static void
00927 parse_one_reloption(relopt_value *option, char *text_str, int text_len,
00928                     bool validate)
00929 {
00930     char       *value;
00931     int         value_len;
00932     bool        parsed;
00933     bool        nofree = false;
00934 
00935     if (option->isset && validate)
00936         ereport(ERROR,
00937                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
00938                  errmsg("parameter \"%s\" specified more than once",
00939                         option->gen->name)));
00940 
00941     value_len = text_len - option->gen->namelen - 1;
00942     value = (char *) palloc(value_len + 1);
00943     memcpy(value, text_str + option->gen->namelen + 1, value_len);
00944     value[value_len] = '\0';
00945 
00946     switch (option->gen->type)
00947     {
00948         case RELOPT_TYPE_BOOL:
00949             {
00950                 parsed = parse_bool(value, &option->values.bool_val);
00951                 if (validate && !parsed)
00952                     ereport(ERROR,
00953                        (errmsg("invalid value for boolean option \"%s\": %s",
00954                                option->gen->name, value)));
00955             }
00956             break;
00957         case RELOPT_TYPE_INT:
00958             {
00959                 relopt_int *optint = (relopt_int *) option->gen;
00960 
00961                 parsed = parse_int(value, &option->values.int_val, 0, NULL);
00962                 if (validate && !parsed)
00963                     ereport(ERROR,
00964                        (errmsg("invalid value for integer option \"%s\": %s",
00965                                option->gen->name, value)));
00966                 if (validate && (option->values.int_val < optint->min ||
00967                                  option->values.int_val > optint->max))
00968                     ereport(ERROR,
00969                           (errmsg("value %s out of bounds for option \"%s\"",
00970                                   value, option->gen->name),
00971                      errdetail("Valid values are between \"%d\" and \"%d\".",
00972                                optint->min, optint->max)));
00973             }
00974             break;
00975         case RELOPT_TYPE_REAL:
00976             {
00977                 relopt_real *optreal = (relopt_real *) option->gen;
00978 
00979                 parsed = parse_real(value, &option->values.real_val);
00980                 if (validate && !parsed)
00981                     ereport(ERROR,
00982                             (errmsg("invalid value for floating point option \"%s\": %s",
00983                                     option->gen->name, value)));
00984                 if (validate && (option->values.real_val < optreal->min ||
00985                                  option->values.real_val > optreal->max))
00986                     ereport(ERROR,
00987                           (errmsg("value %s out of bounds for option \"%s\"",
00988                                   value, option->gen->name),
00989                      errdetail("Valid values are between \"%f\" and \"%f\".",
00990                                optreal->min, optreal->max)));
00991             }
00992             break;
00993         case RELOPT_TYPE_STRING:
00994             {
00995                 relopt_string *optstring = (relopt_string *) option->gen;
00996 
00997                 option->values.string_val = value;
00998                 nofree = true;
00999                 if (validate && optstring->validate_cb)
01000                     (optstring->validate_cb) (value);
01001                 parsed = true;
01002             }
01003             break;
01004         default:
01005             elog(ERROR, "unsupported reloption type %d", option->gen->type);
01006             parsed = true;      /* quiet compiler */
01007             break;
01008     }
01009 
01010     if (parsed)
01011         option->isset = true;
01012     if (!nofree)
01013         pfree(value);
01014 }
01015 
01016 /*
01017  * Given the result from parseRelOptions, allocate a struct that's of the
01018  * specified base size plus any extra space that's needed for string variables.
01019  *
01020  * "base" should be sizeof(struct) of the reloptions struct (StdRdOptions or
01021  * equivalent).
01022  */
01023 void *
01024 allocateReloptStruct(Size base, relopt_value *options, int numoptions)
01025 {
01026     Size        size = base;
01027     int         i;
01028 
01029     for (i = 0; i < numoptions; i++)
01030         if (options[i].gen->type == RELOPT_TYPE_STRING)
01031             size += GET_STRING_RELOPTION_LEN(options[i]) + 1;
01032 
01033     return palloc0(size);
01034 }
01035 
01036 /*
01037  * Given the result of parseRelOptions and a parsing table, fill in the
01038  * struct (previously allocated with allocateReloptStruct) with the parsed
01039  * values.
01040  *
01041  * rdopts is the pointer to the allocated struct to be filled.
01042  * basesize is the sizeof(struct) that was passed to allocateReloptStruct.
01043  * options, of length numoptions, is parseRelOptions' output.
01044  * elems, of length numelems, is the table describing the allowed options.
01045  * When validate is true, it is expected that all options appear in elems.
01046  */
01047 void
01048 fillRelOptions(void *rdopts, Size basesize,
01049                relopt_value *options, int numoptions,
01050                bool validate,
01051                const relopt_parse_elt *elems, int numelems)
01052 {
01053     int         i;
01054     int         offset = basesize;
01055 
01056     for (i = 0; i < numoptions; i++)
01057     {
01058         int         j;
01059         bool        found = false;
01060 
01061         for (j = 0; j < numelems; j++)
01062         {
01063             if (pg_strcasecmp(options[i].gen->name, elems[j].optname) == 0)
01064             {
01065                 relopt_string *optstring;
01066                 char       *itempos = ((char *) rdopts) + elems[j].offset;
01067                 char       *string_val;
01068 
01069                 switch (options[i].gen->type)
01070                 {
01071                     case RELOPT_TYPE_BOOL:
01072                         *(bool *) itempos = options[i].isset ?
01073                             options[i].values.bool_val :
01074                             ((relopt_bool *) options[i].gen)->default_val;
01075                         break;
01076                     case RELOPT_TYPE_INT:
01077                         *(int *) itempos = options[i].isset ?
01078                             options[i].values.int_val :
01079                             ((relopt_int *) options[i].gen)->default_val;
01080                         break;
01081                     case RELOPT_TYPE_REAL:
01082                         *(double *) itempos = options[i].isset ?
01083                             options[i].values.real_val :
01084                             ((relopt_real *) options[i].gen)->default_val;
01085                         break;
01086                     case RELOPT_TYPE_STRING:
01087                         optstring = (relopt_string *) options[i].gen;
01088                         if (options[i].isset)
01089                             string_val = options[i].values.string_val;
01090                         else if (!optstring->default_isnull)
01091                             string_val = optstring->default_val;
01092                         else
01093                             string_val = NULL;
01094 
01095                         if (string_val == NULL)
01096                             *(int *) itempos = 0;
01097                         else
01098                         {
01099                             strcpy((char *) rdopts + offset, string_val);
01100                             *(int *) itempos = offset;
01101                             offset += strlen(string_val) + 1;
01102                         }
01103                         break;
01104                     default:
01105                         elog(ERROR, "unrecognized reloption type %c",
01106                              options[i].gen->type);
01107                         break;
01108                 }
01109                 found = true;
01110                 break;
01111             }
01112         }
01113         if (validate && !found)
01114             elog(ERROR, "reloption \"%s\" not found in parse table",
01115                  options[i].gen->name);
01116     }
01117     SET_VARSIZE(rdopts, offset);
01118 }
01119 
01120 
01121 /*
01122  * Option parser for anything that uses StdRdOptions (i.e. fillfactor and
01123  * autovacuum)
01124  */
01125 bytea *
01126 default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
01127 {
01128     relopt_value *options;
01129     StdRdOptions *rdopts;
01130     int         numoptions;
01131     static const relopt_parse_elt tab[] = {
01132         {"fillfactor", RELOPT_TYPE_INT, offsetof(StdRdOptions, fillfactor)},
01133         {"autovacuum_enabled", RELOPT_TYPE_BOOL,
01134         offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, enabled)},
01135         {"autovacuum_vacuum_threshold", RELOPT_TYPE_INT,
01136         offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, vacuum_threshold)},
01137         {"autovacuum_analyze_threshold", RELOPT_TYPE_INT,
01138         offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, analyze_threshold)},
01139         {"autovacuum_vacuum_cost_delay", RELOPT_TYPE_INT,
01140         offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, vacuum_cost_delay)},
01141         {"autovacuum_vacuum_cost_limit", RELOPT_TYPE_INT,
01142         offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, vacuum_cost_limit)},
01143         {"autovacuum_freeze_min_age", RELOPT_TYPE_INT,
01144         offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, freeze_min_age)},
01145         {"autovacuum_freeze_max_age", RELOPT_TYPE_INT,
01146         offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, freeze_max_age)},
01147         {"autovacuum_freeze_table_age", RELOPT_TYPE_INT,
01148         offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, freeze_table_age)},
01149         {"autovacuum_vacuum_scale_factor", RELOPT_TYPE_REAL,
01150         offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, vacuum_scale_factor)},
01151         {"autovacuum_analyze_scale_factor", RELOPT_TYPE_REAL,
01152         offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, analyze_scale_factor)},
01153         {"security_barrier", RELOPT_TYPE_BOOL,
01154         offsetof(StdRdOptions, security_barrier)},
01155     };
01156 
01157     options = parseRelOptions(reloptions, validate, kind, &numoptions);
01158 
01159     /* if none set, we're done */
01160     if (numoptions == 0)
01161         return NULL;
01162 
01163     rdopts = allocateReloptStruct(sizeof(StdRdOptions), options, numoptions);
01164 
01165     fillRelOptions((void *) rdopts, sizeof(StdRdOptions), options, numoptions,
01166                    validate, tab, lengthof(tab));
01167 
01168     pfree(options);
01169 
01170     return (bytea *) rdopts;
01171 }
01172 
01173 /*
01174  * Parse options for heaps, views and toast tables.
01175  */
01176 bytea *
01177 heap_reloptions(char relkind, Datum reloptions, bool validate)
01178 {
01179     StdRdOptions *rdopts;
01180 
01181     switch (relkind)
01182     {
01183         case RELKIND_TOASTVALUE:
01184             rdopts = (StdRdOptions *)
01185                 default_reloptions(reloptions, validate, RELOPT_KIND_TOAST);
01186             if (rdopts != NULL)
01187             {
01188                 /* adjust default-only parameters for TOAST relations */
01189                 rdopts->fillfactor = 100;
01190                 rdopts->autovacuum.analyze_threshold = -1;
01191                 rdopts->autovacuum.analyze_scale_factor = -1;
01192             }
01193             return (bytea *) rdopts;
01194         case RELKIND_RELATION:
01195         case RELKIND_MATVIEW:
01196             return default_reloptions(reloptions, validate, RELOPT_KIND_HEAP);
01197         case RELKIND_VIEW:
01198             return default_reloptions(reloptions, validate, RELOPT_KIND_VIEW);
01199         default:
01200             /* other relkinds are not supported */
01201             return NULL;
01202     }
01203 }
01204 
01205 
01206 /*
01207  * Parse options for indexes.
01208  *
01209  *  amoptions   Oid of option parser
01210  *  reloptions  options as text[] datum
01211  *  validate    error flag
01212  */
01213 bytea *
01214 index_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
01215 {
01216     FmgrInfo    flinfo;
01217     FunctionCallInfoData fcinfo;
01218     Datum       result;
01219 
01220     Assert(RegProcedureIsValid(amoptions));
01221 
01222     /* Assume function is strict */
01223     if (!PointerIsValid(DatumGetPointer(reloptions)))
01224         return NULL;
01225 
01226     /* Can't use OidFunctionCallN because we might get a NULL result */
01227     fmgr_info(amoptions, &flinfo);
01228 
01229     InitFunctionCallInfoData(fcinfo, &flinfo, 2, InvalidOid, NULL, NULL);
01230 
01231     fcinfo.arg[0] = reloptions;
01232     fcinfo.arg[1] = BoolGetDatum(validate);
01233     fcinfo.argnull[0] = false;
01234     fcinfo.argnull[1] = false;
01235 
01236     result = FunctionCallInvoke(&fcinfo);
01237 
01238     if (fcinfo.isnull || DatumGetPointer(result) == NULL)
01239         return NULL;
01240 
01241     return DatumGetByteaP(result);
01242 }
01243 
01244 /*
01245  * Option parser for attribute reloptions
01246  */
01247 bytea *
01248 attribute_reloptions(Datum reloptions, bool validate)
01249 {
01250     relopt_value *options;
01251     AttributeOpts *aopts;
01252     int         numoptions;
01253     static const relopt_parse_elt tab[] = {
01254         {"n_distinct", RELOPT_TYPE_REAL, offsetof(AttributeOpts, n_distinct)},
01255         {"n_distinct_inherited", RELOPT_TYPE_REAL, offsetof(AttributeOpts, n_distinct_inherited)}
01256     };
01257 
01258     options = parseRelOptions(reloptions, validate, RELOPT_KIND_ATTRIBUTE,
01259                               &numoptions);
01260 
01261     /* if none set, we're done */
01262     if (numoptions == 0)
01263         return NULL;
01264 
01265     aopts = allocateReloptStruct(sizeof(AttributeOpts), options, numoptions);
01266 
01267     fillRelOptions((void *) aopts, sizeof(AttributeOpts), options, numoptions,
01268                    validate, tab, lengthof(tab));
01269 
01270     pfree(options);
01271 
01272     return (bytea *) aopts;
01273 }
01274 
01275 /*
01276  * Option parser for tablespace reloptions
01277  */
01278 bytea *
01279 tablespace_reloptions(Datum reloptions, bool validate)
01280 {
01281     relopt_value *options;
01282     TableSpaceOpts *tsopts;
01283     int         numoptions;
01284     static const relopt_parse_elt tab[] = {
01285         {"random_page_cost", RELOPT_TYPE_REAL, offsetof(TableSpaceOpts, random_page_cost)},
01286         {"seq_page_cost", RELOPT_TYPE_REAL, offsetof(TableSpaceOpts, seq_page_cost)}
01287     };
01288 
01289     options = parseRelOptions(reloptions, validate, RELOPT_KIND_TABLESPACE,
01290                               &numoptions);
01291 
01292     /* if none set, we're done */
01293     if (numoptions == 0)
01294         return NULL;
01295 
01296     tsopts = allocateReloptStruct(sizeof(TableSpaceOpts), options, numoptions);
01297 
01298     fillRelOptions((void *) tsopts, sizeof(TableSpaceOpts), options, numoptions,
01299                    validate, tab, lengthof(tab));
01300 
01301     pfree(options);
01302 
01303     return (bytea *) tsopts;
01304 }