Line data Source code
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
445 261 : initialize_reloptions(void)
446 : {
447 : int i;
448 : int j;
449 :
450 261 : j = 0;
451 1566 : for (i = 0; boolRelOpts[i].gen.name; i++)
452 : {
453 1305 : Assert(DoLockModesConflict(boolRelOpts[i].gen.lockmode,
454 : boolRelOpts[i].gen.lockmode));
455 1305 : j++;
456 : }
457 5481 : for (i = 0; intRelOpts[i].gen.name; i++)
458 : {
459 5220 : Assert(DoLockModesConflict(intRelOpts[i].gen.lockmode,
460 : intRelOpts[i].gen.lockmode));
461 5220 : j++;
462 : }
463 1827 : for (i = 0; realRelOpts[i].gen.name; i++)
464 : {
465 1566 : Assert(DoLockModesConflict(realRelOpts[i].gen.lockmode,
466 : realRelOpts[i].gen.lockmode));
467 1566 : j++;
468 : }
469 783 : for (i = 0; stringRelOpts[i].gen.name; i++)
470 : {
471 522 : Assert(DoLockModesConflict(stringRelOpts[i].gen.lockmode,
472 : stringRelOpts[i].gen.lockmode));
473 522 : j++;
474 : }
475 261 : j += num_custom_options;
476 :
477 261 : if (relOpts)
478 0 : pfree(relOpts);
479 261 : relOpts = MemoryContextAlloc(TopMemoryContext,
480 : (j + 1) * sizeof(relopt_gen *));
481 :
482 261 : j = 0;
483 1566 : for (i = 0; boolRelOpts[i].gen.name; i++)
484 : {
485 1305 : relOpts[j] = &boolRelOpts[i].gen;
486 1305 : relOpts[j]->type = RELOPT_TYPE_BOOL;
487 1305 : relOpts[j]->namelen = strlen(relOpts[j]->name);
488 1305 : j++;
489 : }
490 :
491 5481 : for (i = 0; intRelOpts[i].gen.name; i++)
492 : {
493 5220 : relOpts[j] = &intRelOpts[i].gen;
494 5220 : relOpts[j]->type = RELOPT_TYPE_INT;
495 5220 : relOpts[j]->namelen = strlen(relOpts[j]->name);
496 5220 : j++;
497 : }
498 :
499 1827 : for (i = 0; realRelOpts[i].gen.name; i++)
500 : {
501 1566 : relOpts[j] = &realRelOpts[i].gen;
502 1566 : relOpts[j]->type = RELOPT_TYPE_REAL;
503 1566 : relOpts[j]->namelen = strlen(relOpts[j]->name);
504 1566 : j++;
505 : }
506 :
507 783 : for (i = 0; stringRelOpts[i].gen.name; i++)
508 : {
509 522 : relOpts[j] = &stringRelOpts[i].gen;
510 522 : relOpts[j]->type = RELOPT_TYPE_STRING;
511 522 : relOpts[j]->namelen = strlen(relOpts[j]->name);
512 522 : j++;
513 : }
514 :
515 261 : for (i = 0; i < num_custom_options; i++)
516 : {
517 0 : relOpts[j] = custom_options[i];
518 0 : j++;
519 : }
520 :
521 : /* add a list terminator */
522 261 : relOpts[j] = NULL;
523 :
524 : /* flag the work is complete */
525 261 : need_initialization = false;
526 261 : }
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
534 0 : add_reloption_kind(void)
535 : {
536 : /* don't hand out the last bit so that the enum's behavior is portable */
537 0 : if (last_assigned_kind >= RELOPT_KIND_MAX)
538 0 : ereport(ERROR,
539 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
540 : errmsg("user-defined relation parameter types limit exceeded")));
541 0 : last_assigned_kind <<= 1;
542 0 : 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
551 0 : add_reloption(relopt_gen *newoption)
552 : {
553 : static int max_custom_options = 0;
554 :
555 0 : if (num_custom_options >= max_custom_options)
556 : {
557 : MemoryContext oldcxt;
558 :
559 0 : oldcxt = MemoryContextSwitchTo(TopMemoryContext);
560 :
561 0 : if (max_custom_options == 0)
562 : {
563 0 : max_custom_options = 8;
564 0 : custom_options = palloc(max_custom_options * sizeof(relopt_gen *));
565 : }
566 : else
567 : {
568 0 : max_custom_options *= 2;
569 0 : custom_options = repalloc(custom_options,
570 : max_custom_options * sizeof(relopt_gen *));
571 : }
572 0 : MemoryContextSwitchTo(oldcxt);
573 : }
574 0 : custom_options[num_custom_options++] = newoption;
575 :
576 0 : need_initialization = true;
577 0 : }
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 *
585 0 : allocate_reloption(bits32 kinds, int type, char *name, char *desc)
586 : {
587 : MemoryContext oldcxt;
588 : size_t size;
589 : relopt_gen *newoption;
590 :
591 0 : oldcxt = MemoryContextSwitchTo(TopMemoryContext);
592 :
593 0 : switch (type)
594 : {
595 : case RELOPT_TYPE_BOOL:
596 0 : size = sizeof(relopt_bool);
597 0 : break;
598 : case RELOPT_TYPE_INT:
599 0 : size = sizeof(relopt_int);
600 0 : break;
601 : case RELOPT_TYPE_REAL:
602 0 : size = sizeof(relopt_real);
603 0 : break;
604 : case RELOPT_TYPE_STRING:
605 0 : size = sizeof(relopt_string);
606 0 : break;
607 : default:
608 0 : elog(ERROR, "unsupported reloption type %d", type);
609 : return NULL; /* keep compiler quiet */
610 : }
611 :
612 0 : newoption = palloc(size);
613 :
614 0 : newoption->name = pstrdup(name);
615 0 : if (desc)
616 0 : newoption->desc = pstrdup(desc);
617 : else
618 0 : newoption->desc = NULL;
619 0 : newoption->kinds = kinds;
620 0 : newoption->namelen = strlen(name);
621 0 : newoption->type = type;
622 :
623 0 : MemoryContextSwitchTo(oldcxt);
624 :
625 0 : return newoption;
626 : }
627 :
628 : /*
629 : * add_bool_reloption
630 : * Add a new boolean reloption
631 : */
632 : void
633 0 : add_bool_reloption(bits32 kinds, char *name, char *desc, bool default_val)
634 : {
635 : relopt_bool *newoption;
636 :
637 0 : newoption = (relopt_bool *) allocate_reloption(kinds, RELOPT_TYPE_BOOL,
638 : name, desc);
639 0 : newoption->default_val = default_val;
640 :
641 0 : add_reloption((relopt_gen *) newoption);
642 0 : }
643 :
644 : /*
645 : * add_int_reloption
646 : * Add a new integer reloption
647 : */
648 : void
649 0 : add_int_reloption(bits32 kinds, char *name, char *desc, int default_val,
650 : int min_val, int max_val)
651 : {
652 : relopt_int *newoption;
653 :
654 0 : newoption = (relopt_int *) allocate_reloption(kinds, RELOPT_TYPE_INT,
655 : name, desc);
656 0 : newoption->default_val = default_val;
657 0 : newoption->min = min_val;
658 0 : newoption->max = max_val;
659 :
660 0 : add_reloption((relopt_gen *) newoption);
661 0 : }
662 :
663 : /*
664 : * add_real_reloption
665 : * Add a new float reloption
666 : */
667 : void
668 0 : add_real_reloption(bits32 kinds, char *name, char *desc, double default_val,
669 : double min_val, double max_val)
670 : {
671 : relopt_real *newoption;
672 :
673 0 : newoption = (relopt_real *) allocate_reloption(kinds, RELOPT_TYPE_REAL,
674 : name, desc);
675 0 : newoption->default_val = default_val;
676 0 : newoption->min = min_val;
677 0 : newoption->max = max_val;
678 :
679 0 : add_reloption((relopt_gen *) newoption);
680 0 : }
681 :
682 : /*
683 : * add_string_reloption
684 : * Add a new string reloption
685 : *
686 : * "validator" is an optional function pointer that can be used to test the
687 : * validity of the values. It must elog(ERROR) when the argument string is
688 : * not acceptable for the variable. Note that the default value must pass
689 : * the validation.
690 : */
691 : void
692 0 : add_string_reloption(bits32 kinds, char *name, char *desc, char *default_val,
693 : validate_string_relopt validator)
694 : {
695 : relopt_string *newoption;
696 :
697 : /* make sure the validator/default combination is sane */
698 0 : if (validator)
699 0 : (validator) (default_val);
700 :
701 0 : newoption = (relopt_string *) allocate_reloption(kinds, RELOPT_TYPE_STRING,
702 : name, desc);
703 0 : newoption->validate_cb = validator;
704 0 : if (default_val)
705 : {
706 0 : newoption->default_val = MemoryContextStrdup(TopMemoryContext,
707 : default_val);
708 0 : newoption->default_len = strlen(default_val);
709 0 : newoption->default_isnull = false;
710 : }
711 : else
712 : {
713 0 : newoption->default_val = "";
714 0 : newoption->default_len = 0;
715 0 : newoption->default_isnull = true;
716 : }
717 :
718 0 : add_reloption((relopt_gen *) newoption);
719 0 : }
720 :
721 : /*
722 : * Transform a relation options list (list of DefElem) into the text array
723 : * format that is kept in pg_class.reloptions, including only those options
724 : * that are in the passed namespace. The output values do not include the
725 : * namespace.
726 : *
727 : * This is used for three cases: CREATE TABLE/INDEX, ALTER TABLE SET, and
728 : * ALTER TABLE RESET. In the ALTER cases, oldOptions is the existing
729 : * reloptions value (possibly NULL), and we replace or remove entries
730 : * as needed.
731 : *
732 : * If ignoreOids is true, then we should ignore any occurrence of "oids"
733 : * in the list (it will be or has been handled by interpretOidsOption()).
734 : *
735 : * Note that this is not responsible for determining whether the options
736 : * are valid, but it does check that namespaces for all the options given are
737 : * listed in validnsps. The NULL namespace is always valid and need not be
738 : * explicitly listed. Passing a NULL pointer means that only the NULL
739 : * namespace is valid.
740 : *
741 : * Both oldOptions and the result are text arrays (or NULL for "default"),
742 : * but we declare them as Datums to avoid including array.h in reloptions.h.
743 : */
744 : Datum
745 4467 : transformRelOptions(Datum oldOptions, List *defList, char *namspace,
746 : char *validnsps[], bool ignoreOids, bool isReset)
747 : {
748 : Datum result;
749 : ArrayBuildState *astate;
750 : ListCell *cell;
751 :
752 : /* no change if empty list */
753 4467 : if (defList == NIL)
754 4211 : return oldOptions;
755 :
756 : /* We build new array using accumArrayResult */
757 256 : astate = NULL;
758 :
759 : /* Copy any oldOptions that aren't to be replaced */
760 256 : if (PointerIsValid(DatumGetPointer(oldOptions)))
761 : {
762 23 : ArrayType *array = DatumGetArrayTypeP(oldOptions);
763 : Datum *oldoptions;
764 : int noldoptions;
765 : int i;
766 :
767 23 : deconstruct_array(array, TEXTOID, -1, false, 'i',
768 : &oldoptions, NULL, &noldoptions);
769 :
770 58 : for (i = 0; i < noldoptions; i++)
771 : {
772 35 : char *text_str = VARDATA(oldoptions[i]);
773 35 : int text_len = VARSIZE(oldoptions[i]) - VARHDRSZ;
774 :
775 : /* Search for a match in defList */
776 54 : foreach(cell, defList)
777 : {
778 42 : DefElem *def = (DefElem *) lfirst(cell);
779 : int kw_len;
780 :
781 : /* ignore if not in the same namespace */
782 42 : if (namspace == NULL)
783 : {
784 37 : if (def->defnamespace != NULL)
785 0 : continue;
786 : }
787 5 : else if (def->defnamespace == NULL)
788 3 : continue;
789 2 : else if (pg_strcasecmp(def->defnamespace, namspace) != 0)
790 0 : continue;
791 :
792 39 : kw_len = strlen(def->defname);
793 62 : if (text_len > kw_len && text_str[kw_len] == '=' &&
794 23 : pg_strncasecmp(text_str, def->defname, kw_len) == 0)
795 23 : break;
796 : }
797 35 : if (!cell)
798 : {
799 : /* No match, so keep old option */
800 12 : astate = accumArrayResult(astate, oldoptions[i],
801 : false, TEXTOID,
802 : CurrentMemoryContext);
803 : }
804 : }
805 : }
806 :
807 : /*
808 : * If CREATE/SET, add new options to array; if RESET, just check that the
809 : * user didn't say RESET (option=val). (Must do this because the grammar
810 : * doesn't enforce it.)
811 : */
812 537 : foreach(cell, defList)
813 : {
814 285 : DefElem *def = (DefElem *) lfirst(cell);
815 :
816 285 : if (isReset)
817 : {
818 18 : if (def->arg != NULL)
819 2 : ereport(ERROR,
820 : (errcode(ERRCODE_SYNTAX_ERROR),
821 : errmsg("RESET must not include values for parameters")));
822 : }
823 : else
824 : {
825 : text *t;
826 : const char *value;
827 : Size len;
828 :
829 : /*
830 : * Error out if the namespace is not valid. A NULL namespace is
831 : * always valid.
832 : */
833 267 : if (def->defnamespace != NULL)
834 : {
835 12 : bool valid = false;
836 : int i;
837 :
838 12 : if (validnsps)
839 : {
840 12 : for (i = 0; validnsps[i]; i++)
841 : {
842 11 : if (pg_strcasecmp(def->defnamespace,
843 11 : validnsps[i]) == 0)
844 : {
845 10 : valid = true;
846 10 : break;
847 : }
848 : }
849 : }
850 :
851 12 : if (!valid)
852 2 : ereport(ERROR,
853 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
854 : errmsg("unrecognized parameter namespace \"%s\"",
855 : def->defnamespace)));
856 : }
857 :
858 265 : if (ignoreOids && pg_strcasecmp(def->defname, "oids") == 0)
859 111 : continue;
860 :
861 : /* ignore if not in the same namespace */
862 154 : if (namspace == NULL)
863 : {
864 133 : if (def->defnamespace != NULL)
865 5 : continue;
866 : }
867 21 : else if (def->defnamespace == NULL)
868 16 : continue;
869 5 : else if (pg_strcasecmp(def->defnamespace, namspace) != 0)
870 0 : continue;
871 :
872 : /*
873 : * Flatten the DefElem into a text string like "name=arg". If we
874 : * have just "name", assume "name=true" is meant. Note: the
875 : * namespace is not output.
876 : */
877 133 : if (def->arg != NULL)
878 120 : value = defGetString(def);
879 : else
880 13 : value = "true";
881 133 : len = VARHDRSZ + strlen(def->defname) + 1 + strlen(value);
882 : /* +1 leaves room for sprintf's trailing null */
883 133 : t = (text *) palloc(len + 1);
884 133 : SET_VARSIZE(t, len);
885 133 : sprintf(VARDATA(t), "%s=%s", def->defname, value);
886 :
887 133 : astate = accumArrayResult(astate, PointerGetDatum(t),
888 : false, TEXTOID,
889 : CurrentMemoryContext);
890 : }
891 : }
892 :
893 252 : if (astate)
894 122 : result = makeArrayResult(astate, CurrentMemoryContext);
895 : else
896 130 : result = (Datum) 0;
897 :
898 252 : return result;
899 : }
900 :
901 :
902 : /*
903 : * Convert the text-array format of reloptions into a List of DefElem.
904 : * This is the inverse of transformRelOptions().
905 : */
906 : List *
907 358 : untransformRelOptions(Datum options)
908 : {
909 358 : List *result = NIL;
910 : ArrayType *array;
911 : Datum *optiondatums;
912 : int noptions;
913 : int i;
914 :
915 : /* Nothing to do if no options */
916 358 : if (!PointerIsValid(DatumGetPointer(options)))
917 121 : return result;
918 :
919 237 : array = DatumGetArrayTypeP(options);
920 :
921 237 : deconstruct_array(array, TEXTOID, -1, false, 'i',
922 : &optiondatums, NULL, &noptions);
923 :
924 637 : for (i = 0; i < noptions; i++)
925 : {
926 : char *s;
927 : char *p;
928 400 : Node *val = NULL;
929 :
930 400 : s = TextDatumGetCString(optiondatums[i]);
931 400 : p = strchr(s, '=');
932 400 : if (p)
933 : {
934 400 : *p++ = '\0';
935 400 : val = (Node *) makeString(pstrdup(p));
936 : }
937 400 : result = lappend(result, makeDefElem(pstrdup(s), val, -1));
938 : }
939 :
940 237 : return result;
941 : }
942 :
943 : /*
944 : * Extract and parse reloptions from a pg_class tuple.
945 : *
946 : * This is a low-level routine, expected to be used by relcache code and
947 : * callers that do not have a table's relcache entry (e.g. autovacuum). For
948 : * other uses, consider grabbing the rd_options pointer from the relcache entry
949 : * instead.
950 : *
951 : * tupdesc is pg_class' tuple descriptor. amoptions is a pointer to the index
952 : * AM's options parser function in the case of a tuple corresponding to an
953 : * index, or NULL otherwise.
954 : */
955 : bytea *
956 27848 : extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
957 : amoptions_function amoptions)
958 : {
959 : bytea *options;
960 : bool isnull;
961 : Datum datum;
962 : Form_pg_class classForm;
963 :
964 27848 : datum = fastgetattr(tuple,
965 : Anum_pg_class_reloptions,
966 : tupdesc,
967 : &isnull);
968 27848 : if (isnull)
969 27471 : return NULL;
970 :
971 377 : classForm = (Form_pg_class) GETSTRUCT(tuple);
972 :
973 : /* Parse into appropriate format; don't error out here */
974 377 : switch (classForm->relkind)
975 : {
976 : case RELKIND_RELATION:
977 : case RELKIND_TOASTVALUE:
978 : case RELKIND_MATVIEW:
979 : case RELKIND_PARTITIONED_TABLE:
980 192 : options = heap_reloptions(classForm->relkind, datum, false);
981 192 : break;
982 : case RELKIND_VIEW:
983 117 : options = view_reloptions(datum, false);
984 117 : break;
985 : case RELKIND_INDEX:
986 68 : options = index_reloptions(amoptions, datum, false);
987 68 : break;
988 : case RELKIND_FOREIGN_TABLE:
989 0 : options = NULL;
990 0 : break;
991 : default:
992 0 : Assert(false); /* can't get here */
993 : options = NULL; /* keep compiler quiet */
994 : break;
995 : }
996 :
997 377 : return options;
998 : }
999 :
1000 : /*
1001 : * Interpret reloptions that are given in text-array format.
1002 : *
1003 : * options is a reloption text array as constructed by transformRelOptions.
1004 : * kind specifies the family of options to be processed.
1005 : *
1006 : * The return value is a relopt_value * array on which the options actually
1007 : * set in the options array are marked with isset=true. The length of this
1008 : * array is returned in *numrelopts. Options not set are also present in the
1009 : * array; this is so that the caller can easily locate the default values.
1010 : *
1011 : * If there are no options of the given kind, numrelopts is set to 0 and NULL
1012 : * is returned (unless options are illegally supplied despite none being
1013 : * defined, in which case an error occurs).
1014 : *
1015 : * Note: values of type int, bool and real are allocated as part of the
1016 : * returned array. Values of type string are allocated separately and must
1017 : * be freed by the caller.
1018 : */
1019 : relopt_value *
1020 3962 : parseRelOptions(Datum options, bool validate, relopt_kind kind,
1021 : int *numrelopts)
1022 : {
1023 3962 : relopt_value *reloptions = NULL;
1024 3962 : int numoptions = 0;
1025 : int i;
1026 : int j;
1027 :
1028 3962 : if (need_initialization)
1029 260 : initialize_reloptions();
1030 :
1031 : /* Build a list of expected options, based on kind */
1032 :
1033 134708 : for (i = 0; relOpts[i]; i++)
1034 130746 : if (relOpts[i]->kinds & kind)
1035 48387 : numoptions++;
1036 :
1037 3962 : if (numoptions > 0)
1038 : {
1039 3869 : reloptions = palloc(numoptions * sizeof(relopt_value));
1040 :
1041 131546 : for (i = 0, j = 0; relOpts[i]; i++)
1042 : {
1043 127677 : if (relOpts[i]->kinds & kind)
1044 : {
1045 48387 : reloptions[j].gen = relOpts[i];
1046 48387 : reloptions[j].isset = false;
1047 48387 : j++;
1048 : }
1049 : }
1050 : }
1051 :
1052 : /* Done if no options */
1053 3962 : if (PointerIsValid(DatumGetPointer(options)))
1054 : {
1055 501 : ArrayType *array = DatumGetArrayTypeP(options);
1056 : Datum *optiondatums;
1057 : int noptions;
1058 :
1059 501 : deconstruct_array(array, TEXTOID, -1, false, 'i',
1060 : &optiondatums, NULL, &noptions);
1061 :
1062 1055 : for (i = 0; i < noptions; i++)
1063 : {
1064 587 : char *text_str = VARDATA(optiondatums[i]);
1065 587 : int text_len = VARSIZE(optiondatums[i]) - VARHDRSZ;
1066 : int j;
1067 :
1068 : /* Search for a match in reloptions */
1069 2860 : for (j = 0; j < numoptions; j++)
1070 : {
1071 2852 : int kw_len = reloptions[j].gen->namelen;
1072 :
1073 3438 : if (text_len > kw_len && text_str[kw_len] == '=' &&
1074 586 : pg_strncasecmp(text_str, reloptions[j].gen->name,
1075 : kw_len) == 0)
1076 : {
1077 579 : parse_one_reloption(&reloptions[j], text_str, text_len,
1078 : validate);
1079 554 : break;
1080 : }
1081 : }
1082 :
1083 562 : if (j >= numoptions && validate)
1084 : {
1085 : char *s;
1086 : char *p;
1087 :
1088 8 : s = TextDatumGetCString(optiondatums[i]);
1089 8 : p = strchr(s, '=');
1090 8 : if (p)
1091 8 : *p = '\0';
1092 8 : ereport(ERROR,
1093 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1094 : errmsg("unrecognized parameter \"%s\"", s)));
1095 : }
1096 : }
1097 :
1098 : /* It's worth avoiding memory leaks in this function */
1099 468 : pfree(optiondatums);
1100 468 : if (((void *) array) != DatumGetPointer(options))
1101 379 : pfree(array);
1102 : }
1103 :
1104 3929 : *numrelopts = numoptions;
1105 3929 : return reloptions;
1106 : }
1107 :
1108 : /*
1109 : * Subroutine for parseRelOptions, to parse and validate a single option's
1110 : * value
1111 : */
1112 : static void
1113 579 : parse_one_reloption(relopt_value *option, char *text_str, int text_len,
1114 : bool validate)
1115 : {
1116 : char *value;
1117 : int value_len;
1118 : bool parsed;
1119 579 : bool nofree = false;
1120 :
1121 579 : if (option->isset && validate)
1122 1 : ereport(ERROR,
1123 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1124 : errmsg("parameter \"%s\" specified more than once",
1125 : option->gen->name)));
1126 :
1127 578 : value_len = text_len - option->gen->namelen - 1;
1128 578 : value = (char *) palloc(value_len + 1);
1129 578 : memcpy(value, text_str + option->gen->namelen + 1, value_len);
1130 578 : value[value_len] = '\0';
1131 :
1132 578 : switch (option->gen->type)
1133 : {
1134 : case RELOPT_TYPE_BOOL:
1135 : {
1136 164 : parsed = parse_bool(value, &option->values.bool_val);
1137 164 : if (validate && !parsed)
1138 4 : ereport(ERROR,
1139 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1140 : errmsg("invalid value for boolean option \"%s\": %s",
1141 : option->gen->name, value)));
1142 : }
1143 160 : break;
1144 : case RELOPT_TYPE_INT:
1145 : {
1146 286 : relopt_int *optint = (relopt_int *) option->gen;
1147 :
1148 286 : parsed = parse_int(value, &option->values.int_val, 0, NULL);
1149 286 : if (validate && !parsed)
1150 4 : ereport(ERROR,
1151 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1152 : errmsg("invalid value for integer option \"%s\": %s",
1153 : option->gen->name, value)));
1154 321 : if (validate && (option->values.int_val < optint->min ||
1155 39 : option->values.int_val > optint->max))
1156 10 : ereport(ERROR,
1157 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1158 : errmsg("value %s out of bounds for option \"%s\"",
1159 : value, option->gen->name),
1160 : errdetail("Valid values are between \"%d\" and \"%d\".",
1161 : optint->min, optint->max)));
1162 : }
1163 272 : break;
1164 : case RELOPT_TYPE_REAL:
1165 : {
1166 30 : relopt_real *optreal = (relopt_real *) option->gen;
1167 :
1168 30 : parsed = parse_real(value, &option->values.real_val);
1169 30 : if (validate && !parsed)
1170 2 : ereport(ERROR,
1171 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1172 : errmsg("invalid value for floating point option \"%s\": %s",
1173 : option->gen->name, value)));
1174 45 : if (validate && (option->values.real_val < optreal->min ||
1175 17 : option->values.real_val > optreal->max))
1176 2 : ereport(ERROR,
1177 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1178 : errmsg("value %s out of bounds for option \"%s\"",
1179 : value, option->gen->name),
1180 : errdetail("Valid values are between \"%f\" and \"%f\".",
1181 : optreal->min, optreal->max)));
1182 : }
1183 26 : break;
1184 : case RELOPT_TYPE_STRING:
1185 : {
1186 98 : relopt_string *optstring = (relopt_string *) option->gen;
1187 :
1188 98 : option->values.string_val = value;
1189 98 : nofree = true;
1190 98 : if (validate && optstring->validate_cb)
1191 25 : (optstring->validate_cb) (value);
1192 96 : parsed = true;
1193 : }
1194 96 : break;
1195 : default:
1196 0 : elog(ERROR, "unsupported reloption type %d", option->gen->type);
1197 : parsed = true; /* quiet compiler */
1198 : break;
1199 : }
1200 :
1201 554 : if (parsed)
1202 554 : option->isset = true;
1203 554 : if (!nofree)
1204 458 : pfree(value);
1205 554 : }
1206 :
1207 : /*
1208 : * Given the result from parseRelOptions, allocate a struct that's of the
1209 : * specified base size plus any extra space that's needed for string variables.
1210 : *
1211 : * "base" should be sizeof(struct) of the reloptions struct (StdRdOptions or
1212 : * equivalent).
1213 : */
1214 : void *
1215 3836 : allocateReloptStruct(Size base, relopt_value *options, int numoptions)
1216 : {
1217 3836 : Size size = base;
1218 : int i;
1219 :
1220 51927 : for (i = 0; i < numoptions; i++)
1221 48091 : if (options[i].gen->type == RELOPT_TYPE_STRING)
1222 550 : size += GET_STRING_RELOPTION_LEN(options[i]) + 1;
1223 :
1224 3836 : return palloc0(size);
1225 : }
1226 :
1227 : /*
1228 : * Given the result of parseRelOptions and a parsing table, fill in the
1229 : * struct (previously allocated with allocateReloptStruct) with the parsed
1230 : * values.
1231 : *
1232 : * rdopts is the pointer to the allocated struct to be filled.
1233 : * basesize is the sizeof(struct) that was passed to allocateReloptStruct.
1234 : * options, of length numoptions, is parseRelOptions' output.
1235 : * elems, of length numelems, is the table describing the allowed options.
1236 : * When validate is true, it is expected that all options appear in elems.
1237 : */
1238 : void
1239 3836 : fillRelOptions(void *rdopts, Size basesize,
1240 : relopt_value *options, int numoptions,
1241 : bool validate,
1242 : const relopt_parse_elt *elems, int numelems)
1243 : {
1244 : int i;
1245 3836 : int offset = basesize;
1246 :
1247 51927 : for (i = 0; i < numoptions; i++)
1248 : {
1249 : int j;
1250 48091 : bool found = false;
1251 :
1252 411368 : for (j = 0; j < numelems; j++)
1253 : {
1254 411368 : if (pg_strcasecmp(options[i].gen->name, elems[j].optname) == 0)
1255 : {
1256 : relopt_string *optstring;
1257 48091 : char *itempos = ((char *) rdopts) + elems[j].offset;
1258 : char *string_val;
1259 :
1260 48091 : switch (options[i].gen->type)
1261 : {
1262 : case RELOPT_TYPE_BOOL:
1263 10894 : *(bool *) itempos = options[i].isset ?
1264 160 : options[i].values.bool_val :
1265 5287 : ((relopt_bool *) options[i].gen)->default_val;
1266 5447 : break;
1267 : case RELOPT_TYPE_INT:
1268 74372 : *(int *) itempos = options[i].isset ?
1269 74101 : options[i].values.int_val :
1270 36915 : ((relopt_int *) options[i].gen)->default_val;
1271 37186 : break;
1272 : case RELOPT_TYPE_REAL:
1273 9816 : *(double *) itempos = options[i].isset ?
1274 9792 : options[i].values.real_val :
1275 4884 : ((relopt_real *) options[i].gen)->default_val;
1276 4908 : break;
1277 : case RELOPT_TYPE_STRING:
1278 550 : optstring = (relopt_string *) options[i].gen;
1279 550 : if (options[i].isset)
1280 96 : string_val = options[i].values.string_val;
1281 454 : else if (!optstring->default_isnull)
1282 0 : string_val = optstring->default_val;
1283 : else
1284 454 : string_val = NULL;
1285 :
1286 550 : if (string_val == NULL)
1287 454 : *(int *) itempos = 0;
1288 : else
1289 : {
1290 96 : strcpy((char *) rdopts + offset, string_val);
1291 96 : *(int *) itempos = offset;
1292 96 : offset += strlen(string_val) + 1;
1293 : }
1294 550 : break;
1295 : default:
1296 0 : elog(ERROR, "unsupported reloption type %d",
1297 : options[i].gen->type);
1298 : break;
1299 : }
1300 48091 : found = true;
1301 48091 : break;
1302 : }
1303 : }
1304 48091 : if (validate && !found)
1305 0 : elog(ERROR, "reloption \"%s\" not found in parse table",
1306 : options[i].gen->name);
1307 : }
1308 3836 : SET_VARSIZE(rdopts, offset);
1309 3836 : }
1310 :
1311 :
1312 : /*
1313 : * Option parser for anything that uses StdRdOptions.
1314 : */
1315 : bytea *
1316 3359 : default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
1317 : {
1318 : relopt_value *options;
1319 : StdRdOptions *rdopts;
1320 : int numoptions;
1321 : static const relopt_parse_elt tab[] = {
1322 : {"fillfactor", RELOPT_TYPE_INT, offsetof(StdRdOptions, fillfactor)},
1323 : {"autovacuum_enabled", RELOPT_TYPE_BOOL,
1324 : offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, enabled)},
1325 : {"autovacuum_vacuum_threshold", RELOPT_TYPE_INT,
1326 : offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_threshold)},
1327 : {"autovacuum_analyze_threshold", RELOPT_TYPE_INT,
1328 : offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, analyze_threshold)},
1329 : {"autovacuum_vacuum_cost_delay", RELOPT_TYPE_INT,
1330 : offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_cost_delay)},
1331 : {"autovacuum_vacuum_cost_limit", RELOPT_TYPE_INT,
1332 : offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_cost_limit)},
1333 : {"autovacuum_freeze_min_age", RELOPT_TYPE_INT,
1334 : offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_min_age)},
1335 : {"autovacuum_freeze_max_age", RELOPT_TYPE_INT,
1336 : offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_max_age)},
1337 : {"autovacuum_freeze_table_age", RELOPT_TYPE_INT,
1338 : offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_table_age)},
1339 : {"autovacuum_multixact_freeze_min_age", RELOPT_TYPE_INT,
1340 : offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_min_age)},
1341 : {"autovacuum_multixact_freeze_max_age", RELOPT_TYPE_INT,
1342 : offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_max_age)},
1343 : {"autovacuum_multixact_freeze_table_age", RELOPT_TYPE_INT,
1344 : offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_table_age)},
1345 : {"log_autovacuum_min_duration", RELOPT_TYPE_INT,
1346 : offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, log_min_duration)},
1347 : {"autovacuum_vacuum_scale_factor", RELOPT_TYPE_REAL,
1348 : offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_scale_factor)},
1349 : {"autovacuum_analyze_scale_factor", RELOPT_TYPE_REAL,
1350 : offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, analyze_scale_factor)},
1351 : {"user_catalog_table", RELOPT_TYPE_BOOL,
1352 : offsetof(StdRdOptions, user_catalog_table)},
1353 : {"parallel_workers", RELOPT_TYPE_INT,
1354 : offsetof(StdRdOptions, parallel_workers)}
1355 : };
1356 :
1357 3359 : options = parseRelOptions(reloptions, validate, kind, &numoptions);
1358 :
1359 : /* if none set, we're done */
1360 3336 : if (numoptions == 0)
1361 93 : return NULL;
1362 :
1363 3243 : rdopts = allocateReloptStruct(sizeof(StdRdOptions), options, numoptions);
1364 :
1365 3243 : fillRelOptions((void *) rdopts, sizeof(StdRdOptions), options, numoptions,
1366 : validate, tab, lengthof(tab));
1367 :
1368 3243 : pfree(options);
1369 :
1370 3243 : return (bytea *) rdopts;
1371 : }
1372 :
1373 : /*
1374 : * Option parser for views
1375 : */
1376 : bytea *
1377 537 : view_reloptions(Datum reloptions, bool validate)
1378 : {
1379 : relopt_value *options;
1380 : ViewOptions *vopts;
1381 : int numoptions;
1382 : static const relopt_parse_elt tab[] = {
1383 : {"security_barrier", RELOPT_TYPE_BOOL,
1384 : offsetof(ViewOptions, security_barrier)},
1385 : {"check_option", RELOPT_TYPE_STRING,
1386 : offsetof(ViewOptions, check_option_offset)}
1387 : };
1388 :
1389 537 : options = parseRelOptions(reloptions, validate, RELOPT_KIND_VIEW, &numoptions);
1390 :
1391 : /* if none set, we're done */
1392 532 : if (numoptions == 0)
1393 0 : return NULL;
1394 :
1395 532 : vopts = allocateReloptStruct(sizeof(ViewOptions), options, numoptions);
1396 :
1397 532 : fillRelOptions((void *) vopts, sizeof(ViewOptions), options, numoptions,
1398 : validate, tab, lengthof(tab));
1399 :
1400 532 : pfree(options);
1401 :
1402 532 : return (bytea *) vopts;
1403 : }
1404 :
1405 : /*
1406 : * Parse options for heaps, views and toast tables.
1407 : */
1408 : bytea *
1409 3513 : heap_reloptions(char relkind, Datum reloptions, bool validate)
1410 : {
1411 : StdRdOptions *rdopts;
1412 :
1413 3513 : switch (relkind)
1414 : {
1415 : case RELKIND_TOASTVALUE:
1416 1529 : rdopts = (StdRdOptions *)
1417 1529 : default_reloptions(reloptions, validate, RELOPT_KIND_TOAST);
1418 1528 : if (rdopts != NULL)
1419 : {
1420 : /* adjust default-only parameters for TOAST relations */
1421 1528 : rdopts->fillfactor = 100;
1422 1528 : rdopts->autovacuum.analyze_threshold = -1;
1423 1528 : rdopts->autovacuum.analyze_scale_factor = -1;
1424 : }
1425 1528 : return (bytea *) rdopts;
1426 : case RELKIND_RELATION:
1427 : case RELKIND_MATVIEW:
1428 1693 : return default_reloptions(reloptions, validate, RELOPT_KIND_HEAP);
1429 : case RELKIND_PARTITIONED_TABLE:
1430 93 : return default_reloptions(reloptions, validate,
1431 : RELOPT_KIND_PARTITIONED);
1432 : default:
1433 : /* other relkinds are not supported */
1434 198 : return NULL;
1435 : }
1436 : }
1437 :
1438 :
1439 : /*
1440 : * Parse options for indexes.
1441 : *
1442 : * amoptions index AM's option parser function
1443 : * reloptions options as text[] datum
1444 : * validate error flag
1445 : */
1446 : bytea *
1447 778 : index_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
1448 : {
1449 778 : Assert(amoptions != NULL);
1450 :
1451 : /* Assume function is strict */
1452 778 : if (!PointerIsValid(DatumGetPointer(reloptions)))
1453 682 : return NULL;
1454 :
1455 96 : return amoptions(reloptions, validate);
1456 : }
1457 :
1458 : /*
1459 : * Option parser for attribute reloptions
1460 : */
1461 : bytea *
1462 6 : attribute_reloptions(Datum reloptions, bool validate)
1463 : {
1464 : relopt_value *options;
1465 : AttributeOpts *aopts;
1466 : int numoptions;
1467 : static const relopt_parse_elt tab[] = {
1468 : {"n_distinct", RELOPT_TYPE_REAL, offsetof(AttributeOpts, n_distinct)},
1469 : {"n_distinct_inherited", RELOPT_TYPE_REAL, offsetof(AttributeOpts, n_distinct_inherited)}
1470 : };
1471 :
1472 6 : options = parseRelOptions(reloptions, validate, RELOPT_KIND_ATTRIBUTE,
1473 : &numoptions);
1474 :
1475 : /* if none set, we're done */
1476 6 : if (numoptions == 0)
1477 0 : return NULL;
1478 :
1479 6 : aopts = allocateReloptStruct(sizeof(AttributeOpts), options, numoptions);
1480 :
1481 6 : fillRelOptions((void *) aopts, sizeof(AttributeOpts), options, numoptions,
1482 : validate, tab, lengthof(tab));
1483 :
1484 6 : pfree(options);
1485 :
1486 6 : return (bytea *) aopts;
1487 : }
1488 :
1489 : /*
1490 : * Option parser for tablespace reloptions
1491 : */
1492 : bytea *
1493 8 : tablespace_reloptions(Datum reloptions, bool validate)
1494 : {
1495 : relopt_value *options;
1496 : TableSpaceOpts *tsopts;
1497 : int numoptions;
1498 : static const relopt_parse_elt tab[] = {
1499 : {"random_page_cost", RELOPT_TYPE_REAL, offsetof(TableSpaceOpts, random_page_cost)},
1500 : {"seq_page_cost", RELOPT_TYPE_REAL, offsetof(TableSpaceOpts, seq_page_cost)},
1501 : {"effective_io_concurrency", RELOPT_TYPE_INT, offsetof(TableSpaceOpts, effective_io_concurrency)}
1502 : };
1503 :
1504 8 : options = parseRelOptions(reloptions, validate, RELOPT_KIND_TABLESPACE,
1505 : &numoptions);
1506 :
1507 : /* if none set, we're done */
1508 6 : if (numoptions == 0)
1509 0 : return NULL;
1510 :
1511 6 : tsopts = allocateReloptStruct(sizeof(TableSpaceOpts), options, numoptions);
1512 :
1513 6 : fillRelOptions((void *) tsopts, sizeof(TableSpaceOpts), options, numoptions,
1514 : validate, tab, lengthof(tab));
1515 :
1516 6 : pfree(options);
1517 :
1518 6 : return (bytea *) tsopts;
1519 : }
1520 :
1521 : /*
1522 : * Determine the required LOCKMODE from an option list.
1523 : *
1524 : * Called from AlterTableGetLockLevel(), see that function
1525 : * for a longer explanation of how this works.
1526 : */
1527 : LOCKMODE
1528 34 : AlterTableGetRelOptionsLockLevel(List *defList)
1529 : {
1530 34 : LOCKMODE lockmode = NoLock;
1531 : ListCell *cell;
1532 :
1533 34 : if (defList == NIL)
1534 0 : return AccessExclusiveLock;
1535 :
1536 34 : if (need_initialization)
1537 1 : initialize_reloptions();
1538 :
1539 72 : foreach(cell, defList)
1540 : {
1541 38 : DefElem *def = (DefElem *) lfirst(cell);
1542 : int i;
1543 :
1544 1292 : for (i = 0; relOpts[i]; i++)
1545 : {
1546 2508 : if (pg_strncasecmp(relOpts[i]->name,
1547 1254 : def->defname,
1548 1254 : relOpts[i]->namelen + 1) == 0)
1549 : {
1550 78 : if (lockmode < relOpts[i]->lockmode)
1551 34 : lockmode = relOpts[i]->lockmode;
1552 : }
1553 : }
1554 : }
1555 :
1556 34 : return lockmode;
1557 : }
|