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