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