1/*-------------------------------------------------------------------------
2 *
3 * reloptions.c
4 * Core support for relation options (pg_class.reloptions)
5 *
6 * Portions Copyright (c) 1996-2019, 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 <float.h>
19
20#include "access/gist_private.h"
21#include "access/hash.h"
22#include "access/htup_details.h"
23#include "access/nbtree.h"
24#include "access/reloptions.h"
25#include "access/spgist.h"
26#include "access/tuptoaster.h"
27#include "catalog/pg_type.h"
28#include "commands/defrem.h"
29#include "commands/tablespace.h"
30#include "commands/view.h"
31#include "nodes/makefuncs.h"
32#include "postmaster/postmaster.h"
33#include "utils/array.h"
34#include "utils/attoptcache.h"
35#include "utils/builtins.h"
36#include "utils/guc.h"
37#include "utils/memutils.h"
38#include "utils/rel.h"
39
40/*
41 * Contents of pg_class.reloptions
42 *
43 * To add an option:
44 *
45 * (i) decide on a type (integer, real, bool, string), name, default value,
46 * upper and lower bounds (if applicable); for strings, consider a validation
47 * routine.
48 * (ii) add a record below (or use add_<type>_reloption).
49 * (iii) add it to the appropriate options struct (perhaps StdRdOptions)
50 * (iv) add it to the appropriate handling routine (perhaps
51 * default_reloptions)
52 * (v) make sure the lock level is set correctly for that operation
53 * (vi) don't forget to document the option
54 *
55 * The default choice for any new option should be AccessExclusiveLock.
56 * In some cases the lock level can be reduced from there, but the lock
57 * level chosen should always conflict with itself to ensure that multiple
58 * changes aren't lost when we attempt concurrent changes.
59 * The choice of lock level depends completely upon how that parameter
60 * is used within the server, not upon how and when you'd like to change it.
61 * Safety first. Existing choices are documented here, and elsewhere in
62 * backend code where the parameters are used.
63 *
64 * In general, anything that affects the results obtained from a SELECT must be
65 * protected by AccessExclusiveLock.
66 *
67 * Autovacuum related parameters can be set at ShareUpdateExclusiveLock
68 * since they are only used by the AV procs and don't change anything
69 * currently executing.
70 *
71 * Fillfactor can be set because it applies only to subsequent changes made to
72 * data blocks, as documented in heapio.c
73 *
74 * n_distinct options can be set at ShareUpdateExclusiveLock because they
75 * are only used during ANALYZE, which uses a ShareUpdateExclusiveLock,
76 * so the ANALYZE will not be affected by in-flight changes. Changing those
77 * values has no effect until the next ANALYZE, so no need for stronger lock.
78 *
79 * Planner-related parameters can be set with ShareUpdateExclusiveLock because
80 * they only affect planning and not the correctness of the execution. Plans
81 * cannot be changed in mid-flight, so changes here could not easily result in
82 * new improved plans in any case. So we allow existing queries to continue
83 * and existing plans to survive, a small price to pay for allowing better
84 * plans to be introduced concurrently without interfering with users.
85 *
86 * Setting parallel_workers is safe, since it acts the same as
87 * max_parallel_workers_per_gather which is a USERSET parameter that doesn't
88 * affect existing plans or queries.
89 *
90 * vacuum_truncate can be set at ShareUpdateExclusiveLock because it
91 * is only used during VACUUM, which uses a ShareUpdateExclusiveLock,
92 * so the VACUUM will not be affected by in-flight changes. Changing its
93 * value has no effect until the next VACUUM, so no need for stronger lock.
94 */
95
96static relopt_bool boolRelOpts[] =
97{
98 {
99 {
100 "autosummarize",
101 "Enables automatic summarization on this BRIN index",
102 RELOPT_KIND_BRIN,
103 AccessExclusiveLock
104 },
105 false
106 },
107 {
108 {
109 "autovacuum_enabled",
110 "Enables autovacuum in this relation",
111 RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
112 ShareUpdateExclusiveLock
113 },
114 true
115 },
116 {
117 {
118 "user_catalog_table",
119 "Declare a table as an additional catalog table, e.g. for the purpose of logical replication",
120 RELOPT_KIND_HEAP,
121 AccessExclusiveLock
122 },
123 false
124 },
125 {
126 {
127 "fastupdate",
128 "Enables \"fast update\" feature for this GIN index",
129 RELOPT_KIND_GIN,
130 AccessExclusiveLock
131 },
132 true
133 },
134 {
135 {
136 "security_barrier",
137 "View acts as a row security barrier",
138 RELOPT_KIND_VIEW,
139 AccessExclusiveLock
140 },
141 false
142 },
143 {
144 {
145 "vacuum_index_cleanup",
146 "Enables index vacuuming and index cleanup",
147 RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
148 ShareUpdateExclusiveLock
149 },
150 true
151 },
152 {
153 {
154 "vacuum_truncate",
155 "Enables vacuum to truncate empty pages at the end of this table",
156 RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
157 ShareUpdateExclusiveLock
158 },
159 true
160 },
161 /* list terminator */
162 {{NULL}}
163};
164
165static relopt_int intRelOpts[] =
166{
167 {
168 {
169 "fillfactor",
170 "Packs table pages only to this percentage",
171 RELOPT_KIND_HEAP,
172 ShareUpdateExclusiveLock /* since it applies only to later
173 * inserts */
174 },
175 HEAP_DEFAULT_FILLFACTOR, HEAP_MIN_FILLFACTOR, 100
176 },
177 {
178 {
179 "fillfactor",
180 "Packs btree index pages only to this percentage",
181 RELOPT_KIND_BTREE,
182 ShareUpdateExclusiveLock /* since it applies only to later
183 * inserts */
184 },
185 BTREE_DEFAULT_FILLFACTOR, BTREE_MIN_FILLFACTOR, 100
186 },
187 {
188 {
189 "fillfactor",
190 "Packs hash index pages only to this percentage",
191 RELOPT_KIND_HASH,
192 ShareUpdateExclusiveLock /* since it applies only to later
193 * inserts */
194 },
195 HASH_DEFAULT_FILLFACTOR, HASH_MIN_FILLFACTOR, 100
196 },
197 {
198 {
199 "fillfactor",
200 "Packs gist index pages only to this percentage",
201 RELOPT_KIND_GIST,
202 ShareUpdateExclusiveLock /* since it applies only to later
203 * inserts */
204 },
205 GIST_DEFAULT_FILLFACTOR, GIST_MIN_FILLFACTOR, 100
206 },
207 {
208 {
209 "fillfactor",
210 "Packs spgist index pages only to this percentage",
211 RELOPT_KIND_SPGIST,
212 ShareUpdateExclusiveLock /* since it applies only to later
213 * inserts */
214 },
215 SPGIST_DEFAULT_FILLFACTOR, SPGIST_MIN_FILLFACTOR, 100
216 },
217 {
218 {
219 "autovacuum_vacuum_threshold",
220 "Minimum number of tuple updates or deletes prior to vacuum",
221 RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
222 ShareUpdateExclusiveLock
223 },
224 -1, 0, INT_MAX
225 },
226 {
227 {
228 "autovacuum_analyze_threshold",
229 "Minimum number of tuple inserts, updates or deletes prior to analyze",
230 RELOPT_KIND_HEAP,
231 ShareUpdateExclusiveLock
232 },
233 -1, 0, INT_MAX
234 },
235 {
236 {
237 "autovacuum_vacuum_cost_limit",
238 "Vacuum cost amount available before napping, for autovacuum",
239 RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
240 ShareUpdateExclusiveLock
241 },
242 -1, 1, 10000
243 },
244 {
245 {
246 "autovacuum_freeze_min_age",
247 "Minimum age at which VACUUM should freeze a table row, for autovacuum",
248 RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
249 ShareUpdateExclusiveLock
250 },
251 -1, 0, 1000000000
252 },
253 {
254 {
255 "autovacuum_multixact_freeze_min_age",
256 "Minimum multixact age at which VACUUM should freeze a row multixact's, for autovacuum",
257 RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
258 ShareUpdateExclusiveLock
259 },
260 -1, 0, 1000000000
261 },
262 {
263 {
264 "autovacuum_freeze_max_age",
265 "Age at which to autovacuum a table to prevent transaction ID wraparound",
266 RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
267 ShareUpdateExclusiveLock
268 },
269 -1, 100000, 2000000000
270 },
271 {
272 {
273 "autovacuum_multixact_freeze_max_age",
274 "Multixact age at which to autovacuum a table to prevent multixact wraparound",
275 RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
276 ShareUpdateExclusiveLock
277 },
278 -1, 10000, 2000000000
279 },
280 {
281 {
282 "autovacuum_freeze_table_age",
283 "Age at which VACUUM should perform a full table sweep to freeze row versions",
284 RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
285 ShareUpdateExclusiveLock
286 }, -1, 0, 2000000000
287 },
288 {
289 {
290 "autovacuum_multixact_freeze_table_age",
291 "Age of multixact at which VACUUM should perform a full table sweep to freeze row versions",
292 RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
293 ShareUpdateExclusiveLock
294 }, -1, 0, 2000000000
295 },
296 {
297 {
298 "log_autovacuum_min_duration",
299 "Sets the minimum execution time above which autovacuum actions will be logged",
300 RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
301 ShareUpdateExclusiveLock
302 },
303 -1, -1, INT_MAX
304 },
305 {
306 {
307 "toast_tuple_target",
308 "Sets the target tuple length at which external columns will be toasted",
309 RELOPT_KIND_HEAP,
310 ShareUpdateExclusiveLock
311 },
312 TOAST_TUPLE_TARGET, 128, TOAST_TUPLE_TARGET_MAIN
313 },
314 {
315 {
316 "pages_per_range",
317 "Number of pages that each page range covers in a BRIN index",
318 RELOPT_KIND_BRIN,
319 AccessExclusiveLock
320 }, 128, 1, 131072
321 },
322 {
323 {
324 "gin_pending_list_limit",
325 "Maximum size of the pending list for this GIN index, in kilobytes.",
326 RELOPT_KIND_GIN,
327 AccessExclusiveLock
328 },
329 -1, 64, MAX_KILOBYTES
330 },
331 {
332 {
333 "effective_io_concurrency",
334 "Number of simultaneous requests that can be handled efficiently by the disk subsystem.",
335 RELOPT_KIND_TABLESPACE,
336 ShareUpdateExclusiveLock
337 },
338#ifdef USE_PREFETCH
339 -1, 0, MAX_IO_CONCURRENCY
340#else
341 0, 0, 0
342#endif
343 },
344 {
345 {
346 "parallel_workers",
347 "Number of parallel processes that can be used per executor node for this relation.",
348 RELOPT_KIND_HEAP,
349 ShareUpdateExclusiveLock
350 },
351 -1, 0, 1024
352 },
353
354 /* list terminator */
355 {{NULL}}
356};
357
358static relopt_real realRelOpts[] =
359{
360 {
361 {
362 "autovacuum_vacuum_cost_delay",
363 "Vacuum cost delay in milliseconds, for autovacuum",
364 RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
365 ShareUpdateExclusiveLock
366 },
367 -1, 0.0, 100.0
368 },
369 {
370 {
371 "autovacuum_vacuum_scale_factor",
372 "Number of tuple updates or deletes prior to vacuum as a fraction of reltuples",
373 RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
374 ShareUpdateExclusiveLock
375 },
376 -1, 0.0, 100.0
377 },
378 {
379 {
380 "autovacuum_analyze_scale_factor",
381 "Number of tuple inserts, updates or deletes prior to analyze as a fraction of reltuples",
382 RELOPT_KIND_HEAP,
383 ShareUpdateExclusiveLock
384 },
385 -1, 0.0, 100.0
386 },
387 {
388 {
389 "seq_page_cost",
390 "Sets the planner's estimate of the cost of a sequentially fetched disk page.",
391 RELOPT_KIND_TABLESPACE,
392 ShareUpdateExclusiveLock
393 },
394 -1, 0.0, DBL_MAX
395 },
396 {
397 {
398 "random_page_cost",
399 "Sets the planner's estimate of the cost of a nonsequentially fetched disk page.",
400 RELOPT_KIND_TABLESPACE,
401 ShareUpdateExclusiveLock
402 },
403 -1, 0.0, DBL_MAX
404 },
405 {
406 {
407 "n_distinct",
408 "Sets the planner's estimate of the number of distinct values appearing in a column (excluding child relations).",
409 RELOPT_KIND_ATTRIBUTE,
410 ShareUpdateExclusiveLock
411 },
412 0, -1.0, DBL_MAX
413 },
414 {
415 {
416 "n_distinct_inherited",
417 "Sets the planner's estimate of the number of distinct values appearing in a column (including child relations).",
418 RELOPT_KIND_ATTRIBUTE,
419 ShareUpdateExclusiveLock
420 },
421 0, -1.0, DBL_MAX
422 },
423 {
424 {
425 "vacuum_cleanup_index_scale_factor",
426 "Number of tuple inserts prior to index cleanup as a fraction of reltuples.",
427 RELOPT_KIND_BTREE,
428 ShareUpdateExclusiveLock
429 },
430 -1, 0.0, 1e10
431 },
432 /* list terminator */
433 {{NULL}}
434};
435
436static relopt_string stringRelOpts[] =
437{
438 {
439 {
440 "buffering",
441 "Enables buffering build for this GiST index",
442 RELOPT_KIND_GIST,
443 AccessExclusiveLock
444 },
445 4,
446 false,
447 gistValidateBufferingOption,
448 "auto"
449 },
450 {
451 {
452 "check_option",
453 "View has WITH CHECK OPTION defined (local or cascaded).",
454 RELOPT_KIND_VIEW,
455 AccessExclusiveLock
456 },
457 0,
458 true,
459 validateWithCheckOption,
460 NULL
461 },
462 /* list terminator */
463 {{NULL}}
464};
465
466static relopt_gen **relOpts = NULL;
467static bits32 last_assigned_kind = RELOPT_KIND_LAST_DEFAULT;
468
469static int num_custom_options = 0;
470static relopt_gen **custom_options = NULL;
471static bool need_initialization = true;
472
473static void initialize_reloptions(void);
474static void parse_one_reloption(relopt_value *option, char *text_str,
475 int text_len, bool validate);
476
477/*
478 * initialize_reloptions
479 * initialization routine, must be called before parsing
480 *
481 * Initialize the relOpts array and fill each variable's type and name length.
482 */
483static void
484initialize_reloptions(void)
485{
486 int i;
487 int j;
488
489 j = 0;
490 for (i = 0; boolRelOpts[i].gen.name; i++)
491 {
492 Assert(DoLockModesConflict(boolRelOpts[i].gen.lockmode,
493 boolRelOpts[i].gen.lockmode));
494 j++;
495 }
496 for (i = 0; intRelOpts[i].gen.name; i++)
497 {
498 Assert(DoLockModesConflict(intRelOpts[i].gen.lockmode,
499 intRelOpts[i].gen.lockmode));
500 j++;
501 }
502 for (i = 0; realRelOpts[i].gen.name; i++)
503 {
504 Assert(DoLockModesConflict(realRelOpts[i].gen.lockmode,
505 realRelOpts[i].gen.lockmode));
506 j++;
507 }
508 for (i = 0; stringRelOpts[i].gen.name; i++)
509 {
510 Assert(DoLockModesConflict(stringRelOpts[i].gen.lockmode,
511 stringRelOpts[i].gen.lockmode));
512 j++;
513 }
514 j += num_custom_options;
515
516 if (relOpts)
517 pfree(relOpts);
518 relOpts = MemoryContextAlloc(TopMemoryContext,
519 (j + 1) * sizeof(relopt_gen *));
520
521 j = 0;
522 for (i = 0; boolRelOpts[i].gen.name; i++)
523 {
524 relOpts[j] = &boolRelOpts[i].gen;
525 relOpts[j]->type = RELOPT_TYPE_BOOL;
526 relOpts[j]->namelen = strlen(relOpts[j]->name);
527 j++;
528 }
529
530 for (i = 0; intRelOpts[i].gen.name; i++)
531 {
532 relOpts[j] = &intRelOpts[i].gen;
533 relOpts[j]->type = RELOPT_TYPE_INT;
534 relOpts[j]->namelen = strlen(relOpts[j]->name);
535 j++;
536 }
537
538 for (i = 0; realRelOpts[i].gen.name; i++)
539 {
540 relOpts[j] = &realRelOpts[i].gen;
541 relOpts[j]->type = RELOPT_TYPE_REAL;
542 relOpts[j]->namelen = strlen(relOpts[j]->name);
543 j++;
544 }
545
546 for (i = 0; stringRelOpts[i].gen.name; i++)
547 {
548 relOpts[j] = &stringRelOpts[i].gen;
549 relOpts[j]->type = RELOPT_TYPE_STRING;
550 relOpts[j]->namelen = strlen(relOpts[j]->name);
551 j++;
552 }
553
554 for (i = 0; i < num_custom_options; i++)
555 {
556 relOpts[j] = custom_options[i];
557 j++;
558 }
559
560 /* add a list terminator */
561 relOpts[j] = NULL;
562
563 /* flag the work is complete */
564 need_initialization = false;
565}
566
567/*
568 * add_reloption_kind
569 * Create a new relopt_kind value, to be used in custom reloptions by
570 * user-defined AMs.
571 */
572relopt_kind
573add_reloption_kind(void)
574{
575 /* don't hand out the last bit so that the enum's behavior is portable */
576 if (last_assigned_kind >= RELOPT_KIND_MAX)
577 ereport(ERROR,
578 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
579 errmsg("user-defined relation parameter types limit exceeded")));
580 last_assigned_kind <<= 1;
581 return (relopt_kind) last_assigned_kind;
582}
583
584/*
585 * add_reloption
586 * Add an already-created custom reloption to the list, and recompute the
587 * main parser table.
588 */
589static void
590add_reloption(relopt_gen *newoption)
591{
592 static int max_custom_options = 0;
593
594 if (num_custom_options >= max_custom_options)
595 {
596 MemoryContext oldcxt;
597
598 oldcxt = MemoryContextSwitchTo(TopMemoryContext);
599
600 if (max_custom_options == 0)
601 {
602 max_custom_options = 8;
603 custom_options = palloc(max_custom_options * sizeof(relopt_gen *));
604 }
605 else
606 {
607 max_custom_options *= 2;
608 custom_options = repalloc(custom_options,
609 max_custom_options * sizeof(relopt_gen *));
610 }
611 MemoryContextSwitchTo(oldcxt);
612 }
613 custom_options[num_custom_options++] = newoption;
614
615 need_initialization = true;
616}
617
618/*
619 * allocate_reloption
620 * Allocate a new reloption and initialize the type-agnostic fields
621 * (for types other than string)
622 */
623static relopt_gen *
624allocate_reloption(bits32 kinds, int type, const char *name, const char *desc)
625{
626 MemoryContext oldcxt;
627 size_t size;
628 relopt_gen *newoption;
629
630 oldcxt = MemoryContextSwitchTo(TopMemoryContext);
631
632 switch (type)
633 {
634 case RELOPT_TYPE_BOOL:
635 size = sizeof(relopt_bool);
636 break;
637 case RELOPT_TYPE_INT:
638 size = sizeof(relopt_int);
639 break;
640 case RELOPT_TYPE_REAL:
641 size = sizeof(relopt_real);
642 break;
643 case RELOPT_TYPE_STRING:
644 size = sizeof(relopt_string);
645 break;
646 default:
647 elog(ERROR, "unsupported reloption type %d", type);
648 return NULL; /* keep compiler quiet */
649 }
650
651 newoption = palloc(size);
652
653 newoption->name = pstrdup(name);
654 if (desc)
655 newoption->desc = pstrdup(desc);
656 else
657 newoption->desc = NULL;
658 newoption->kinds = kinds;
659 newoption->namelen = strlen(name);
660 newoption->type = type;
661
662 /*
663 * Set the default lock mode for this option. There is no actual way
664 * for a module to enforce it when declaring a custom relation option,
665 * so just use the highest level, which is safe for all cases.
666 */
667 newoption->lockmode = AccessExclusiveLock;
668
669 MemoryContextSwitchTo(oldcxt);
670
671 return newoption;
672}
673
674/*
675 * add_bool_reloption
676 * Add a new boolean reloption
677 */
678void
679add_bool_reloption(bits32 kinds, const char *name, const char *desc, bool default_val)
680{
681 relopt_bool *newoption;
682
683 newoption = (relopt_bool *) allocate_reloption(kinds, RELOPT_TYPE_BOOL,
684 name, desc);
685 newoption->default_val = default_val;
686
687 add_reloption((relopt_gen *) newoption);
688}
689
690/*
691 * add_int_reloption
692 * Add a new integer reloption
693 */
694void
695add_int_reloption(bits32 kinds, const char *name, const char *desc, int default_val,
696 int min_val, int max_val)
697{
698 relopt_int *newoption;
699
700 newoption = (relopt_int *) allocate_reloption(kinds, RELOPT_TYPE_INT,
701 name, desc);
702 newoption->default_val = default_val;
703 newoption->min = min_val;
704 newoption->max = max_val;
705
706 add_reloption((relopt_gen *) newoption);
707}
708
709/*
710 * add_real_reloption
711 * Add a new float reloption
712 */
713void
714add_real_reloption(bits32 kinds, const char *name, const char *desc, double default_val,
715 double min_val, double max_val)
716{
717 relopt_real *newoption;
718
719 newoption = (relopt_real *) allocate_reloption(kinds, RELOPT_TYPE_REAL,
720 name, desc);
721 newoption->default_val = default_val;
722 newoption->min = min_val;
723 newoption->max = max_val;
724
725 add_reloption((relopt_gen *) newoption);
726}
727
728/*
729 * add_string_reloption
730 * Add a new string reloption
731 *
732 * "validator" is an optional function pointer that can be used to test the
733 * validity of the values. It must elog(ERROR) when the argument string is
734 * not acceptable for the variable. Note that the default value must pass
735 * the validation.
736 */
737void
738add_string_reloption(bits32 kinds, const char *name, const char *desc, const char *default_val,
739 validate_string_relopt validator)
740{
741 relopt_string *newoption;
742
743 /* make sure the validator/default combination is sane */
744 if (validator)
745 (validator) (default_val);
746
747 newoption = (relopt_string *) allocate_reloption(kinds, RELOPT_TYPE_STRING,
748 name, desc);
749 newoption->validate_cb = validator;
750 if (default_val)
751 {
752 newoption->default_val = MemoryContextStrdup(TopMemoryContext,
753 default_val);
754 newoption->default_len = strlen(default_val);
755 newoption->default_isnull = false;
756 }
757 else
758 {
759 newoption->default_val = "";
760 newoption->default_len = 0;
761 newoption->default_isnull = true;
762 }
763
764 add_reloption((relopt_gen *) newoption);
765}
766
767/*
768 * Transform a relation options list (list of DefElem) into the text array
769 * format that is kept in pg_class.reloptions, including only those options
770 * that are in the passed namespace. The output values do not include the
771 * namespace.
772 *
773 * This is used for three cases: CREATE TABLE/INDEX, ALTER TABLE SET, and
774 * ALTER TABLE RESET. In the ALTER cases, oldOptions is the existing
775 * reloptions value (possibly NULL), and we replace or remove entries
776 * as needed.
777 *
778 * If acceptOidsOff is true, then we allow oids = false, but throw error when
779 * on. This is solely needed for backwards compatibility.
780 *
781 * Note that this is not responsible for determining whether the options
782 * are valid, but it does check that namespaces for all the options given are
783 * listed in validnsps. The NULL namespace is always valid and need not be
784 * explicitly listed. Passing a NULL pointer means that only the NULL
785 * namespace is valid.
786 *
787 * Both oldOptions and the result are text arrays (or NULL for "default"),
788 * but we declare them as Datums to avoid including array.h in reloptions.h.
789 */
790Datum
791transformRelOptions(Datum oldOptions, List *defList, const char *namspace,
792 char *validnsps[], bool acceptOidsOff, bool isReset)
793{
794 Datum result;
795 ArrayBuildState *astate;
796 ListCell *cell;
797
798 /* no change if empty list */
799 if (defList == NIL)
800 return oldOptions;
801
802 /* We build new array using accumArrayResult */
803 astate = NULL;
804
805 /* Copy any oldOptions that aren't to be replaced */
806 if (PointerIsValid(DatumGetPointer(oldOptions)))
807 {
808 ArrayType *array = DatumGetArrayTypeP(oldOptions);
809 Datum *oldoptions;
810 int noldoptions;
811 int i;
812
813 deconstruct_array(array, TEXTOID, -1, false, 'i',
814 &oldoptions, NULL, &noldoptions);
815
816 for (i = 0; i < noldoptions; i++)
817 {
818 char *text_str = VARDATA(oldoptions[i]);
819 int text_len = VARSIZE(oldoptions[i]) - VARHDRSZ;
820
821 /* Search for a match in defList */
822 foreach(cell, defList)
823 {
824 DefElem *def = (DefElem *) lfirst(cell);
825 int kw_len;
826
827 /* ignore if not in the same namespace */
828 if (namspace == NULL)
829 {
830 if (def->defnamespace != NULL)
831 continue;
832 }
833 else if (def->defnamespace == NULL)
834 continue;
835 else if (strcmp(def->defnamespace, namspace) != 0)
836 continue;
837
838 kw_len = strlen(def->defname);
839 if (text_len > kw_len && text_str[kw_len] == '=' &&
840 strncmp(text_str, def->defname, kw_len) == 0)
841 break;
842 }
843 if (!cell)
844 {
845 /* No match, so keep old option */
846 astate = accumArrayResult(astate, oldoptions[i],
847 false, TEXTOID,
848 CurrentMemoryContext);
849 }
850 }
851 }
852
853 /*
854 * If CREATE/SET, add new options to array; if RESET, just check that the
855 * user didn't say RESET (option=val). (Must do this because the grammar
856 * doesn't enforce it.)
857 */
858 foreach(cell, defList)
859 {
860 DefElem *def = (DefElem *) lfirst(cell);
861
862 if (isReset)
863 {
864 if (def->arg != NULL)
865 ereport(ERROR,
866 (errcode(ERRCODE_SYNTAX_ERROR),
867 errmsg("RESET must not include values for parameters")));
868 }
869 else
870 {
871 text *t;
872 const char *value;
873 Size len;
874
875 /*
876 * Error out if the namespace is not valid. A NULL namespace is
877 * always valid.
878 */
879 if (def->defnamespace != NULL)
880 {
881 bool valid = false;
882 int i;
883
884 if (validnsps)
885 {
886 for (i = 0; validnsps[i]; i++)
887 {
888 if (strcmp(def->defnamespace, validnsps[i]) == 0)
889 {
890 valid = true;
891 break;
892 }
893 }
894 }
895
896 if (!valid)
897 ereport(ERROR,
898 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
899 errmsg("unrecognized parameter namespace \"%s\"",
900 def->defnamespace)));
901 }
902
903 /* ignore if not in the same namespace */
904 if (namspace == NULL)
905 {
906 if (def->defnamespace != NULL)
907 continue;
908 }
909 else if (def->defnamespace == NULL)
910 continue;
911 else if (strcmp(def->defnamespace, namspace) != 0)
912 continue;
913
914 /*
915 * Flatten the DefElem into a text string like "name=arg". If we
916 * have just "name", assume "name=true" is meant. Note: the
917 * namespace is not output.
918 */
919 if (def->arg != NULL)
920 value = defGetString(def);
921 else
922 value = "true";
923
924 /*
925 * This is not a great place for this test, but there's no other
926 * convenient place to filter the option out. As WITH (oids =
927 * false) will be removed someday, this seems like an acceptable
928 * amount of ugly.
929 */
930 if (acceptOidsOff && def->defnamespace == NULL &&
931 strcmp(def->defname, "oids") == 0)
932 {
933 if (defGetBoolean(def))
934 ereport(ERROR,
935 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
936 errmsg("tables declared WITH OIDS are not supported")));
937 /* skip over option, reloptions machinery doesn't know it */
938 continue;
939 }
940
941 len = VARHDRSZ + strlen(def->defname) + 1 + strlen(value);
942 /* +1 leaves room for sprintf's trailing null */
943 t = (text *) palloc(len + 1);
944 SET_VARSIZE(t, len);
945 sprintf(VARDATA(t), "%s=%s", def->defname, value);
946
947 astate = accumArrayResult(astate, PointerGetDatum(t),
948 false, TEXTOID,
949 CurrentMemoryContext);
950 }
951 }
952
953 if (astate)
954 result = makeArrayResult(astate, CurrentMemoryContext);
955 else
956 result = (Datum) 0;
957
958 return result;
959}
960
961
962/*
963 * Convert the text-array format of reloptions into a List of DefElem.
964 * This is the inverse of transformRelOptions().
965 */
966List *
967untransformRelOptions(Datum options)
968{
969 List *result = NIL;
970 ArrayType *array;
971 Datum *optiondatums;
972 int noptions;
973 int i;
974
975 /* Nothing to do if no options */
976 if (!PointerIsValid(DatumGetPointer(options)))
977 return result;
978
979 array = DatumGetArrayTypeP(options);
980
981 deconstruct_array(array, TEXTOID, -1, false, 'i',
982 &optiondatums, NULL, &noptions);
983
984 for (i = 0; i < noptions; i++)
985 {
986 char *s;
987 char *p;
988 Node *val = NULL;
989
990 s = TextDatumGetCString(optiondatums[i]);
991 p = strchr(s, '=');
992 if (p)
993 {
994 *p++ = '\0';
995 val = (Node *) makeString(pstrdup(p));
996 }
997 result = lappend(result, makeDefElem(pstrdup(s), val, -1));
998 }
999
1000 return result;
1001}
1002
1003/*
1004 * Extract and parse reloptions from a pg_class tuple.
1005 *
1006 * This is a low-level routine, expected to be used by relcache code and
1007 * callers that do not have a table's relcache entry (e.g. autovacuum). For
1008 * other uses, consider grabbing the rd_options pointer from the relcache entry
1009 * instead.
1010 *
1011 * tupdesc is pg_class' tuple descriptor. amoptions is a pointer to the index
1012 * AM's options parser function in the case of a tuple corresponding to an
1013 * index, or NULL otherwise.
1014 */
1015bytea *
1016extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
1017 amoptions_function amoptions)
1018{
1019 bytea *options;
1020 bool isnull;
1021 Datum datum;
1022 Form_pg_class classForm;
1023
1024 datum = fastgetattr(tuple,
1025 Anum_pg_class_reloptions,
1026 tupdesc,
1027 &isnull);
1028 if (isnull)
1029 return NULL;
1030
1031 classForm = (Form_pg_class) GETSTRUCT(tuple);
1032
1033 /* Parse into appropriate format; don't error out here */
1034 switch (classForm->relkind)
1035 {
1036 case RELKIND_RELATION:
1037 case RELKIND_TOASTVALUE:
1038 case RELKIND_MATVIEW:
1039 case RELKIND_PARTITIONED_TABLE:
1040 options = heap_reloptions(classForm->relkind, datum, false);
1041 break;
1042 case RELKIND_VIEW:
1043 options = view_reloptions(datum, false);
1044 break;
1045 case RELKIND_INDEX:
1046 case RELKIND_PARTITIONED_INDEX:
1047 options = index_reloptions(amoptions, datum, false);
1048 break;
1049 case RELKIND_FOREIGN_TABLE:
1050 options = NULL;
1051 break;
1052 default:
1053 Assert(false); /* can't get here */
1054 options = NULL; /* keep compiler quiet */
1055 break;
1056 }
1057
1058 return options;
1059}
1060
1061/*
1062 * Interpret reloptions that are given in text-array format.
1063 *
1064 * options is a reloption text array as constructed by transformRelOptions.
1065 * kind specifies the family of options to be processed.
1066 *
1067 * The return value is a relopt_value * array on which the options actually
1068 * set in the options array are marked with isset=true. The length of this
1069 * array is returned in *numrelopts. Options not set are also present in the
1070 * array; this is so that the caller can easily locate the default values.
1071 *
1072 * If there are no options of the given kind, numrelopts is set to 0 and NULL
1073 * is returned (unless options are illegally supplied despite none being
1074 * defined, in which case an error occurs).
1075 *
1076 * Note: values of type int, bool and real are allocated as part of the
1077 * returned array. Values of type string are allocated separately and must
1078 * be freed by the caller.
1079 */
1080relopt_value *
1081parseRelOptions(Datum options, bool validate, relopt_kind kind,
1082 int *numrelopts)
1083{
1084 relopt_value *reloptions = NULL;
1085 int numoptions = 0;
1086 int i;
1087 int j;
1088
1089 if (need_initialization)
1090 initialize_reloptions();
1091
1092 /* Build a list of expected options, based on kind */
1093
1094 for (i = 0; relOpts[i]; i++)
1095 if (relOpts[i]->kinds & kind)
1096 numoptions++;
1097
1098 if (numoptions > 0)
1099 {
1100 reloptions = palloc(numoptions * sizeof(relopt_value));
1101
1102 for (i = 0, j = 0; relOpts[i]; i++)
1103 {
1104 if (relOpts[i]->kinds & kind)
1105 {
1106 reloptions[j].gen = relOpts[i];
1107 reloptions[j].isset = false;
1108 j++;
1109 }
1110 }
1111 }
1112
1113 /* Done if no options */
1114 if (PointerIsValid(DatumGetPointer(options)))
1115 {
1116 ArrayType *array = DatumGetArrayTypeP(options);
1117 Datum *optiondatums;
1118 int noptions;
1119
1120 deconstruct_array(array, TEXTOID, -1, false, 'i',
1121 &optiondatums, NULL, &noptions);
1122
1123 for (i = 0; i < noptions; i++)
1124 {
1125 char *text_str = VARDATA(optiondatums[i]);
1126 int text_len = VARSIZE(optiondatums[i]) - VARHDRSZ;
1127 int j;
1128
1129 /* Search for a match in reloptions */
1130 for (j = 0; j < numoptions; j++)
1131 {
1132 int kw_len = reloptions[j].gen->namelen;
1133
1134 if (text_len > kw_len && text_str[kw_len] == '=' &&
1135 strncmp(text_str, reloptions[j].gen->name, kw_len) == 0)
1136 {
1137 parse_one_reloption(&reloptions[j], text_str, text_len,
1138 validate);
1139 break;
1140 }
1141 }
1142
1143 if (j >= numoptions && validate)
1144 {
1145 char *s;
1146 char *p;
1147
1148 s = TextDatumGetCString(optiondatums[i]);
1149 p = strchr(s, '=');
1150 if (p)
1151 *p = '\0';
1152 ereport(ERROR,
1153 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1154 errmsg("unrecognized parameter \"%s\"", s)));
1155 }
1156 }
1157
1158 /* It's worth avoiding memory leaks in this function */
1159 pfree(optiondatums);
1160 if (((void *) array) != DatumGetPointer(options))
1161 pfree(array);
1162 }
1163
1164 *numrelopts = numoptions;
1165 return reloptions;
1166}
1167
1168/*
1169 * Subroutine for parseRelOptions, to parse and validate a single option's
1170 * value
1171 */
1172static void
1173parse_one_reloption(relopt_value *option, char *text_str, int text_len,
1174 bool validate)
1175{
1176 char *value;
1177 int value_len;
1178 bool parsed;
1179 bool nofree = false;
1180
1181 if (option->isset && validate)
1182 ereport(ERROR,
1183 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1184 errmsg("parameter \"%s\" specified more than once",
1185 option->gen->name)));
1186
1187 value_len = text_len - option->gen->namelen - 1;
1188 value = (char *) palloc(value_len + 1);
1189 memcpy(value, text_str + option->gen->namelen + 1, value_len);
1190 value[value_len] = '\0';
1191
1192 switch (option->gen->type)
1193 {
1194 case RELOPT_TYPE_BOOL:
1195 {
1196 parsed = parse_bool(value, &option->values.bool_val);
1197 if (validate && !parsed)
1198 ereport(ERROR,
1199 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1200 errmsg("invalid value for boolean option \"%s\": %s",
1201 option->gen->name, value)));
1202 }
1203 break;
1204 case RELOPT_TYPE_INT:
1205 {
1206 relopt_int *optint = (relopt_int *) option->gen;
1207
1208 parsed = parse_int(value, &option->values.int_val, 0, NULL);
1209 if (validate && !parsed)
1210 ereport(ERROR,
1211 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1212 errmsg("invalid value for integer option \"%s\": %s",
1213 option->gen->name, value)));
1214 if (validate && (option->values.int_val < optint->min ||
1215 option->values.int_val > optint->max))
1216 ereport(ERROR,
1217 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1218 errmsg("value %s out of bounds for option \"%s\"",
1219 value, option->gen->name),
1220 errdetail("Valid values are between \"%d\" and \"%d\".",
1221 optint->min, optint->max)));
1222 }
1223 break;
1224 case RELOPT_TYPE_REAL:
1225 {
1226 relopt_real *optreal = (relopt_real *) option->gen;
1227
1228 parsed = parse_real(value, &option->values.real_val, 0, NULL);
1229 if (validate && !parsed)
1230 ereport(ERROR,
1231 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1232 errmsg("invalid value for floating point option \"%s\": %s",
1233 option->gen->name, value)));
1234 if (validate && (option->values.real_val < optreal->min ||
1235 option->values.real_val > optreal->max))
1236 ereport(ERROR,
1237 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1238 errmsg("value %s out of bounds for option \"%s\"",
1239 value, option->gen->name),
1240 errdetail("Valid values are between \"%f\" and \"%f\".",
1241 optreal->min, optreal->max)));
1242 }
1243 break;
1244 case RELOPT_TYPE_STRING:
1245 {
1246 relopt_string *optstring = (relopt_string *) option->gen;
1247
1248 option->values.string_val = value;
1249 nofree = true;
1250 if (validate && optstring->validate_cb)
1251 (optstring->validate_cb) (value);
1252 parsed = true;
1253 }
1254 break;
1255 default:
1256 elog(ERROR, "unsupported reloption type %d", option->gen->type);
1257 parsed = true; /* quiet compiler */
1258 break;
1259 }
1260
1261 if (parsed)
1262 option->isset = true;
1263 if (!nofree)
1264 pfree(value);
1265}
1266
1267/*
1268 * Given the result from parseRelOptions, allocate a struct that's of the
1269 * specified base size plus any extra space that's needed for string variables.
1270 *
1271 * "base" should be sizeof(struct) of the reloptions struct (StdRdOptions or
1272 * equivalent).
1273 */
1274void *
1275allocateReloptStruct(Size base, relopt_value *options, int numoptions)
1276{
1277 Size size = base;
1278 int i;
1279
1280 for (i = 0; i < numoptions; i++)
1281 if (options[i].gen->type == RELOPT_TYPE_STRING)
1282 size += GET_STRING_RELOPTION_LEN(options[i]) + 1;
1283
1284 return palloc0(size);
1285}
1286
1287/*
1288 * Given the result of parseRelOptions and a parsing table, fill in the
1289 * struct (previously allocated with allocateReloptStruct) with the parsed
1290 * values.
1291 *
1292 * rdopts is the pointer to the allocated struct to be filled.
1293 * basesize is the sizeof(struct) that was passed to allocateReloptStruct.
1294 * options, of length numoptions, is parseRelOptions' output.
1295 * elems, of length numelems, is the table describing the allowed options.
1296 * When validate is true, it is expected that all options appear in elems.
1297 */
1298void
1299fillRelOptions(void *rdopts, Size basesize,
1300 relopt_value *options, int numoptions,
1301 bool validate,
1302 const relopt_parse_elt *elems, int numelems)
1303{
1304 int i;
1305 int offset = basesize;
1306
1307 for (i = 0; i < numoptions; i++)
1308 {
1309 int j;
1310 bool found = false;
1311
1312 for (j = 0; j < numelems; j++)
1313 {
1314 if (strcmp(options[i].gen->name, elems[j].optname) == 0)
1315 {
1316 relopt_string *optstring;
1317 char *itempos = ((char *) rdopts) + elems[j].offset;
1318 char *string_val;
1319
1320 switch (options[i].gen->type)
1321 {
1322 case RELOPT_TYPE_BOOL:
1323 *(bool *) itempos = options[i].isset ?
1324 options[i].values.bool_val :
1325 ((relopt_bool *) options[i].gen)->default_val;
1326 break;
1327 case RELOPT_TYPE_INT:
1328 *(int *) itempos = options[i].isset ?
1329 options[i].values.int_val :
1330 ((relopt_int *) options[i].gen)->default_val;
1331 break;
1332 case RELOPT_TYPE_REAL:
1333 *(double *) itempos = options[i].isset ?
1334 options[i].values.real_val :
1335 ((relopt_real *) options[i].gen)->default_val;
1336 break;
1337 case RELOPT_TYPE_STRING:
1338 optstring = (relopt_string *) options[i].gen;
1339 if (options[i].isset)
1340 string_val = options[i].values.string_val;
1341 else if (!optstring->default_isnull)
1342 string_val = optstring->default_val;
1343 else
1344 string_val = NULL;
1345
1346 if (string_val == NULL)
1347 *(int *) itempos = 0;
1348 else
1349 {
1350 strcpy((char *) rdopts + offset, string_val);
1351 *(int *) itempos = offset;
1352 offset += strlen(string_val) + 1;
1353 }
1354 break;
1355 default:
1356 elog(ERROR, "unsupported reloption type %d",
1357 options[i].gen->type);
1358 break;
1359 }
1360 found = true;
1361 break;
1362 }
1363 }
1364 if (validate && !found)
1365 elog(ERROR, "reloption \"%s\" not found in parse table",
1366 options[i].gen->name);
1367 }
1368 SET_VARSIZE(rdopts, offset);
1369}
1370
1371
1372/*
1373 * Option parser for anything that uses StdRdOptions.
1374 */
1375bytea *
1376default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
1377{
1378 relopt_value *options;
1379 StdRdOptions *rdopts;
1380 int numoptions;
1381 static const relopt_parse_elt tab[] = {
1382 {"fillfactor", RELOPT_TYPE_INT, offsetof(StdRdOptions, fillfactor)},
1383 {"autovacuum_enabled", RELOPT_TYPE_BOOL,
1384 offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, enabled)},
1385 {"autovacuum_vacuum_threshold", RELOPT_TYPE_INT,
1386 offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_threshold)},
1387 {"autovacuum_analyze_threshold", RELOPT_TYPE_INT,
1388 offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, analyze_threshold)},
1389 {"autovacuum_vacuum_cost_limit", RELOPT_TYPE_INT,
1390 offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_cost_limit)},
1391 {"autovacuum_freeze_min_age", RELOPT_TYPE_INT,
1392 offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_min_age)},
1393 {"autovacuum_freeze_max_age", RELOPT_TYPE_INT,
1394 offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_max_age)},
1395 {"autovacuum_freeze_table_age", RELOPT_TYPE_INT,
1396 offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_table_age)},
1397 {"autovacuum_multixact_freeze_min_age", RELOPT_TYPE_INT,
1398 offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_min_age)},
1399 {"autovacuum_multixact_freeze_max_age", RELOPT_TYPE_INT,
1400 offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_max_age)},
1401 {"autovacuum_multixact_freeze_table_age", RELOPT_TYPE_INT,
1402 offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_table_age)},
1403 {"log_autovacuum_min_duration", RELOPT_TYPE_INT,
1404 offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, log_min_duration)},
1405 {"toast_tuple_target", RELOPT_TYPE_INT,
1406 offsetof(StdRdOptions, toast_tuple_target)},
1407 {"autovacuum_vacuum_cost_delay", RELOPT_TYPE_REAL,
1408 offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_cost_delay)},
1409 {"autovacuum_vacuum_scale_factor", RELOPT_TYPE_REAL,
1410 offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_scale_factor)},
1411 {"autovacuum_analyze_scale_factor", RELOPT_TYPE_REAL,
1412 offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, analyze_scale_factor)},
1413 {"user_catalog_table", RELOPT_TYPE_BOOL,
1414 offsetof(StdRdOptions, user_catalog_table)},
1415 {"parallel_workers", RELOPT_TYPE_INT,
1416 offsetof(StdRdOptions, parallel_workers)},
1417 {"vacuum_cleanup_index_scale_factor", RELOPT_TYPE_REAL,
1418 offsetof(StdRdOptions, vacuum_cleanup_index_scale_factor)},
1419 {"vacuum_index_cleanup", RELOPT_TYPE_BOOL,
1420 offsetof(StdRdOptions, vacuum_index_cleanup)},
1421 {"vacuum_truncate", RELOPT_TYPE_BOOL,
1422 offsetof(StdRdOptions, vacuum_truncate)}
1423 };
1424
1425 options = parseRelOptions(reloptions, validate, kind, &numoptions);
1426
1427 /* if none set, we're done */
1428 if (numoptions == 0)
1429 return NULL;
1430
1431 rdopts = allocateReloptStruct(sizeof(StdRdOptions), options, numoptions);
1432
1433 fillRelOptions((void *) rdopts, sizeof(StdRdOptions), options, numoptions,
1434 validate, tab, lengthof(tab));
1435
1436 pfree(options);
1437
1438 return (bytea *) rdopts;
1439}
1440
1441/*
1442 * Option parser for views
1443 */
1444bytea *
1445view_reloptions(Datum reloptions, bool validate)
1446{
1447 relopt_value *options;
1448 ViewOptions *vopts;
1449 int numoptions;
1450 static const relopt_parse_elt tab[] = {
1451 {"security_barrier", RELOPT_TYPE_BOOL,
1452 offsetof(ViewOptions, security_barrier)},
1453 {"check_option", RELOPT_TYPE_STRING,
1454 offsetof(ViewOptions, check_option_offset)}
1455 };
1456
1457 options = parseRelOptions(reloptions, validate, RELOPT_KIND_VIEW, &numoptions);
1458
1459 /* if none set, we're done */
1460 if (numoptions == 0)
1461 return NULL;
1462
1463 vopts = allocateReloptStruct(sizeof(ViewOptions), options, numoptions);
1464
1465 fillRelOptions((void *) vopts, sizeof(ViewOptions), options, numoptions,
1466 validate, tab, lengthof(tab));
1467
1468 pfree(options);
1469
1470 return (bytea *) vopts;
1471}
1472
1473/*
1474 * Parse options for heaps, views and toast tables.
1475 */
1476bytea *
1477heap_reloptions(char relkind, Datum reloptions, bool validate)
1478{
1479 StdRdOptions *rdopts;
1480
1481 switch (relkind)
1482 {
1483 case RELKIND_TOASTVALUE:
1484 rdopts = (StdRdOptions *)
1485 default_reloptions(reloptions, validate, RELOPT_KIND_TOAST);
1486 if (rdopts != NULL)
1487 {
1488 /* adjust default-only parameters for TOAST relations */
1489 rdopts->fillfactor = 100;
1490 rdopts->autovacuum.analyze_threshold = -1;
1491 rdopts->autovacuum.analyze_scale_factor = -1;
1492 }
1493 return (bytea *) rdopts;
1494 case RELKIND_RELATION:
1495 case RELKIND_MATVIEW:
1496 return default_reloptions(reloptions, validate, RELOPT_KIND_HEAP);
1497 case RELKIND_PARTITIONED_TABLE:
1498 return default_reloptions(reloptions, validate,
1499 RELOPT_KIND_PARTITIONED);
1500 default:
1501 /* other relkinds are not supported */
1502 return NULL;
1503 }
1504}
1505
1506
1507/*
1508 * Parse options for indexes.
1509 *
1510 * amoptions index AM's option parser function
1511 * reloptions options as text[] datum
1512 * validate error flag
1513 */
1514bytea *
1515index_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
1516{
1517 Assert(amoptions != NULL);
1518
1519 /* Assume function is strict */
1520 if (!PointerIsValid(DatumGetPointer(reloptions)))
1521 return NULL;
1522
1523 return amoptions(reloptions, validate);
1524}
1525
1526/*
1527 * Option parser for attribute reloptions
1528 */
1529bytea *
1530attribute_reloptions(Datum reloptions, bool validate)
1531{
1532 relopt_value *options;
1533 AttributeOpts *aopts;
1534 int numoptions;
1535 static const relopt_parse_elt tab[] = {
1536 {"n_distinct", RELOPT_TYPE_REAL, offsetof(AttributeOpts, n_distinct)},
1537 {"n_distinct_inherited", RELOPT_TYPE_REAL, offsetof(AttributeOpts, n_distinct_inherited)}
1538 };
1539
1540 options = parseRelOptions(reloptions, validate, RELOPT_KIND_ATTRIBUTE,
1541 &numoptions);
1542
1543 /* if none set, we're done */
1544 if (numoptions == 0)
1545 return NULL;
1546
1547 aopts = allocateReloptStruct(sizeof(AttributeOpts), options, numoptions);
1548
1549 fillRelOptions((void *) aopts, sizeof(AttributeOpts), options, numoptions,
1550 validate, tab, lengthof(tab));
1551
1552 pfree(options);
1553
1554 return (bytea *) aopts;
1555}
1556
1557/*
1558 * Option parser for tablespace reloptions
1559 */
1560bytea *
1561tablespace_reloptions(Datum reloptions, bool validate)
1562{
1563 relopt_value *options;
1564 TableSpaceOpts *tsopts;
1565 int numoptions;
1566 static const relopt_parse_elt tab[] = {
1567 {"random_page_cost", RELOPT_TYPE_REAL, offsetof(TableSpaceOpts, random_page_cost)},
1568 {"seq_page_cost", RELOPT_TYPE_REAL, offsetof(TableSpaceOpts, seq_page_cost)},
1569 {"effective_io_concurrency", RELOPT_TYPE_INT, offsetof(TableSpaceOpts, effective_io_concurrency)}
1570 };
1571
1572 options = parseRelOptions(reloptions, validate, RELOPT_KIND_TABLESPACE,
1573 &numoptions);
1574
1575 /* if none set, we're done */
1576 if (numoptions == 0)
1577 return NULL;
1578
1579 tsopts = allocateReloptStruct(sizeof(TableSpaceOpts), options, numoptions);
1580
1581 fillRelOptions((void *) tsopts, sizeof(TableSpaceOpts), options, numoptions,
1582 validate, tab, lengthof(tab));
1583
1584 pfree(options);
1585
1586 return (bytea *) tsopts;
1587}
1588
1589/*
1590 * Determine the required LOCKMODE from an option list.
1591 *
1592 * Called from AlterTableGetLockLevel(), see that function
1593 * for a longer explanation of how this works.
1594 */
1595LOCKMODE
1596AlterTableGetRelOptionsLockLevel(List *defList)
1597{
1598 LOCKMODE lockmode = NoLock;
1599 ListCell *cell;
1600
1601 if (defList == NIL)
1602 return AccessExclusiveLock;
1603
1604 if (need_initialization)
1605 initialize_reloptions();
1606
1607 foreach(cell, defList)
1608 {
1609 DefElem *def = (DefElem *) lfirst(cell);
1610 int i;
1611
1612 for (i = 0; relOpts[i]; i++)
1613 {
1614 if (strncmp(relOpts[i]->name,
1615 def->defname,
1616 relOpts[i]->namelen + 1) == 0)
1617 {
1618 if (lockmode < relOpts[i]->lockmode)
1619 lockmode = relOpts[i]->lockmode;
1620 }
1621 }
1622 }
1623
1624 return lockmode;
1625}
1626