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