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