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 
96 static 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 
165 static 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 
358 static 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 
436 static 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 
466 static relopt_gen **relOpts = NULL;
467 static bits32 last_assigned_kind = RELOPT_KIND_LAST_DEFAULT;
468 
469 static int	num_custom_options = 0;
470 static relopt_gen **custom_options = NULL;
471 static bool need_initialization = true;
472 
473 static void initialize_reloptions(void);
474 static 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  */
483 static void
initialize_reloptions(void)484 initialize_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  */
572 relopt_kind
add_reloption_kind(void)573 add_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  */
589 static void
add_reloption(relopt_gen * newoption)590 add_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  */
623 static relopt_gen *
allocate_reloption(bits32 kinds,int type,const char * name,const char * desc)624 allocate_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  */
678 void
add_bool_reloption(bits32 kinds,const char * name,const char * desc,bool default_val)679 add_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  */
694 void
add_int_reloption(bits32 kinds,const char * name,const char * desc,int default_val,int min_val,int max_val)695 add_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  */
713 void
add_real_reloption(bits32 kinds,const char * name,const char * desc,double default_val,double min_val,double max_val)714 add_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  */
737 void
add_string_reloption(bits32 kinds,const char * name,const char * desc,const char * default_val,validate_string_relopt validator)738 add_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  */
790 Datum
transformRelOptions(Datum oldOptions,List * defList,const char * namspace,char * validnsps[],bool acceptOidsOff,bool isReset)791 transformRelOptions(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  */
966 List *
untransformRelOptions(Datum options)967 untransformRelOptions(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  */
1015 bytea *
extractRelOptions(HeapTuple tuple,TupleDesc tupdesc,amoptions_function amoptions)1016 extractRelOptions(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  */
1080 relopt_value *
parseRelOptions(Datum options,bool validate,relopt_kind kind,int * numrelopts)1081 parseRelOptions(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  */
1172 static void
parse_one_reloption(relopt_value * option,char * text_str,int text_len,bool validate)1173 parse_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  */
1274 void *
allocateReloptStruct(Size base,relopt_value * options,int numoptions)1275 allocateReloptStruct(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  */
1298 void
fillRelOptions(void * rdopts,Size basesize,relopt_value * options,int numoptions,bool validate,const relopt_parse_elt * elems,int numelems)1299 fillRelOptions(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  */
1375 bytea *
default_reloptions(Datum reloptions,bool validate,relopt_kind kind)1376 default_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  */
1444 bytea *
view_reloptions(Datum reloptions,bool validate)1445 view_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  */
1476 bytea *
heap_reloptions(char relkind,Datum reloptions,bool validate)1477 heap_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  */
1514 bytea *
index_reloptions(amoptions_function amoptions,Datum reloptions,bool validate)1515 index_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  */
1529 bytea *
attribute_reloptions(Datum reloptions,bool validate)1530 attribute_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  */
1560 bytea *
tablespace_reloptions(Datum reloptions,bool validate)1561 tablespace_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  */
1595 LOCKMODE
AlterTableGetRelOptionsLockLevel(List * defList)1596 AlterTableGetRelOptionsLockLevel(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