Update autovacuum to use reloptions instead of a system catalog, for
[PostgreSQL.git] / src / backend / access / common / reloptions.c
blob4b7cddac5972241075ac73856c99c683c5aa84a2
1 /*-------------------------------------------------------------------------
3 * reloptions.c
4 * Core support for relation options (pg_class.reloptions)
6 * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
10 * IDENTIFICATION
11 * $PostgreSQL$
13 *-------------------------------------------------------------------------
16 #include "postgres.h"
18 #include "access/gist_private.h"
19 #include "access/hash.h"
20 #include "access/nbtree.h"
21 #include "access/reloptions.h"
22 #include "catalog/pg_type.h"
23 #include "commands/defrem.h"
24 #include "nodes/makefuncs.h"
25 #include "utils/array.h"
26 #include "utils/builtins.h"
27 #include "utils/guc.h"
28 #include "utils/memutils.h"
29 #include "utils/rel.h"
32 * Contents of pg_class.reloptions
34 * To add an option:
36 * (i) decide on a type (integer, real, bool, string), name, default value,
37 * upper and lower bounds (if applicable); for strings, consider a validation
38 * routine.
39 * (ii) add a record below (or use add_<type>_reloption).
40 * (iii) add it to the appropriate options struct (perhaps StdRdOptions)
41 * (iv) add it to the appropriate handling routine (perhaps
42 * default_reloptions)
43 * (v) don't forget to document the option
45 * Note that we don't handle "oids" in relOpts because it is handled by
46 * interpretOidsOption().
49 static relopt_bool boolRelOpts[] =
53 "autovacuum_enabled",
54 "Enables autovacuum in this relation",
55 RELOPT_KIND_HEAP
57 true
59 /* list terminator */
60 { { NULL } }
63 static relopt_int intRelOpts[] =
67 "fillfactor",
68 "Packs table pages only to this percentage",
69 RELOPT_KIND_HEAP
71 HEAP_DEFAULT_FILLFACTOR, HEAP_MIN_FILLFACTOR, 100
75 "fillfactor",
76 "Packs btree index pages only to this percentage",
77 RELOPT_KIND_BTREE
79 BTREE_DEFAULT_FILLFACTOR, BTREE_MIN_FILLFACTOR, 100
83 "fillfactor",
84 "Packs hash index pages only to this percentage",
85 RELOPT_KIND_HASH
87 HASH_DEFAULT_FILLFACTOR, HASH_MIN_FILLFACTOR, 100
91 "fillfactor",
92 "Packs gist index pages only to this percentage",
93 RELOPT_KIND_GIST
95 GIST_DEFAULT_FILLFACTOR, GIST_MIN_FILLFACTOR, 100
99 "autovacuum_vacuum_threshold",
100 "Minimum number of tuple updates or deletes prior to vacuum",
101 RELOPT_KIND_HEAP
103 50, 0, INT_MAX
107 "autovacuum_analyze_threshold",
108 "Minimum number of tuple inserts, updates or deletes prior to analyze",
109 RELOPT_KIND_HEAP
111 50, 0, INT_MAX
115 "autovacuum_vacuum_cost_delay",
116 "Vacuum cost delay in milliseconds, for autovacuum",
117 RELOPT_KIND_HEAP
119 20, 0, 1000
123 "autovacuum_vacuum_cost_limit",
124 "Vacuum cost amount available before napping, for autovacuum",
125 RELOPT_KIND_HEAP
127 200, 1, 10000
131 "autovacuum_freeze_min_age",
132 "Minimum age at which VACUUM should freeze a table row, for autovacuum",
133 RELOPT_KIND_HEAP
135 100000000, 0, 1000000000
139 "autovacuum_freeze_max_age",
140 "Age at which to autovacuum a table to prevent transaction ID wraparound",
141 RELOPT_KIND_HEAP
143 200000000, 100000000, 2000000000
147 "autovacuum_freeze_table_age",
148 "Age at which VACUUM should perform a full table sweep to replace old Xid values with FrozenXID",
149 RELOPT_KIND_HEAP
150 }, 150000000, 0, 2000000000
152 /* list terminator */
153 { { NULL } }
156 static relopt_real realRelOpts[] =
160 "autovacuum_vacuum_scale_factor",
161 "Number of tuple updates or deletes prior to vacuum as a fraction of reltuples",
162 RELOPT_KIND_HEAP
164 0.2, 0.0, 100.0
168 "autovacuum_analyze_scale_factor",
169 "Number of tuple inserts, updates or deletes prior to analyze as a fraction of reltuples",
170 RELOPT_KIND_HEAP
172 0.1, 0.0, 100.0
174 /* list terminator */
175 { { NULL } }
178 static relopt_string stringRelOpts[] =
180 /* list terminator */
181 { { NULL } }
184 static relopt_gen **relOpts = NULL;
185 static int last_assigned_kind = RELOPT_KIND_LAST_DEFAULT + 1;
187 static int num_custom_options = 0;
188 static relopt_gen **custom_options = NULL;
189 static bool need_initialization = true;
191 static void initialize_reloptions(void);
192 static void parse_one_reloption(relopt_value *option, char *text_str,
193 int text_len, bool validate);
196 * initialize_reloptions
197 * initialization routine, must be called before parsing
199 * Initialize the relOpts array and fill each variable's type and name length.
201 static void
202 initialize_reloptions(void)
204 int i;
205 int j = 0;
207 for (i = 0; boolRelOpts[i].gen.name; i++)
208 j++;
209 for (i = 0; intRelOpts[i].gen.name; i++)
210 j++;
211 for (i = 0; realRelOpts[i].gen.name; i++)
212 j++;
213 for (i = 0; stringRelOpts[i].gen.name; i++)
214 j++;
215 j += num_custom_options;
217 if (relOpts)
218 pfree(relOpts);
219 relOpts = MemoryContextAlloc(TopMemoryContext,
220 (j + 1) * sizeof(relopt_gen *));
222 j = 0;
223 for (i = 0; boolRelOpts[i].gen.name; i++)
225 relOpts[j] = &boolRelOpts[i].gen;
226 relOpts[j]->type = RELOPT_TYPE_BOOL;
227 relOpts[j]->namelen = strlen(relOpts[j]->name);
228 j++;
231 for (i = 0; intRelOpts[i].gen.name; i++)
233 relOpts[j] = &intRelOpts[i].gen;
234 relOpts[j]->type = RELOPT_TYPE_INT;
235 relOpts[j]->namelen = strlen(relOpts[j]->name);
236 j++;
239 for (i = 0; realRelOpts[i].gen.name; i++)
241 relOpts[j] = &realRelOpts[i].gen;
242 relOpts[j]->type = RELOPT_TYPE_REAL;
243 relOpts[j]->namelen = strlen(relOpts[j]->name);
244 j++;
247 for (i = 0; stringRelOpts[i].gen.name; i++)
249 relOpts[j] = &stringRelOpts[i].gen;
250 relOpts[j]->type = RELOPT_TYPE_STRING;
251 relOpts[j]->namelen = strlen(relOpts[j]->name);
252 j++;
255 for (i = 0; i < num_custom_options; i++)
257 relOpts[j] = custom_options[i];
258 j++;
261 /* add a list terminator */
262 relOpts[j] = NULL;
266 * add_reloption_kind
267 * Create a new relopt_kind value, to be used in custom reloptions by
268 * user-defined AMs.
271 add_reloption_kind(void)
273 if (last_assigned_kind >= RELOPT_KIND_MAX)
274 ereport(ERROR,
275 (errmsg("user-defined relation parameter types limit exceeded")));
277 return last_assigned_kind++;
281 * add_reloption
282 * Add an already-created custom reloption to the list, and recompute the
283 * main parser table.
285 static void
286 add_reloption(relopt_gen *newoption)
288 static int max_custom_options = 0;
290 if (num_custom_options >= max_custom_options)
292 MemoryContext oldcxt;
294 oldcxt = MemoryContextSwitchTo(TopMemoryContext);
296 if (max_custom_options == 0)
298 max_custom_options = 8;
299 custom_options = palloc(max_custom_options * sizeof(relopt_gen *));
301 else
303 max_custom_options *= 2;
304 custom_options = repalloc(custom_options,
305 max_custom_options * sizeof(relopt_gen *));
307 MemoryContextSwitchTo(oldcxt);
309 custom_options[num_custom_options++] = newoption;
311 need_initialization = true;
315 * allocate_reloption
316 * Allocate a new reloption and initialize the type-agnostic fields
317 * (for types other than string)
319 static relopt_gen *
320 allocate_reloption(int kind, int type, char *name, char *desc)
322 MemoryContext oldcxt;
323 size_t size;
324 relopt_gen *newoption;
326 Assert(type != RELOPT_TYPE_STRING);
328 oldcxt = MemoryContextSwitchTo(TopMemoryContext);
330 switch (type)
332 case RELOPT_TYPE_BOOL:
333 size = sizeof(relopt_bool);
334 break;
335 case RELOPT_TYPE_INT:
336 size = sizeof(relopt_int);
337 break;
338 case RELOPT_TYPE_REAL:
339 size = sizeof(relopt_real);
340 break;
341 default:
342 elog(ERROR, "unsupported option type");
343 return NULL; /* keep compiler quiet */
346 newoption = palloc(size);
348 newoption->name = pstrdup(name);
349 if (desc)
350 newoption->desc = pstrdup(desc);
351 else
352 newoption->desc = NULL;
353 newoption->kind = kind;
354 newoption->namelen = strlen(name);
355 newoption->type = type;
357 MemoryContextSwitchTo(oldcxt);
359 return newoption;
363 * add_bool_reloption
364 * Add a new boolean reloption
366 void
367 add_bool_reloption(int kind, char *name, char *desc, bool default_val)
369 relopt_bool *newoption;
371 newoption = (relopt_bool *) allocate_reloption(kind, RELOPT_TYPE_BOOL,
372 name, desc);
373 newoption->default_val = default_val;
375 add_reloption((relopt_gen *) newoption);
379 * add_int_reloption
380 * Add a new integer reloption
382 void
383 add_int_reloption(int kind, char *name, char *desc, int default_val,
384 int min_val, int max_val)
386 relopt_int *newoption;
388 newoption = (relopt_int *) allocate_reloption(kind, RELOPT_TYPE_INT,
389 name, desc);
390 newoption->default_val = default_val;
391 newoption->min = min_val;
392 newoption->max = max_val;
394 add_reloption((relopt_gen *) newoption);
398 * add_real_reloption
399 * Add a new float reloption
401 void
402 add_real_reloption(int kind, char *name, char *desc, double default_val,
403 double min_val, double max_val)
405 relopt_real *newoption;
407 newoption = (relopt_real *) allocate_reloption(kind, RELOPT_TYPE_REAL,
408 name, desc);
409 newoption->default_val = default_val;
410 newoption->min = min_val;
411 newoption->max = max_val;
413 add_reloption((relopt_gen *) newoption);
417 * add_string_reloption
418 * Add a new string reloption
420 * "validator" is an optional function pointer that can be used to test the
421 * validity of the values. It must elog(ERROR) when the argument string is
422 * not acceptable for the variable. Note that the default value must pass
423 * the validation.
425 void
426 add_string_reloption(int kind, char *name, char *desc, char *default_val,
427 validate_string_relopt validator)
429 MemoryContext oldcxt;
430 relopt_string *newoption;
431 int default_len = 0;
433 oldcxt = MemoryContextSwitchTo(TopMemoryContext);
435 if (default_val)
436 default_len = strlen(default_val);
438 newoption = palloc0(sizeof(relopt_string) + default_len);
440 newoption->gen.name = pstrdup(name);
441 if (desc)
442 newoption->gen.desc = pstrdup(desc);
443 else
444 newoption->gen.desc = NULL;
445 newoption->gen.kind = kind;
446 newoption->gen.namelen = strlen(name);
447 newoption->gen.type = RELOPT_TYPE_STRING;
448 newoption->validate_cb = validator;
449 if (default_val)
451 strcpy(newoption->default_val, default_val);
452 newoption->default_len = default_len;
453 newoption->default_isnull = false;
455 else
457 newoption->default_val[0] = '\0';
458 newoption->default_len = 0;
459 newoption->default_isnull = true;
462 /* make sure the validator/default combination is sane */
463 if (newoption->validate_cb)
464 (newoption->validate_cb) (newoption->default_val);
466 MemoryContextSwitchTo(oldcxt);
468 add_reloption((relopt_gen *) newoption);
472 * Transform a relation options list (list of ReloptElem) into the text array
473 * format that is kept in pg_class.reloptions, including only those options
474 * that are in the passed namespace. The output values do not include the
475 * namespace.
477 * This is used for three cases: CREATE TABLE/INDEX, ALTER TABLE SET, and
478 * ALTER TABLE RESET. In the ALTER cases, oldOptions is the existing
479 * reloptions value (possibly NULL), and we replace or remove entries
480 * as needed.
482 * If ignoreOids is true, then we should ignore any occurrence of "oids"
483 * in the list (it will be or has been handled by interpretOidsOption()).
485 * Note that this is not responsible for determining whether the options
486 * are valid, but it does check that namespaces for all the options given are
487 * listed in validnsps. The NULL namespace is always valid and needs not be
488 * explicitely listed. Passing a NULL pointer means that only the NULL
489 * namespace is valid.
491 * Both oldOptions and the result are text arrays (or NULL for "default"),
492 * but we declare them as Datums to avoid including array.h in reloptions.h.
494 Datum
495 transformRelOptions(Datum oldOptions, List *defList, char *namspace,
496 char *validnsps[], bool ignoreOids, bool isReset)
498 Datum result;
499 ArrayBuildState *astate;
500 ListCell *cell;
502 /* no change if empty list */
503 if (defList == NIL)
504 return oldOptions;
506 /* We build new array using accumArrayResult */
507 astate = NULL;
509 /* Copy any oldOptions that aren't to be replaced */
510 if (PointerIsValid(DatumGetPointer(oldOptions)))
512 ArrayType *array = DatumGetArrayTypeP(oldOptions);
513 Datum *oldoptions;
514 int noldoptions;
515 int i;
517 Assert(ARR_ELEMTYPE(array) == TEXTOID);
519 deconstruct_array(array, TEXTOID, -1, false, 'i',
520 &oldoptions, NULL, &noldoptions);
522 for (i = 0; i < noldoptions; i++)
524 text *oldoption = DatumGetTextP(oldoptions[i]);
525 char *text_str = VARDATA(oldoption);
526 int text_len = VARSIZE(oldoption) - VARHDRSZ;
528 /* Search for a match in defList */
529 foreach(cell, defList)
531 ReloptElem *def = lfirst(cell);
532 int kw_len;
534 /* ignore if not in the same namespace */
535 if (namspace == NULL)
537 if (def->nmspc != NULL)
538 continue;
540 else if (def->nmspc == NULL)
541 continue;
542 else if (pg_strcasecmp(def->nmspc, namspace) != 0)
543 continue;
545 kw_len = strlen(def->optname);
546 if (text_len > kw_len && text_str[kw_len] == '=' &&
547 pg_strncasecmp(text_str, def->optname, kw_len) == 0)
548 break;
550 if (!cell)
552 /* No match, so keep old option */
553 astate = accumArrayResult(astate, oldoptions[i],
554 false, TEXTOID,
555 CurrentMemoryContext);
561 * If CREATE/SET, add new options to array; if RESET, just check that the
562 * user didn't say RESET (option=val). (Must do this because the grammar
563 * doesn't enforce it.)
565 foreach(cell, defList)
567 ReloptElem *def = lfirst(cell);
570 if (isReset)
572 if (def->arg != NULL)
573 ereport(ERROR,
574 (errcode(ERRCODE_SYNTAX_ERROR),
575 errmsg("RESET must not include values for parameters")));
577 else
579 text *t;
580 const char *value;
581 Size len;
584 * Error out if the namespace is not valid. A NULL namespace
585 * is always valid.
587 if (def->nmspc != NULL)
589 bool valid = false;
590 int i;
592 if (validnsps)
594 for (i = 0; validnsps[i]; i++)
596 if (pg_strcasecmp(def->nmspc, validnsps[i]) == 0)
598 valid = true;
599 break;
604 if (!valid)
605 ereport(ERROR,
606 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
607 errmsg("unrecognized parameter namespace \"%s\"",
608 def->nmspc)));
611 if (ignoreOids && pg_strcasecmp(def->optname, "oids") == 0)
612 continue;
614 /* ignore if not in the same namespace */
615 if (namspace == NULL)
617 if (def->nmspc != NULL)
618 continue;
620 else if (def->nmspc == NULL)
621 continue;
622 else if (pg_strcasecmp(def->nmspc, namspace) != 0)
623 continue;
626 * Flatten the ReloptElem into a text string like "name=arg". If we
627 * have just "name", assume "name=true" is meant. Note: the
628 * namespace is not output.
630 if (def->arg != NULL)
631 value = reloptGetString(def);
632 else
633 value = "true";
634 len = VARHDRSZ + strlen(def->optname) + 1 + strlen(value);
635 /* +1 leaves room for sprintf's trailing null */
636 t = (text *) palloc(len + 1);
637 SET_VARSIZE(t, len);
638 sprintf(VARDATA(t), "%s=%s", def->optname, value);
640 astate = accumArrayResult(astate, PointerGetDatum(t),
641 false, TEXTOID,
642 CurrentMemoryContext);
646 if (astate)
647 result = makeArrayResult(astate, CurrentMemoryContext);
648 else
649 result = (Datum) 0;
651 return result;
656 * Convert the text-array format of reloptions into a List of DefElem.
657 * This is the inverse of transformRelOptions().
659 List *
660 untransformRelOptions(Datum options)
662 List *result = NIL;
663 ArrayType *array;
664 Datum *optiondatums;
665 int noptions;
666 int i;
668 /* Nothing to do if no options */
669 if (!PointerIsValid(DatumGetPointer(options)))
670 return result;
672 array = DatumGetArrayTypeP(options);
674 Assert(ARR_ELEMTYPE(array) == TEXTOID);
676 deconstruct_array(array, TEXTOID, -1, false, 'i',
677 &optiondatums, NULL, &noptions);
679 for (i = 0; i < noptions; i++)
681 char *s;
682 char *p;
683 Node *val = NULL;
685 s = TextDatumGetCString(optiondatums[i]);
686 p = strchr(s, '=');
687 if (p)
689 *p++ = '\0';
690 val = (Node *) makeString(pstrdup(p));
692 result = lappend(result, makeDefElem(pstrdup(s), val));
695 return result;
699 * Extract and parse reloptions from a pg_class tuple.
701 * This is a low-level routine, expected to be used by relcache code and
702 * callers that do not have a table's relcache entry (e.g. autovacuum). For
703 * other uses, consider grabbing the rd_options pointer from the relcache entry
704 * instead.
706 * tupdesc is pg_class' tuple descriptor. amoptions is the amoptions regproc
707 * in the case of the tuple corresponding to an index, or InvalidOid otherwise.
709 bytea *
710 extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, Oid amoptions)
712 bytea *options;
713 bool isnull;
714 Datum datum;
715 Form_pg_class classForm;
717 datum = fastgetattr(tuple,
718 Anum_pg_class_reloptions,
719 tupdesc,
720 &isnull);
721 if (isnull)
722 return NULL;
724 classForm = (Form_pg_class) GETSTRUCT(tuple);
726 /* Parse into appropriate format; don't error out here */
727 switch (classForm->relkind)
729 case RELKIND_RELATION:
730 case RELKIND_TOASTVALUE:
731 case RELKIND_UNCATALOGED:
732 options = heap_reloptions(classForm->relkind, datum, false);
733 break;
734 case RELKIND_INDEX:
735 options = index_reloptions(amoptions, datum, false);
736 break;
737 default:
738 Assert(false); /* can't get here */
739 options = NULL; /* keep compiler quiet */
740 break;
743 return options;
747 * Interpret reloptions that are given in text-array format.
749 * options is a reloption text array as constructed by transformRelOptions.
750 * kind specifies the family of options to be processed.
752 * The return value is a relopt_value * array on which the options actually
753 * set in the options array are marked with isset=true. The length of this
754 * array is returned in *numrelopts. Options not set are also present in the
755 * array; this is so that the caller can easily locate the default values.
757 * If there are no options of the given kind, numrelopts is set to 0 and NULL
758 * is returned.
760 * Note: values of type int, bool and real are allocated as part of the
761 * returned array. Values of type string are allocated separately and must
762 * be freed by the caller.
764 relopt_value *
765 parseRelOptions(Datum options, bool validate, relopt_kind kind,
766 int *numrelopts)
768 relopt_value *reloptions;
769 int numoptions = 0;
770 int i;
771 int j;
773 if (need_initialization)
774 initialize_reloptions();
776 /* Build a list of expected options, based on kind */
778 for (i = 0; relOpts[i]; i++)
779 if (relOpts[i]->kind == kind)
780 numoptions++;
782 if (numoptions == 0)
784 *numrelopts = 0;
785 return NULL;
788 reloptions = palloc(numoptions * sizeof(relopt_value));
790 for (i = 0, j = 0; relOpts[i]; i++)
792 if (relOpts[i]->kind == kind)
794 reloptions[j].gen = relOpts[i];
795 reloptions[j].isset = false;
796 j++;
800 /* Done if no options */
801 if (PointerIsValid(DatumGetPointer(options)))
803 ArrayType *array;
804 Datum *optiondatums;
805 int noptions;
807 array = DatumGetArrayTypeP(options);
809 Assert(ARR_ELEMTYPE(array) == TEXTOID);
811 deconstruct_array(array, TEXTOID, -1, false, 'i',
812 &optiondatums, NULL, &noptions);
814 for (i = 0; i < noptions; i++)
816 text *optiontext = DatumGetTextP(optiondatums[i]);
817 char *text_str = VARDATA(optiontext);
818 int text_len = VARSIZE(optiontext) - VARHDRSZ;
819 int j;
821 /* Search for a match in reloptions */
822 for (j = 0; j < numoptions; j++)
824 int kw_len = reloptions[j].gen->namelen;
826 if (text_len > kw_len && text_str[kw_len] == '=' &&
827 pg_strncasecmp(text_str, reloptions[j].gen->name,
828 kw_len) == 0)
830 parse_one_reloption(&reloptions[j], text_str, text_len,
831 validate);
832 break;
836 if (j >= numoptions && validate)
838 char *s;
839 char *p;
841 s = TextDatumGetCString(optiondatums[i]);
842 p = strchr(s, '=');
843 if (p)
844 *p = '\0';
845 ereport(ERROR,
846 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
847 errmsg("unrecognized parameter \"%s\"", s)));
852 *numrelopts = numoptions;
853 return reloptions;
857 * Subroutine for parseRelOptions, to parse and validate a single option's
858 * value
860 static void
861 parse_one_reloption(relopt_value *option, char *text_str, int text_len,
862 bool validate)
864 char *value;
865 int value_len;
866 bool parsed;
867 bool nofree = false;
869 if (option->isset && validate)
870 ereport(ERROR,
871 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
872 errmsg("parameter \"%s\" specified more than once",
873 option->gen->name)));
875 value_len = text_len - option->gen->namelen - 1;
876 value = (char *) palloc(value_len + 1);
877 memcpy(value, text_str + option->gen->namelen + 1, value_len);
878 value[value_len] = '\0';
880 switch (option->gen->type)
882 case RELOPT_TYPE_BOOL:
884 parsed = parse_bool(value, &option->values.bool_val);
885 if (validate && !parsed)
886 ereport(ERROR,
887 (errmsg("invalid value for boolean option \"%s\": %s",
888 option->gen->name, value)));
890 break;
891 case RELOPT_TYPE_INT:
893 relopt_int *optint = (relopt_int *) option->gen;
895 parsed = parse_int(value, &option->values.int_val, 0, NULL);
896 if (validate && !parsed)
897 ereport(ERROR,
898 (errmsg("invalid value for integer option \"%s\": %s",
899 option->gen->name, value)));
900 if (validate && (option->values.int_val < optint->min ||
901 option->values.int_val > optint->max))
902 ereport(ERROR,
903 (errmsg("value %s out of bounds for option \"%s\"",
904 value, option->gen->name),
905 errdetail("Valid values are between \"%d\" and \"%d\".",
906 optint->min, optint->max)));
908 break;
909 case RELOPT_TYPE_REAL:
911 relopt_real *optreal = (relopt_real *) option->gen;
913 parsed = parse_real(value, &option->values.real_val);
914 if (validate && !parsed)
915 ereport(ERROR,
916 (errmsg("invalid value for floating point option \"%s\": %s",
917 option->gen->name, value)));
918 if (validate && (option->values.real_val < optreal->min ||
919 option->values.real_val > optreal->max))
920 ereport(ERROR,
921 (errmsg("value %s out of bounds for option \"%s\"",
922 value, option->gen->name),
923 errdetail("Valid values are between \"%f\" and \"%f\".",
924 optreal->min, optreal->max)));
926 break;
927 case RELOPT_TYPE_STRING:
929 relopt_string *optstring = (relopt_string *) option->gen;
931 option->values.string_val = value;
932 nofree = true;
933 if (validate && optstring->validate_cb)
934 (optstring->validate_cb) (value);
935 parsed = true;
937 break;
938 default:
939 elog(ERROR, "unsupported reloption type %d", option->gen->type);
940 parsed = true; /* quiet compiler */
941 break;
944 if (parsed)
945 option->isset = true;
946 if (!nofree)
947 pfree(value);
951 * Given the result from parseRelOptions, allocate a struct that's of the
952 * specified base size plus any extra space that's needed for string variables.
954 * "base" should be sizeof(struct) of the reloptions struct (StdRdOptions or
955 * equivalent).
957 void *
958 allocateReloptStruct(Size base, relopt_value *options, int numoptions)
960 Size size = base;
961 int i;
963 for (i = 0; i < numoptions; i++)
964 if (options[i].gen->type == RELOPT_TYPE_STRING)
965 size += GET_STRING_RELOPTION_LEN(options[i]) + 1;
967 return palloc0(size);
971 * Given the result of parseRelOptions and a parsing table, fill in the
972 * struct (previously allocated with allocateReloptStruct) with the parsed
973 * values.
975 * rdopts is the pointer to the allocated struct to be filled; basesize is
976 * the sizeof(struct) that was passed to allocateReloptStruct. options and
977 * numoptions are parseRelOptions' output. elems and numelems is the array
978 * of elements to be parsed. Note that when validate is true, it is expected
979 * that all options are also in elems.
981 void
982 fillRelOptions(void *rdopts, Size basesize, relopt_value *options,
983 int numoptions, bool validate, relopt_parse_elt *elems,
984 int numelems)
986 int i;
987 int offset = basesize;
989 for (i = 0; i < numoptions; i++)
991 int j;
992 bool found = false;
994 for (j = 0; j < numelems; j++)
996 if (pg_strcasecmp(options[i].gen->name, elems[j].optname) == 0)
998 relopt_string *optstring;
999 char *itempos = ((char *) rdopts) + elems[j].offset;
1000 char *string_val;
1002 switch (options[i].gen->type)
1004 case RELOPT_TYPE_BOOL:
1005 *(bool *) itempos = options[i].isset ?
1006 options[i].values.bool_val :
1007 ((relopt_bool *) options[i].gen)->default_val;
1008 break;
1009 case RELOPT_TYPE_INT:
1010 *(int *) itempos = options[i].isset ?
1011 options[i].values.int_val :
1012 ((relopt_int *) options[i].gen)->default_val;
1013 break;
1014 case RELOPT_TYPE_REAL:
1015 *(double *) itempos = options[i].isset ?
1016 options[i].values.real_val :
1017 ((relopt_real *) options[i].gen)->default_val;
1018 break;
1019 case RELOPT_TYPE_STRING:
1020 optstring = (relopt_string *) options[i].gen;
1021 if (options[i].isset)
1022 string_val = options[i].values.string_val;
1023 else if (!optstring->default_isnull)
1024 string_val = optstring->default_val;
1025 else
1026 string_val = NULL;
1028 if (string_val == NULL)
1029 *(int *) itempos = 0;
1030 else
1032 strcpy((char *) rdopts + offset, string_val);
1033 *(int *) itempos = offset;
1034 offset += strlen(string_val) + 1;
1036 break;
1037 default:
1038 elog(ERROR, "unrecognized reloption type %c",
1039 options[i].gen->type);
1040 break;
1042 found = true;
1043 break;
1046 if (validate && !found)
1047 elog(ERROR, "storate parameter \"%s\" not found in parse table",
1048 options[i].gen->name);
1050 SET_VARSIZE(rdopts, offset);
1055 * Option parser for anything that uses StdRdOptions (i.e. fillfactor and
1056 * autovacuum)
1058 bytea *
1059 default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
1061 relopt_value *options;
1062 StdRdOptions *rdopts;
1063 int numoptions;
1064 relopt_parse_elt tab[] = {
1065 {"fillfactor", RELOPT_TYPE_INT, offsetof(StdRdOptions, fillfactor)},
1066 {"autovacuum_enabled", RELOPT_TYPE_BOOL,
1067 offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, enabled)},
1068 {"autovacuum_vacuum_threshold", RELOPT_TYPE_INT,
1069 offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_threshold)},
1070 {"autovacuum_analyze_threshold", RELOPT_TYPE_INT,
1071 offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, analyze_threshold)},
1072 {"autovacuum_vacuum_cost_delay", RELOPT_TYPE_INT,
1073 offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_cost_delay)},
1074 {"autovacuum_vacuum_cost_limit", RELOPT_TYPE_INT,
1075 offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_cost_limit)},
1076 {"autovacuum_freeze_min_age", RELOPT_TYPE_INT,
1077 offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_min_age)},
1078 {"autovacuum_freeze_max_age", RELOPT_TYPE_INT,
1079 offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_max_age)},
1080 {"autovacuum_freeze_table_age", RELOPT_TYPE_INT,
1081 offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_table_age)},
1082 {"autovacuum_vacuum_scale_factor", RELOPT_TYPE_REAL,
1083 offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_scale_factor)},
1084 {"autovacuum_analyze_scale_factor", RELOPT_TYPE_REAL,
1085 offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, analyze_scale_factor)}
1088 options = parseRelOptions(reloptions, validate, kind, &numoptions);
1090 /* if none set, we're done */
1091 if (numoptions == 0)
1092 return NULL;
1094 rdopts = allocateReloptStruct(sizeof(StdRdOptions), options, numoptions);
1096 fillRelOptions((void *) rdopts, sizeof(StdRdOptions), options, numoptions,
1097 validate, tab, lengthof(tab));
1099 pfree(options);
1101 return (bytea *) rdopts;
1105 * Parse options for heaps and toast tables.
1107 bytea *
1108 heap_reloptions(char relkind, Datum reloptions, bool validate)
1110 return default_reloptions(reloptions, validate, RELOPT_KIND_HEAP);
1115 * Parse options for indexes.
1117 * amoptions Oid of option parser
1118 * reloptions options as text[] datum
1119 * validate error flag
1121 bytea *
1122 index_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
1124 FmgrInfo flinfo;
1125 FunctionCallInfoData fcinfo;
1126 Datum result;
1128 Assert(RegProcedureIsValid(amoptions));
1130 /* Assume function is strict */
1131 if (!PointerIsValid(DatumGetPointer(reloptions)))
1132 return NULL;
1134 /* Can't use OidFunctionCallN because we might get a NULL result */
1135 fmgr_info(amoptions, &flinfo);
1137 InitFunctionCallInfoData(fcinfo, &flinfo, 2, NULL, NULL);
1139 fcinfo.arg[0] = reloptions;
1140 fcinfo.arg[1] = BoolGetDatum(validate);
1141 fcinfo.argnull[0] = false;
1142 fcinfo.argnull[1] = false;
1144 result = FunctionCallInvoke(&fcinfo);
1146 if (fcinfo.isnull || DatumGetPointer(result) == NULL)
1147 return NULL;
1149 return DatumGetByteaP(result);