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