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