]> granicus.if.org Git - postgresql/blob - src/backend/access/common/reloptions.c
Add a new reloption, user_catalog_table.
[postgresql] / src / backend / access / common / reloptions.c
1 /*-------------------------------------------------------------------------
2  *
3  * reloptions.c
4  *        Core support for relation options (pg_class.reloptions)
5  *
6  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        src/backend/access/common/reloptions.c
12  *
13  *-------------------------------------------------------------------------
14  */
15
16 #include "postgres.h"
17
18 #include "access/gist_private.h"
19 #include "access/hash.h"
20 #include "access/htup_details.h"
21 #include "access/nbtree.h"
22 #include "access/reloptions.h"
23 #include "access/spgist.h"
24 #include "catalog/pg_type.h"
25 #include "commands/defrem.h"
26 #include "commands/tablespace.h"
27 #include "commands/view.h"
28 #include "nodes/makefuncs.h"
29 #include "utils/array.h"
30 #include "utils/attoptcache.h"
31 #include "utils/builtins.h"
32 #include "utils/guc.h"
33 #include "utils/memutils.h"
34 #include "utils/rel.h"
35
36 /*
37  * Contents of pg_class.reloptions
38  *
39  * To add an option:
40  *
41  * (i) decide on a type (integer, real, bool, string), name, default value,
42  * upper and lower bounds (if applicable); for strings, consider a validation
43  * routine.
44  * (ii) add a record below (or use add_<type>_reloption).
45  * (iii) add it to the appropriate options struct (perhaps StdRdOptions)
46  * (iv) add it to the appropriate handling routine (perhaps
47  * default_reloptions)
48  * (v) don't forget to document the option
49  *
50  * Note that we don't handle "oids" in relOpts because it is handled by
51  * interpretOidsOption().
52  */
53
54 static relopt_bool boolRelOpts[] =
55 {
56         {
57                 {
58                         "autovacuum_enabled",
59                         "Enables autovacuum in this relation",
60                         RELOPT_KIND_HEAP | RELOPT_KIND_TOAST
61                 },
62                 true
63         },
64         {
65                 {
66                         "user_catalog_table",
67                         "Declare a table as an additional catalog table, e.g. for the purpose of logical replication",
68                         RELOPT_KIND_HEAP
69                 },
70                 false
71         },
72         {
73                 {
74                         "fastupdate",
75                         "Enables \"fast update\" feature for this GIN index",
76                         RELOPT_KIND_GIN
77                 },
78                 true
79         },
80         {
81                 {
82                         "security_barrier",
83                         "View acts as a row security barrier",
84                         RELOPT_KIND_VIEW
85                 },
86                 false
87         },
88         /* list terminator */
89         {{NULL}}
90 };
91
92 static relopt_int intRelOpts[] =
93 {
94         {
95                 {
96                         "fillfactor",
97                         "Packs table pages only to this percentage",
98                         RELOPT_KIND_HEAP
99                 },
100                 HEAP_DEFAULT_FILLFACTOR, HEAP_MIN_FILLFACTOR, 100
101         },
102         {
103                 {
104                         "fillfactor",
105                         "Packs btree index pages only to this percentage",
106                         RELOPT_KIND_BTREE
107                 },
108                 BTREE_DEFAULT_FILLFACTOR, BTREE_MIN_FILLFACTOR, 100
109         },
110         {
111                 {
112                         "fillfactor",
113                         "Packs hash index pages only to this percentage",
114                         RELOPT_KIND_HASH
115                 },
116                 HASH_DEFAULT_FILLFACTOR, HASH_MIN_FILLFACTOR, 100
117         },
118         {
119                 {
120                         "fillfactor",
121                         "Packs gist index pages only to this percentage",
122                         RELOPT_KIND_GIST
123                 },
124                 GIST_DEFAULT_FILLFACTOR, GIST_MIN_FILLFACTOR, 100
125         },
126         {
127                 {
128                         "fillfactor",
129                         "Packs spgist index pages only to this percentage",
130                         RELOPT_KIND_SPGIST
131                 },
132                 SPGIST_DEFAULT_FILLFACTOR, SPGIST_MIN_FILLFACTOR, 100
133         },
134         {
135                 {
136                         "autovacuum_vacuum_threshold",
137                         "Minimum number of tuple updates or deletes prior to vacuum",
138                         RELOPT_KIND_HEAP | RELOPT_KIND_TOAST
139                 },
140                 -1, 0, INT_MAX
141         },
142         {
143                 {
144                         "autovacuum_analyze_threshold",
145                         "Minimum number of tuple inserts, updates or deletes prior to analyze",
146                         RELOPT_KIND_HEAP
147                 },
148                 -1, 0, INT_MAX
149         },
150         {
151                 {
152                         "autovacuum_vacuum_cost_delay",
153                         "Vacuum cost delay in milliseconds, for autovacuum",
154                         RELOPT_KIND_HEAP | RELOPT_KIND_TOAST
155                 },
156                 -1, 0, 100
157         },
158         {
159                 {
160                         "autovacuum_vacuum_cost_limit",
161                         "Vacuum cost amount available before napping, for autovacuum",
162                         RELOPT_KIND_HEAP | RELOPT_KIND_TOAST
163                 },
164                 -1, 1, 10000
165         },
166         {
167                 {
168                         "autovacuum_freeze_min_age",
169                         "Minimum age at which VACUUM should freeze a table row, for autovacuum",
170                         RELOPT_KIND_HEAP | RELOPT_KIND_TOAST
171                 },
172                 -1, 0, 1000000000
173         },
174         {
175                 {
176                         "autovacuum_freeze_max_age",
177                         "Age at which to autovacuum a table to prevent transaction ID wraparound",
178                         RELOPT_KIND_HEAP | RELOPT_KIND_TOAST
179                 },
180                 -1, 100000000, 2000000000
181         },
182         {
183                 {
184                         "autovacuum_freeze_table_age",
185                         "Age at which VACUUM should perform a full table sweep to replace old Xid values with FrozenXID",
186                         RELOPT_KIND_HEAP | RELOPT_KIND_TOAST
187                 }, -1, 0, 2000000000
188         },
189         /* list terminator */
190         {{NULL}}
191 };
192
193 static relopt_real realRelOpts[] =
194 {
195         {
196                 {
197                         "autovacuum_vacuum_scale_factor",
198                         "Number of tuple updates or deletes prior to vacuum as a fraction of reltuples",
199                         RELOPT_KIND_HEAP | RELOPT_KIND_TOAST
200                 },
201                 -1, 0.0, 100.0
202         },
203         {
204                 {
205                         "autovacuum_analyze_scale_factor",
206                         "Number of tuple inserts, updates or deletes prior to analyze as a fraction of reltuples",
207                         RELOPT_KIND_HEAP
208                 },
209                 -1, 0.0, 100.0
210         },
211         {
212                 {
213                         "seq_page_cost",
214                         "Sets the planner's estimate of the cost of a sequentially fetched disk page.",
215                         RELOPT_KIND_TABLESPACE
216                 },
217                 -1, 0.0, DBL_MAX
218         },
219         {
220                 {
221                         "random_page_cost",
222                         "Sets the planner's estimate of the cost of a nonsequentially fetched disk page.",
223                         RELOPT_KIND_TABLESPACE
224                 },
225                 -1, 0.0, DBL_MAX
226         },
227         {
228                 {
229                         "n_distinct",
230                         "Sets the planner's estimate of the number of distinct values appearing in a column (excluding child relations).",
231                         RELOPT_KIND_ATTRIBUTE
232                 },
233                 0, -1.0, DBL_MAX
234         },
235         {
236                 {
237                         "n_distinct_inherited",
238                         "Sets the planner's estimate of the number of distinct values appearing in a column (including child relations).",
239                         RELOPT_KIND_ATTRIBUTE
240                 },
241                 0, -1.0, DBL_MAX
242         },
243         /* list terminator */
244         {{NULL}}
245 };
246
247 static relopt_string stringRelOpts[] =
248 {
249         {
250                 {
251                         "buffering",
252                         "Enables buffering build for this GiST index",
253                         RELOPT_KIND_GIST
254                 },
255                 4,
256                 false,
257                 gistValidateBufferingOption,
258                 "auto"
259         },
260         {
261                 {
262                         "check_option",
263                         "View has WITH CHECK OPTION defined (local or cascaded).",
264                         RELOPT_KIND_VIEW
265                 },
266                 0,
267                 true,
268                 validateWithCheckOption,
269                 NULL
270         },
271         /* list terminator */
272         {{NULL}}
273 };
274
275 static relopt_gen **relOpts = NULL;
276 static bits32 last_assigned_kind = RELOPT_KIND_LAST_DEFAULT;
277
278 static int      num_custom_options = 0;
279 static relopt_gen **custom_options = NULL;
280 static bool need_initialization = true;
281
282 static void initialize_reloptions(void);
283 static void parse_one_reloption(relopt_value *option, char *text_str,
284                                         int text_len, bool validate);
285
286 /*
287  * initialize_reloptions
288  *              initialization routine, must be called before parsing
289  *
290  * Initialize the relOpts array and fill each variable's type and name length.
291  */
292 static void
293 initialize_reloptions(void)
294 {
295         int                     i;
296         int                     j;
297
298         j = 0;
299         for (i = 0; boolRelOpts[i].gen.name; i++)
300                 j++;
301         for (i = 0; intRelOpts[i].gen.name; i++)
302                 j++;
303         for (i = 0; realRelOpts[i].gen.name; i++)
304                 j++;
305         for (i = 0; stringRelOpts[i].gen.name; i++)
306                 j++;
307         j += num_custom_options;
308
309         if (relOpts)
310                 pfree(relOpts);
311         relOpts = MemoryContextAlloc(TopMemoryContext,
312                                                                  (j + 1) * sizeof(relopt_gen *));
313
314         j = 0;
315         for (i = 0; boolRelOpts[i].gen.name; i++)
316         {
317                 relOpts[j] = &boolRelOpts[i].gen;
318                 relOpts[j]->type = RELOPT_TYPE_BOOL;
319                 relOpts[j]->namelen = strlen(relOpts[j]->name);
320                 j++;
321         }
322
323         for (i = 0; intRelOpts[i].gen.name; i++)
324         {
325                 relOpts[j] = &intRelOpts[i].gen;
326                 relOpts[j]->type = RELOPT_TYPE_INT;
327                 relOpts[j]->namelen = strlen(relOpts[j]->name);
328                 j++;
329         }
330
331         for (i = 0; realRelOpts[i].gen.name; i++)
332         {
333                 relOpts[j] = &realRelOpts[i].gen;
334                 relOpts[j]->type = RELOPT_TYPE_REAL;
335                 relOpts[j]->namelen = strlen(relOpts[j]->name);
336                 j++;
337         }
338
339         for (i = 0; stringRelOpts[i].gen.name; i++)
340         {
341                 relOpts[j] = &stringRelOpts[i].gen;
342                 relOpts[j]->type = RELOPT_TYPE_STRING;
343                 relOpts[j]->namelen = strlen(relOpts[j]->name);
344                 j++;
345         }
346
347         for (i = 0; i < num_custom_options; i++)
348         {
349                 relOpts[j] = custom_options[i];
350                 j++;
351         }
352
353         /* add a list terminator */
354         relOpts[j] = NULL;
355
356         /* flag the work is complete */
357         need_initialization = false;
358 }
359
360 /*
361  * add_reloption_kind
362  *              Create a new relopt_kind value, to be used in custom reloptions by
363  *              user-defined AMs.
364  */
365 relopt_kind
366 add_reloption_kind(void)
367 {
368         /* don't hand out the last bit so that the enum's behavior is portable */
369         if (last_assigned_kind >= RELOPT_KIND_MAX)
370                 ereport(ERROR,
371                                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
372                         errmsg("user-defined relation parameter types limit exceeded")));
373         last_assigned_kind <<= 1;
374         return (relopt_kind) last_assigned_kind;
375 }
376
377 /*
378  * add_reloption
379  *              Add an already-created custom reloption to the list, and recompute the
380  *              main parser table.
381  */
382 static void
383 add_reloption(relopt_gen *newoption)
384 {
385         static int      max_custom_options = 0;
386
387         if (num_custom_options >= max_custom_options)
388         {
389                 MemoryContext oldcxt;
390
391                 oldcxt = MemoryContextSwitchTo(TopMemoryContext);
392
393                 if (max_custom_options == 0)
394                 {
395                         max_custom_options = 8;
396                         custom_options = palloc(max_custom_options * sizeof(relopt_gen *));
397                 }
398                 else
399                 {
400                         max_custom_options *= 2;
401                         custom_options = repalloc(custom_options,
402                                                                   max_custom_options * sizeof(relopt_gen *));
403                 }
404                 MemoryContextSwitchTo(oldcxt);
405         }
406         custom_options[num_custom_options++] = newoption;
407
408         need_initialization = true;
409 }
410
411 /*
412  * allocate_reloption
413  *              Allocate a new reloption and initialize the type-agnostic fields
414  *              (for types other than string)
415  */
416 static relopt_gen *
417 allocate_reloption(bits32 kinds, int type, char *name, char *desc)
418 {
419         MemoryContext oldcxt;
420         size_t          size;
421         relopt_gen *newoption;
422
423         oldcxt = MemoryContextSwitchTo(TopMemoryContext);
424
425         switch (type)
426         {
427                 case RELOPT_TYPE_BOOL:
428                         size = sizeof(relopt_bool);
429                         break;
430                 case RELOPT_TYPE_INT:
431                         size = sizeof(relopt_int);
432                         break;
433                 case RELOPT_TYPE_REAL:
434                         size = sizeof(relopt_real);
435                         break;
436                 case RELOPT_TYPE_STRING:
437                         size = sizeof(relopt_string);
438                         break;
439                 default:
440                         elog(ERROR, "unsupported option type");
441                         return NULL;            /* keep compiler quiet */
442         }
443
444         newoption = palloc(size);
445
446         newoption->name = pstrdup(name);
447         if (desc)
448                 newoption->desc = pstrdup(desc);
449         else
450                 newoption->desc = NULL;
451         newoption->kinds = kinds;
452         newoption->namelen = strlen(name);
453         newoption->type = type;
454
455         MemoryContextSwitchTo(oldcxt);
456
457         return newoption;
458 }
459
460 /*
461  * add_bool_reloption
462  *              Add a new boolean reloption
463  */
464 void
465 add_bool_reloption(bits32 kinds, char *name, char *desc, bool default_val)
466 {
467         relopt_bool *newoption;
468
469         newoption = (relopt_bool *) allocate_reloption(kinds, RELOPT_TYPE_BOOL,
470                                                                                                    name, desc);
471         newoption->default_val = default_val;
472
473         add_reloption((relopt_gen *) newoption);
474 }
475
476 /*
477  * add_int_reloption
478  *              Add a new integer reloption
479  */
480 void
481 add_int_reloption(bits32 kinds, char *name, char *desc, int default_val,
482                                   int min_val, int max_val)
483 {
484         relopt_int *newoption;
485
486         newoption = (relopt_int *) allocate_reloption(kinds, RELOPT_TYPE_INT,
487                                                                                                   name, desc);
488         newoption->default_val = default_val;
489         newoption->min = min_val;
490         newoption->max = max_val;
491
492         add_reloption((relopt_gen *) newoption);
493 }
494
495 /*
496  * add_real_reloption
497  *              Add a new float reloption
498  */
499 void
500 add_real_reloption(bits32 kinds, char *name, char *desc, double default_val,
501                                    double min_val, double max_val)
502 {
503         relopt_real *newoption;
504
505         newoption = (relopt_real *) allocate_reloption(kinds, RELOPT_TYPE_REAL,
506                                                                                                    name, desc);
507         newoption->default_val = default_val;
508         newoption->min = min_val;
509         newoption->max = max_val;
510
511         add_reloption((relopt_gen *) newoption);
512 }
513
514 /*
515  * add_string_reloption
516  *              Add a new string reloption
517  *
518  * "validator" is an optional function pointer that can be used to test the
519  * validity of the values.      It must elog(ERROR) when the argument string is
520  * not acceptable for the variable.  Note that the default value must pass
521  * the validation.
522  */
523 void
524 add_string_reloption(bits32 kinds, char *name, char *desc, char *default_val,
525                                          validate_string_relopt validator)
526 {
527         relopt_string *newoption;
528
529         /* make sure the validator/default combination is sane */
530         if (validator)
531                 (validator) (default_val);
532
533         newoption = (relopt_string *) allocate_reloption(kinds, RELOPT_TYPE_STRING,
534                                                                                                          name, desc);
535         newoption->validate_cb = validator;
536         if (default_val)
537         {
538                 newoption->default_val = MemoryContextStrdup(TopMemoryContext,
539                                                                                                          default_val);
540                 newoption->default_len = strlen(default_val);
541                 newoption->default_isnull = false;
542         }
543         else
544         {
545                 newoption->default_val = "";
546                 newoption->default_len = 0;
547                 newoption->default_isnull = true;
548         }
549
550         add_reloption((relopt_gen *) newoption);
551 }
552
553 /*
554  * Transform a relation options list (list of DefElem) into the text array
555  * format that is kept in pg_class.reloptions, including only those options
556  * that are in the passed namespace.  The output values do not include the
557  * namespace.
558  *
559  * This is used for three cases: CREATE TABLE/INDEX, ALTER TABLE SET, and
560  * ALTER TABLE RESET.  In the ALTER cases, oldOptions is the existing
561  * reloptions value (possibly NULL), and we replace or remove entries
562  * as needed.
563  *
564  * If ignoreOids is true, then we should ignore any occurrence of "oids"
565  * in the list (it will be or has been handled by interpretOidsOption()).
566  *
567  * Note that this is not responsible for determining whether the options
568  * are valid, but it does check that namespaces for all the options given are
569  * listed in validnsps.  The NULL namespace is always valid and need not be
570  * explicitly listed.  Passing a NULL pointer means that only the NULL
571  * namespace is valid.
572  *
573  * Both oldOptions and the result are text arrays (or NULL for "default"),
574  * but we declare them as Datums to avoid including array.h in reloptions.h.
575  */
576 Datum
577 transformRelOptions(Datum oldOptions, List *defList, char *namspace,
578                                         char *validnsps[], bool ignoreOids, bool isReset)
579 {
580         Datum           result;
581         ArrayBuildState *astate;
582         ListCell   *cell;
583
584         /* no change if empty list */
585         if (defList == NIL)
586                 return oldOptions;
587
588         /* We build new array using accumArrayResult */
589         astate = NULL;
590
591         /* Copy any oldOptions that aren't to be replaced */
592         if (PointerIsValid(DatumGetPointer(oldOptions)))
593         {
594                 ArrayType  *array = DatumGetArrayTypeP(oldOptions);
595                 Datum      *oldoptions;
596                 int                     noldoptions;
597                 int                     i;
598
599                 Assert(ARR_ELEMTYPE(array) == TEXTOID);
600
601                 deconstruct_array(array, TEXTOID, -1, false, 'i',
602                                                   &oldoptions, NULL, &noldoptions);
603
604                 for (i = 0; i < noldoptions; i++)
605                 {
606                         text       *oldoption = DatumGetTextP(oldoptions[i]);
607                         char       *text_str = VARDATA(oldoption);
608                         int                     text_len = VARSIZE(oldoption) - VARHDRSZ;
609
610                         /* Search for a match in defList */
611                         foreach(cell, defList)
612                         {
613                                 DefElem    *def = (DefElem *) lfirst(cell);
614                                 int                     kw_len;
615
616                                 /* ignore if not in the same namespace */
617                                 if (namspace == NULL)
618                                 {
619                                         if (def->defnamespace != NULL)
620                                                 continue;
621                                 }
622                                 else if (def->defnamespace == NULL)
623                                         continue;
624                                 else if (pg_strcasecmp(def->defnamespace, namspace) != 0)
625                                         continue;
626
627                                 kw_len = strlen(def->defname);
628                                 if (text_len > kw_len && text_str[kw_len] == '=' &&
629                                         pg_strncasecmp(text_str, def->defname, kw_len) == 0)
630                                         break;
631                         }
632                         if (!cell)
633                         {
634                                 /* No match, so keep old option */
635                                 astate = accumArrayResult(astate, oldoptions[i],
636                                                                                   false, TEXTOID,
637                                                                                   CurrentMemoryContext);
638                         }
639                 }
640         }
641
642         /*
643          * If CREATE/SET, add new options to array; if RESET, just check that the
644          * user didn't say RESET (option=val).  (Must do this because the grammar
645          * doesn't enforce it.)
646          */
647         foreach(cell, defList)
648         {
649                 DefElem    *def = (DefElem *) lfirst(cell);
650
651                 if (isReset)
652                 {
653                         if (def->arg != NULL)
654                                 ereport(ERROR,
655                                                 (errcode(ERRCODE_SYNTAX_ERROR),
656                                         errmsg("RESET must not include values for parameters")));
657                 }
658                 else
659                 {
660                         text       *t;
661                         const char *value;
662                         Size            len;
663
664                         /*
665                          * Error out if the namespace is not valid.  A NULL namespace is
666                          * always valid.
667                          */
668                         if (def->defnamespace != NULL)
669                         {
670                                 bool            valid = false;
671                                 int                     i;
672
673                                 if (validnsps)
674                                 {
675                                         for (i = 0; validnsps[i]; i++)
676                                         {
677                                                 if (pg_strcasecmp(def->defnamespace,
678                                                                                   validnsps[i]) == 0)
679                                                 {
680                                                         valid = true;
681                                                         break;
682                                                 }
683                                         }
684                                 }
685
686                                 if (!valid)
687                                         ereport(ERROR,
688                                                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
689                                                          errmsg("unrecognized parameter namespace \"%s\"",
690                                                                         def->defnamespace)));
691                         }
692
693                         if (ignoreOids && pg_strcasecmp(def->defname, "oids") == 0)
694                                 continue;
695
696                         /* ignore if not in the same namespace */
697                         if (namspace == NULL)
698                         {
699                                 if (def->defnamespace != NULL)
700                                         continue;
701                         }
702                         else if (def->defnamespace == NULL)
703                                 continue;
704                         else if (pg_strcasecmp(def->defnamespace, namspace) != 0)
705                                 continue;
706
707                         /*
708                          * Flatten the DefElem into a text string like "name=arg". If we
709                          * have just "name", assume "name=true" is meant.  Note: the
710                          * namespace is not output.
711                          */
712                         if (def->arg != NULL)
713                                 value = defGetString(def);
714                         else
715                                 value = "true";
716                         len = VARHDRSZ + strlen(def->defname) + 1 + strlen(value);
717                         /* +1 leaves room for sprintf's trailing null */
718                         t = (text *) palloc(len + 1);
719                         SET_VARSIZE(t, len);
720                         sprintf(VARDATA(t), "%s=%s", def->defname, value);
721
722                         astate = accumArrayResult(astate, PointerGetDatum(t),
723                                                                           false, TEXTOID,
724                                                                           CurrentMemoryContext);
725                 }
726         }
727
728         if (astate)
729                 result = makeArrayResult(astate, CurrentMemoryContext);
730         else
731                 result = (Datum) 0;
732
733         return result;
734 }
735
736
737 /*
738  * Convert the text-array format of reloptions into a List of DefElem.
739  * This is the inverse of transformRelOptions().
740  */
741 List *
742 untransformRelOptions(Datum options)
743 {
744         List       *result = NIL;
745         ArrayType  *array;
746         Datum      *optiondatums;
747         int                     noptions;
748         int                     i;
749
750         /* Nothing to do if no options */
751         if (!PointerIsValid(DatumGetPointer(options)))
752                 return result;
753
754         array = DatumGetArrayTypeP(options);
755
756         Assert(ARR_ELEMTYPE(array) == TEXTOID);
757
758         deconstruct_array(array, TEXTOID, -1, false, 'i',
759                                           &optiondatums, NULL, &noptions);
760
761         for (i = 0; i < noptions; i++)
762         {
763                 char       *s;
764                 char       *p;
765                 Node       *val = NULL;
766
767                 s = TextDatumGetCString(optiondatums[i]);
768                 p = strchr(s, '=');
769                 if (p)
770                 {
771                         *p++ = '\0';
772                         val = (Node *) makeString(pstrdup(p));
773                 }
774                 result = lappend(result, makeDefElem(pstrdup(s), val));
775         }
776
777         return result;
778 }
779
780 /*
781  * Extract and parse reloptions from a pg_class tuple.
782  *
783  * This is a low-level routine, expected to be used by relcache code and
784  * callers that do not have a table's relcache entry (e.g. autovacuum).  For
785  * other uses, consider grabbing the rd_options pointer from the relcache entry
786  * instead.
787  *
788  * tupdesc is pg_class' tuple descriptor.  amoptions is the amoptions regproc
789  * in the case of the tuple corresponding to an index, or InvalidOid otherwise.
790  */
791 bytea *
792 extractRelOptions(HeapTuple tuple, TupleDesc tupdesc, Oid amoptions)
793 {
794         bytea      *options;
795         bool            isnull;
796         Datum           datum;
797         Form_pg_class classForm;
798
799         datum = fastgetattr(tuple,
800                                                 Anum_pg_class_reloptions,
801                                                 tupdesc,
802                                                 &isnull);
803         if (isnull)
804                 return NULL;
805
806         classForm = (Form_pg_class) GETSTRUCT(tuple);
807
808         /* Parse into appropriate format; don't error out here */
809         switch (classForm->relkind)
810         {
811                 case RELKIND_RELATION:
812                 case RELKIND_TOASTVALUE:
813                 case RELKIND_VIEW:
814                 case RELKIND_MATVIEW:
815                         options = heap_reloptions(classForm->relkind, datum, false);
816                         break;
817                 case RELKIND_INDEX:
818                         options = index_reloptions(amoptions, datum, false);
819                         break;
820                 case RELKIND_FOREIGN_TABLE:
821                         options = NULL;
822                         break;
823                 default:
824                         Assert(false);          /* can't get here */
825                         options = NULL;         /* keep compiler quiet */
826                         break;
827         }
828
829         return options;
830 }
831
832 /*
833  * Interpret reloptions that are given in text-array format.
834  *
835  * options is a reloption text array as constructed by transformRelOptions.
836  * kind specifies the family of options to be processed.
837  *
838  * The return value is a relopt_value * array on which the options actually
839  * set in the options array are marked with isset=true.  The length of this
840  * array is returned in *numrelopts.  Options not set are also present in the
841  * array; this is so that the caller can easily locate the default values.
842  *
843  * If there are no options of the given kind, numrelopts is set to 0 and NULL
844  * is returned.
845  *
846  * Note: values of type int, bool and real are allocated as part of the
847  * returned array.      Values of type string are allocated separately and must
848  * be freed by the caller.
849  */
850 relopt_value *
851 parseRelOptions(Datum options, bool validate, relopt_kind kind,
852                                 int *numrelopts)
853 {
854         relopt_value *reloptions;
855         int                     numoptions = 0;
856         int                     i;
857         int                     j;
858
859         if (need_initialization)
860                 initialize_reloptions();
861
862         /* Build a list of expected options, based on kind */
863
864         for (i = 0; relOpts[i]; i++)
865                 if (relOpts[i]->kinds & kind)
866                         numoptions++;
867
868         if (numoptions == 0)
869         {
870                 *numrelopts = 0;
871                 return NULL;
872         }
873
874         reloptions = palloc(numoptions * sizeof(relopt_value));
875
876         for (i = 0, j = 0; relOpts[i]; i++)
877         {
878                 if (relOpts[i]->kinds & kind)
879                 {
880                         reloptions[j].gen = relOpts[i];
881                         reloptions[j].isset = false;
882                         j++;
883                 }
884         }
885
886         /* Done if no options */
887         if (PointerIsValid(DatumGetPointer(options)))
888         {
889                 ArrayType  *array;
890                 Datum      *optiondatums;
891                 int                     noptions;
892
893                 array = DatumGetArrayTypeP(options);
894
895                 Assert(ARR_ELEMTYPE(array) == TEXTOID);
896
897                 deconstruct_array(array, TEXTOID, -1, false, 'i',
898                                                   &optiondatums, NULL, &noptions);
899
900                 for (i = 0; i < noptions; i++)
901                 {
902                         text       *optiontext = DatumGetTextP(optiondatums[i]);
903                         char       *text_str = VARDATA(optiontext);
904                         int                     text_len = VARSIZE(optiontext) - VARHDRSZ;
905                         int                     j;
906
907                         /* Search for a match in reloptions */
908                         for (j = 0; j < numoptions; j++)
909                         {
910                                 int                     kw_len = reloptions[j].gen->namelen;
911
912                                 if (text_len > kw_len && text_str[kw_len] == '=' &&
913                                         pg_strncasecmp(text_str, reloptions[j].gen->name,
914                                                                    kw_len) == 0)
915                                 {
916                                         parse_one_reloption(&reloptions[j], text_str, text_len,
917                                                                                 validate);
918                                         break;
919                                 }
920                         }
921
922                         if (j >= numoptions && validate)
923                         {
924                                 char       *s;
925                                 char       *p;
926
927                                 s = TextDatumGetCString(optiondatums[i]);
928                                 p = strchr(s, '=');
929                                 if (p)
930                                         *p = '\0';
931                                 ereport(ERROR,
932                                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
933                                                  errmsg("unrecognized parameter \"%s\"", s)));
934                         }
935                 }
936         }
937
938         *numrelopts = numoptions;
939         return reloptions;
940 }
941
942 /*
943  * Subroutine for parseRelOptions, to parse and validate a single option's
944  * value
945  */
946 static void
947 parse_one_reloption(relopt_value *option, char *text_str, int text_len,
948                                         bool validate)
949 {
950         char       *value;
951         int                     value_len;
952         bool            parsed;
953         bool            nofree = false;
954
955         if (option->isset && validate)
956                 ereport(ERROR,
957                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
958                                  errmsg("parameter \"%s\" specified more than once",
959                                                 option->gen->name)));
960
961         value_len = text_len - option->gen->namelen - 1;
962         value = (char *) palloc(value_len + 1);
963         memcpy(value, text_str + option->gen->namelen + 1, value_len);
964         value[value_len] = '\0';
965
966         switch (option->gen->type)
967         {
968                 case RELOPT_TYPE_BOOL:
969                         {
970                                 parsed = parse_bool(value, &option->values.bool_val);
971                                 if (validate && !parsed)
972                                         ereport(ERROR,
973                                            (errmsg("invalid value for boolean option \"%s\": %s",
974                                                            option->gen->name, value)));
975                         }
976                         break;
977                 case RELOPT_TYPE_INT:
978                         {
979                                 relopt_int *optint = (relopt_int *) option->gen;
980
981                                 parsed = parse_int(value, &option->values.int_val, 0, NULL);
982                                 if (validate && !parsed)
983                                         ereport(ERROR,
984                                            (errmsg("invalid value for integer option \"%s\": %s",
985                                                            option->gen->name, value)));
986                                 if (validate && (option->values.int_val < optint->min ||
987                                                                  option->values.int_val > optint->max))
988                                         ereport(ERROR,
989                                                   (errmsg("value %s out of bounds for option \"%s\"",
990                                                                   value, option->gen->name),
991                                          errdetail("Valid values are between \"%d\" and \"%d\".",
992                                                            optint->min, optint->max)));
993                         }
994                         break;
995                 case RELOPT_TYPE_REAL:
996                         {
997                                 relopt_real *optreal = (relopt_real *) option->gen;
998
999                                 parsed = parse_real(value, &option->values.real_val);
1000                                 if (validate && !parsed)
1001                                         ereport(ERROR,
1002                                                         (errmsg("invalid value for floating point option \"%s\": %s",
1003                                                                         option->gen->name, value)));
1004                                 if (validate && (option->values.real_val < optreal->min ||
1005                                                                  option->values.real_val > optreal->max))
1006                                         ereport(ERROR,
1007                                                   (errmsg("value %s out of bounds for option \"%s\"",
1008                                                                   value, option->gen->name),
1009                                          errdetail("Valid values are between \"%f\" and \"%f\".",
1010                                                            optreal->min, optreal->max)));
1011                         }
1012                         break;
1013                 case RELOPT_TYPE_STRING:
1014                         {
1015                                 relopt_string *optstring = (relopt_string *) option->gen;
1016
1017                                 option->values.string_val = value;
1018                                 nofree = true;
1019                                 if (validate && optstring->validate_cb)
1020                                         (optstring->validate_cb) (value);
1021                                 parsed = true;
1022                         }
1023                         break;
1024                 default:
1025                         elog(ERROR, "unsupported reloption type %d", option->gen->type);
1026                         parsed = true;          /* quiet compiler */
1027                         break;
1028         }
1029
1030         if (parsed)
1031                 option->isset = true;
1032         if (!nofree)
1033                 pfree(value);
1034 }
1035
1036 /*
1037  * Given the result from parseRelOptions, allocate a struct that's of the
1038  * specified base size plus any extra space that's needed for string variables.
1039  *
1040  * "base" should be sizeof(struct) of the reloptions struct (StdRdOptions or
1041  * equivalent).
1042  */
1043 void *
1044 allocateReloptStruct(Size base, relopt_value *options, int numoptions)
1045 {
1046         Size            size = base;
1047         int                     i;
1048
1049         for (i = 0; i < numoptions; i++)
1050                 if (options[i].gen->type == RELOPT_TYPE_STRING)
1051                         size += GET_STRING_RELOPTION_LEN(options[i]) + 1;
1052
1053         return palloc0(size);
1054 }
1055
1056 /*
1057  * Given the result of parseRelOptions and a parsing table, fill in the
1058  * struct (previously allocated with allocateReloptStruct) with the parsed
1059  * values.
1060  *
1061  * rdopts is the pointer to the allocated struct to be filled.
1062  * basesize is the sizeof(struct) that was passed to allocateReloptStruct.
1063  * options, of length numoptions, is parseRelOptions' output.
1064  * elems, of length numelems, is the table describing the allowed options.
1065  * When validate is true, it is expected that all options appear in elems.
1066  */
1067 void
1068 fillRelOptions(void *rdopts, Size basesize,
1069                            relopt_value *options, int numoptions,
1070                            bool validate,
1071                            const relopt_parse_elt *elems, int numelems)
1072 {
1073         int                     i;
1074         int                     offset = basesize;
1075
1076         for (i = 0; i < numoptions; i++)
1077         {
1078                 int                     j;
1079                 bool            found = false;
1080
1081                 for (j = 0; j < numelems; j++)
1082                 {
1083                         if (pg_strcasecmp(options[i].gen->name, elems[j].optname) == 0)
1084                         {
1085                                 relopt_string *optstring;
1086                                 char       *itempos = ((char *) rdopts) + elems[j].offset;
1087                                 char       *string_val;
1088
1089                                 switch (options[i].gen->type)
1090                                 {
1091                                         case RELOPT_TYPE_BOOL:
1092                                                 *(bool *) itempos = options[i].isset ?
1093                                                         options[i].values.bool_val :
1094                                                         ((relopt_bool *) options[i].gen)->default_val;
1095                                                 break;
1096                                         case RELOPT_TYPE_INT:
1097                                                 *(int *) itempos = options[i].isset ?
1098                                                         options[i].values.int_val :
1099                                                         ((relopt_int *) options[i].gen)->default_val;
1100                                                 break;
1101                                         case RELOPT_TYPE_REAL:
1102                                                 *(double *) itempos = options[i].isset ?
1103                                                         options[i].values.real_val :
1104                                                         ((relopt_real *) options[i].gen)->default_val;
1105                                                 break;
1106                                         case RELOPT_TYPE_STRING:
1107                                                 optstring = (relopt_string *) options[i].gen;
1108                                                 if (options[i].isset)
1109                                                         string_val = options[i].values.string_val;
1110                                                 else if (!optstring->default_isnull)
1111                                                         string_val = optstring->default_val;
1112                                                 else
1113                                                         string_val = NULL;
1114
1115                                                 if (string_val == NULL)
1116                                                         *(int *) itempos = 0;
1117                                                 else
1118                                                 {
1119                                                         strcpy((char *) rdopts + offset, string_val);
1120                                                         *(int *) itempos = offset;
1121                                                         offset += strlen(string_val) + 1;
1122                                                 }
1123                                                 break;
1124                                         default:
1125                                                 elog(ERROR, "unrecognized reloption type %c",
1126                                                          options[i].gen->type);
1127                                                 break;
1128                                 }
1129                                 found = true;
1130                                 break;
1131                         }
1132                 }
1133                 if (validate && !found)
1134                         elog(ERROR, "reloption \"%s\" not found in parse table",
1135                                  options[i].gen->name);
1136         }
1137         SET_VARSIZE(rdopts, offset);
1138 }
1139
1140
1141 /*
1142  * Option parser for anything that uses StdRdOptions (i.e. fillfactor and
1143  * autovacuum)
1144  */
1145 bytea *
1146 default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
1147 {
1148         relopt_value *options;
1149         StdRdOptions *rdopts;
1150         int                     numoptions;
1151         static const relopt_parse_elt tab[] = {
1152                 {"fillfactor", RELOPT_TYPE_INT, offsetof(StdRdOptions, fillfactor)},
1153                 {"autovacuum_enabled", RELOPT_TYPE_BOOL,
1154                 offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, enabled)},
1155                 {"autovacuum_vacuum_threshold", RELOPT_TYPE_INT,
1156                 offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, vacuum_threshold)},
1157                 {"autovacuum_analyze_threshold", RELOPT_TYPE_INT,
1158                 offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, analyze_threshold)},
1159                 {"autovacuum_vacuum_cost_delay", RELOPT_TYPE_INT,
1160                 offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, vacuum_cost_delay)},
1161                 {"autovacuum_vacuum_cost_limit", RELOPT_TYPE_INT,
1162                 offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, vacuum_cost_limit)},
1163                 {"autovacuum_freeze_min_age", RELOPT_TYPE_INT,
1164                 offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, freeze_min_age)},
1165                 {"autovacuum_freeze_max_age", RELOPT_TYPE_INT,
1166                 offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, freeze_max_age)},
1167                 {"autovacuum_freeze_table_age", RELOPT_TYPE_INT,
1168                 offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, freeze_table_age)},
1169                 {"autovacuum_vacuum_scale_factor", RELOPT_TYPE_REAL,
1170                 offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, vacuum_scale_factor)},
1171                 {"autovacuum_analyze_scale_factor", RELOPT_TYPE_REAL,
1172                 offsetof(StdRdOptions, autovacuum) +offsetof(AutoVacOpts, analyze_scale_factor)},
1173                 {"security_barrier", RELOPT_TYPE_BOOL,
1174                 offsetof(StdRdOptions, security_barrier)},
1175                 {"check_option", RELOPT_TYPE_STRING,
1176                 offsetof(StdRdOptions, check_option_offset)},
1177                 {"user_catalog_table", RELOPT_TYPE_BOOL,
1178                  offsetof(StdRdOptions, user_catalog_table)}
1179         };
1180
1181         options = parseRelOptions(reloptions, validate, kind, &numoptions);
1182
1183         /* if none set, we're done */
1184         if (numoptions == 0)
1185                 return NULL;
1186
1187         rdopts = allocateReloptStruct(sizeof(StdRdOptions), options, numoptions);
1188
1189         fillRelOptions((void *) rdopts, sizeof(StdRdOptions), options, numoptions,
1190                                    validate, tab, lengthof(tab));
1191
1192         pfree(options);
1193
1194         return (bytea *) rdopts;
1195 }
1196
1197 /*
1198  * Parse options for heaps, views and toast tables.
1199  */
1200 bytea *
1201 heap_reloptions(char relkind, Datum reloptions, bool validate)
1202 {
1203         StdRdOptions *rdopts;
1204
1205         switch (relkind)
1206         {
1207                 case RELKIND_TOASTVALUE:
1208                         rdopts = (StdRdOptions *)
1209                                 default_reloptions(reloptions, validate, RELOPT_KIND_TOAST);
1210                         if (rdopts != NULL)
1211                         {
1212                                 /* adjust default-only parameters for TOAST relations */
1213                                 rdopts->fillfactor = 100;
1214                                 rdopts->autovacuum.analyze_threshold = -1;
1215                                 rdopts->autovacuum.analyze_scale_factor = -1;
1216                         }
1217                         return (bytea *) rdopts;
1218                 case RELKIND_RELATION:
1219                 case RELKIND_MATVIEW:
1220                         return default_reloptions(reloptions, validate, RELOPT_KIND_HEAP);
1221                 case RELKIND_VIEW:
1222                         return default_reloptions(reloptions, validate, RELOPT_KIND_VIEW);
1223                 default:
1224                         /* other relkinds are not supported */
1225                         return NULL;
1226         }
1227 }
1228
1229
1230 /*
1231  * Parse options for indexes.
1232  *
1233  *      amoptions       Oid of option parser
1234  *      reloptions      options as text[] datum
1235  *      validate        error flag
1236  */
1237 bytea *
1238 index_reloptions(RegProcedure amoptions, Datum reloptions, bool validate)
1239 {
1240         FmgrInfo        flinfo;
1241         FunctionCallInfoData fcinfo;
1242         Datum           result;
1243
1244         Assert(RegProcedureIsValid(amoptions));
1245
1246         /* Assume function is strict */
1247         if (!PointerIsValid(DatumGetPointer(reloptions)))
1248                 return NULL;
1249
1250         /* Can't use OidFunctionCallN because we might get a NULL result */
1251         fmgr_info(amoptions, &flinfo);
1252
1253         InitFunctionCallInfoData(fcinfo, &flinfo, 2, InvalidOid, NULL, NULL);
1254
1255         fcinfo.arg[0] = reloptions;
1256         fcinfo.arg[1] = BoolGetDatum(validate);
1257         fcinfo.argnull[0] = false;
1258         fcinfo.argnull[1] = false;
1259
1260         result = FunctionCallInvoke(&fcinfo);
1261
1262         if (fcinfo.isnull || DatumGetPointer(result) == NULL)
1263                 return NULL;
1264
1265         return DatumGetByteaP(result);
1266 }
1267
1268 /*
1269  * Option parser for attribute reloptions
1270  */
1271 bytea *
1272 attribute_reloptions(Datum reloptions, bool validate)
1273 {
1274         relopt_value *options;
1275         AttributeOpts *aopts;
1276         int                     numoptions;
1277         static const relopt_parse_elt tab[] = {
1278                 {"n_distinct", RELOPT_TYPE_REAL, offsetof(AttributeOpts, n_distinct)},
1279                 {"n_distinct_inherited", RELOPT_TYPE_REAL, offsetof(AttributeOpts, n_distinct_inherited)}
1280         };
1281
1282         options = parseRelOptions(reloptions, validate, RELOPT_KIND_ATTRIBUTE,
1283                                                           &numoptions);
1284
1285         /* if none set, we're done */
1286         if (numoptions == 0)
1287                 return NULL;
1288
1289         aopts = allocateReloptStruct(sizeof(AttributeOpts), options, numoptions);
1290
1291         fillRelOptions((void *) aopts, sizeof(AttributeOpts), options, numoptions,
1292                                    validate, tab, lengthof(tab));
1293
1294         pfree(options);
1295
1296         return (bytea *) aopts;
1297 }
1298
1299 /*
1300  * Option parser for tablespace reloptions
1301  */
1302 bytea *
1303 tablespace_reloptions(Datum reloptions, bool validate)
1304 {
1305         relopt_value *options;
1306         TableSpaceOpts *tsopts;
1307         int                     numoptions;
1308         static const relopt_parse_elt tab[] = {
1309                 {"random_page_cost", RELOPT_TYPE_REAL, offsetof(TableSpaceOpts, random_page_cost)},
1310                 {"seq_page_cost", RELOPT_TYPE_REAL, offsetof(TableSpaceOpts, seq_page_cost)}
1311         };
1312
1313         options = parseRelOptions(reloptions, validate, RELOPT_KIND_TABLESPACE,
1314                                                           &numoptions);
1315
1316         /* if none set, we're done */
1317         if (numoptions == 0)
1318                 return NULL;
1319
1320         tsopts = allocateReloptStruct(sizeof(TableSpaceOpts), options, numoptions);
1321
1322         fillRelOptions((void *) tsopts, sizeof(TableSpaceOpts), options, numoptions,
1323                                    validate, tab, lengthof(tab));
1324
1325         pfree(options);
1326
1327         return (bytea *) tsopts;
1328 }