Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * reloptions.c
4 : * Core support for relation options (pg_class.reloptions)
5 : *
6 : * Portions Copyright (c) 1996-2026, 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/heaptoast.h"
23 : #include "access/htup_details.h"
24 : #include "access/nbtree.h"
25 : #include "access/reloptions.h"
26 : #include "access/spgist_private.h"
27 : #include "catalog/pg_type.h"
28 : #include "commands/defrem.h"
29 : #include "commands/tablespace.h"
30 : #include "nodes/makefuncs.h"
31 : #include "storage/lock.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 (bool, ternary, integer, real, enum, string), name,
45 : * default value, upper and lower bounds (if applicable); for strings,
46 : * consider a validation 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 : * From the user's point of view, a 'ternary' is exactly like a Boolean,
55 : * so we don't document it separately. On the implementation side, the
56 : * handling code can detect the case where the option has not been set.
57 : *
58 : * The default choice for any new option should be AccessExclusiveLock.
59 : * In some cases the lock level can be reduced from there, but the lock
60 : * level chosen should always conflict with itself to ensure that multiple
61 : * changes aren't lost when we attempt concurrent changes.
62 : * The choice of lock level depends completely upon how that parameter
63 : * is used within the server, not upon how and when you'd like to change it.
64 : * Safety first. Existing choices are documented here, and elsewhere in
65 : * backend code where the parameters are used.
66 : *
67 : * In general, anything that affects the results obtained from a SELECT must be
68 : * protected by AccessExclusiveLock.
69 : *
70 : * Autovacuum related parameters can be set at ShareUpdateExclusiveLock
71 : * since they are only used by the AV procs and don't change anything
72 : * currently executing.
73 : *
74 : * Fillfactor can be set at ShareUpdateExclusiveLock because it applies only to
75 : * subsequent changes made to data blocks, as documented in hio.c
76 : *
77 : * n_distinct options can be set at ShareUpdateExclusiveLock because they
78 : * are only used during ANALYZE, which uses a ShareUpdateExclusiveLock,
79 : * so the ANALYZE will not be affected by in-flight changes. Changing those
80 : * values has no effect until the next ANALYZE, so no need for stronger lock.
81 : *
82 : * Planner-related parameters can be set at ShareUpdateExclusiveLock because
83 : * they only affect planning and not the correctness of the execution. Plans
84 : * cannot be changed in mid-flight, so changes here could not easily result in
85 : * new improved plans in any case. So we allow existing queries to continue
86 : * and existing plans to survive, a small price to pay for allowing better
87 : * plans to be introduced concurrently without interfering with users.
88 : *
89 : * Setting parallel_workers at ShareUpdateExclusiveLock is safe, since it acts
90 : * the same as max_parallel_workers_per_gather which is a USERSET parameter
91 : * that doesn't affect existing plans or queries.
92 : *
93 : * vacuum_truncate can be set at ShareUpdateExclusiveLock because it
94 : * is only used during VACUUM, which uses a ShareUpdateExclusiveLock,
95 : * so the VACUUM will not be affected by in-flight changes. Changing its
96 : * value has no effect until the next VACUUM, so no need for stronger lock.
97 : */
98 :
99 : static relopt_bool boolRelOpts[] =
100 : {
101 : {
102 : {
103 : "autosummarize",
104 : "Enables automatic summarization on this BRIN index",
105 : RELOPT_KIND_BRIN,
106 : AccessExclusiveLock
107 : },
108 : false
109 : },
110 : {
111 : {
112 : "autovacuum_enabled",
113 : "Enables autovacuum in this relation",
114 : RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
115 : ShareUpdateExclusiveLock
116 : },
117 : true
118 : },
119 : {
120 : {
121 : "user_catalog_table",
122 : "Declare a table as an additional catalog table, e.g. for the purpose of logical replication",
123 : RELOPT_KIND_HEAP,
124 : AccessExclusiveLock
125 : },
126 : false
127 : },
128 : {
129 : {
130 : "fastupdate",
131 : "Enables \"fast update\" feature for this GIN index",
132 : RELOPT_KIND_GIN,
133 : AccessExclusiveLock
134 : },
135 : true
136 : },
137 : {
138 : {
139 : "security_barrier",
140 : "View acts as a row security barrier",
141 : RELOPT_KIND_VIEW,
142 : AccessExclusiveLock
143 : },
144 : false
145 : },
146 : {
147 : {
148 : "security_invoker",
149 : "Privileges on underlying relations are checked as the invoking user, not the view owner",
150 : RELOPT_KIND_VIEW,
151 : AccessExclusiveLock
152 : },
153 : false
154 : },
155 : {
156 : {
157 : "deduplicate_items",
158 : "Enables \"deduplicate items\" feature for this btree index",
159 : RELOPT_KIND_BTREE,
160 : ShareUpdateExclusiveLock /* since it applies only to later
161 : * inserts */
162 : },
163 : true
164 : },
165 : /* list terminator */
166 : {{NULL}}
167 : };
168 :
169 : static relopt_ternary ternaryRelOpts[] =
170 : {
171 : {
172 : {
173 : "vacuum_truncate",
174 : "Enables vacuum to truncate empty pages at the end of this table",
175 : RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
176 : ShareUpdateExclusiveLock
177 : }
178 : },
179 : /* list terminator */
180 : {
181 : {
182 : NULL
183 : }
184 : }
185 : };
186 :
187 : static relopt_int intRelOpts[] =
188 : {
189 : {
190 : {
191 : "fillfactor",
192 : "Packs table pages only to this percentage",
193 : RELOPT_KIND_HEAP,
194 : ShareUpdateExclusiveLock /* since it applies only to later
195 : * inserts */
196 : },
197 : HEAP_DEFAULT_FILLFACTOR, HEAP_MIN_FILLFACTOR, 100
198 : },
199 : {
200 : {
201 : "fillfactor",
202 : "Packs btree index pages only to this percentage",
203 : RELOPT_KIND_BTREE,
204 : ShareUpdateExclusiveLock /* since it applies only to later
205 : * inserts */
206 : },
207 : BTREE_DEFAULT_FILLFACTOR, BTREE_MIN_FILLFACTOR, 100
208 : },
209 : {
210 : {
211 : "fillfactor",
212 : "Packs hash index pages only to this percentage",
213 : RELOPT_KIND_HASH,
214 : ShareUpdateExclusiveLock /* since it applies only to later
215 : * inserts */
216 : },
217 : HASH_DEFAULT_FILLFACTOR, HASH_MIN_FILLFACTOR, 100
218 : },
219 : {
220 : {
221 : "fillfactor",
222 : "Packs gist index pages only to this percentage",
223 : RELOPT_KIND_GIST,
224 : ShareUpdateExclusiveLock /* since it applies only to later
225 : * inserts */
226 : },
227 : GIST_DEFAULT_FILLFACTOR, GIST_MIN_FILLFACTOR, 100
228 : },
229 : {
230 : {
231 : "fillfactor",
232 : "Packs spgist index pages only to this percentage",
233 : RELOPT_KIND_SPGIST,
234 : ShareUpdateExclusiveLock /* since it applies only to later
235 : * inserts */
236 : },
237 : SPGIST_DEFAULT_FILLFACTOR, SPGIST_MIN_FILLFACTOR, 100
238 : },
239 : {
240 : {
241 : "autovacuum_parallel_workers",
242 : "Maximum number of parallel autovacuum workers that can be used for processing this table.",
243 : RELOPT_KIND_HEAP,
244 : ShareUpdateExclusiveLock
245 : },
246 : -1, -1, 1024
247 : },
248 : {
249 : {
250 : "autovacuum_vacuum_threshold",
251 : "Minimum number of tuple updates or deletes prior to vacuum",
252 : RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
253 : ShareUpdateExclusiveLock
254 : },
255 : -1, 0, INT_MAX
256 : },
257 : {
258 : {
259 : "autovacuum_vacuum_max_threshold",
260 : "Maximum number of tuple updates or deletes prior to vacuum",
261 : RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
262 : ShareUpdateExclusiveLock
263 : },
264 : -2, -1, INT_MAX
265 : },
266 : {
267 : {
268 : "autovacuum_vacuum_insert_threshold",
269 : "Minimum number of tuple inserts prior to vacuum, or -1 to disable insert vacuums",
270 : RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
271 : ShareUpdateExclusiveLock
272 : },
273 : -2, -1, INT_MAX
274 : },
275 : {
276 : {
277 : "autovacuum_analyze_threshold",
278 : "Minimum number of tuple inserts, updates or deletes prior to analyze",
279 : RELOPT_KIND_HEAP,
280 : ShareUpdateExclusiveLock
281 : },
282 : -1, 0, INT_MAX
283 : },
284 : {
285 : {
286 : "autovacuum_vacuum_cost_limit",
287 : "Vacuum cost amount available before napping, for autovacuum",
288 : RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
289 : ShareUpdateExclusiveLock
290 : },
291 : -1, 1, 10000
292 : },
293 : {
294 : {
295 : "autovacuum_freeze_min_age",
296 : "Minimum age at which VACUUM should freeze a table row, for autovacuum",
297 : RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
298 : ShareUpdateExclusiveLock
299 : },
300 : -1, 0, 1000000000
301 : },
302 : {
303 : {
304 : "autovacuum_multixact_freeze_min_age",
305 : "Minimum multixact age at which VACUUM should freeze a row multixact's, for autovacuum",
306 : RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
307 : ShareUpdateExclusiveLock
308 : },
309 : -1, 0, 1000000000
310 : },
311 : {
312 : {
313 : "autovacuum_freeze_max_age",
314 : "Age at which to autovacuum a table to prevent transaction ID wraparound",
315 : RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
316 : ShareUpdateExclusiveLock
317 : },
318 : -1, 100000, 2000000000
319 : },
320 : {
321 : {
322 : "autovacuum_multixact_freeze_max_age",
323 : "Multixact age at which to autovacuum a table to prevent multixact wraparound",
324 : RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
325 : ShareUpdateExclusiveLock
326 : },
327 : -1, 10000, 2000000000
328 : },
329 : {
330 : {
331 : "autovacuum_freeze_table_age",
332 : "Age at which VACUUM should perform a full table sweep to freeze row versions",
333 : RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
334 : ShareUpdateExclusiveLock
335 : }, -1, 0, 2000000000
336 : },
337 : {
338 : {
339 : "autovacuum_multixact_freeze_table_age",
340 : "Age of multixact at which VACUUM should perform a full table sweep to freeze row versions",
341 : RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
342 : ShareUpdateExclusiveLock
343 : }, -1, 0, 2000000000
344 : },
345 : {
346 : {
347 : "log_autovacuum_min_duration",
348 : "Sets the minimum execution time above which vacuum actions by autovacuum will be logged",
349 : RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
350 : ShareUpdateExclusiveLock
351 : },
352 : -1, -1, INT_MAX
353 : },
354 : {
355 : {
356 : "log_autoanalyze_min_duration",
357 : "Sets the minimum execution time above which analyze actions by autovacuum will be logged",
358 : RELOPT_KIND_HEAP,
359 : ShareUpdateExclusiveLock
360 : },
361 : -1, -1, INT_MAX
362 : },
363 : {
364 : {
365 : "toast_tuple_target",
366 : "Sets the target tuple length at which external columns will be toasted",
367 : RELOPT_KIND_HEAP,
368 : ShareUpdateExclusiveLock
369 : },
370 : TOAST_TUPLE_TARGET, 128, TOAST_TUPLE_TARGET_MAIN
371 : },
372 : {
373 : {
374 : "pages_per_range",
375 : "Number of pages that each page range covers in a BRIN index",
376 : RELOPT_KIND_BRIN,
377 : AccessExclusiveLock
378 : }, 128, 1, 131072
379 : },
380 : {
381 : {
382 : "gin_pending_list_limit",
383 : "Maximum size of the pending list for this GIN index, in kilobytes.",
384 : RELOPT_KIND_GIN,
385 : AccessExclusiveLock
386 : },
387 : -1, 64, MAX_KILOBYTES
388 : },
389 : {
390 : {
391 : "effective_io_concurrency",
392 : "Number of simultaneous requests that can be handled efficiently by the disk subsystem.",
393 : RELOPT_KIND_TABLESPACE,
394 : ShareUpdateExclusiveLock
395 : },
396 : -1, 0, MAX_IO_CONCURRENCY
397 : },
398 : {
399 : {
400 : "maintenance_io_concurrency",
401 : "Number of simultaneous requests that can be handled efficiently by the disk subsystem for maintenance work.",
402 : RELOPT_KIND_TABLESPACE,
403 : ShareUpdateExclusiveLock
404 : },
405 : -1, 0, MAX_IO_CONCURRENCY
406 : },
407 : {
408 : {
409 : "parallel_workers",
410 : "Number of parallel processes that can be used per executor node for this relation.",
411 : RELOPT_KIND_HEAP,
412 : ShareUpdateExclusiveLock
413 : },
414 : -1, 0, 1024
415 : },
416 :
417 : /* list terminator */
418 : {{NULL}}
419 : };
420 :
421 : static relopt_real realRelOpts[] =
422 : {
423 : {
424 : {
425 : "autovacuum_vacuum_cost_delay",
426 : "Vacuum cost delay in milliseconds, for autovacuum",
427 : RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
428 : ShareUpdateExclusiveLock
429 : },
430 : -1, 0.0, 100.0
431 : },
432 : {
433 : {
434 : "autovacuum_vacuum_scale_factor",
435 : "Number of tuple updates or deletes prior to vacuum as a fraction of reltuples",
436 : RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
437 : ShareUpdateExclusiveLock
438 : },
439 : -1, 0.0, 100.0
440 : },
441 : {
442 : {
443 : "autovacuum_vacuum_insert_scale_factor",
444 : "Number of tuple inserts prior to vacuum as a fraction of reltuples",
445 : RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
446 : ShareUpdateExclusiveLock
447 : },
448 : -1, 0.0, 100.0
449 : },
450 : {
451 : {
452 : "autovacuum_analyze_scale_factor",
453 : "Number of tuple inserts, updates or deletes prior to analyze as a fraction of reltuples",
454 : RELOPT_KIND_HEAP,
455 : ShareUpdateExclusiveLock
456 : },
457 : -1, 0.0, 100.0
458 : },
459 : {
460 : {
461 : "vacuum_max_eager_freeze_failure_rate",
462 : "Fraction of pages in a relation vacuum can scan and fail to freeze before disabling eager scanning.",
463 : RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
464 : ShareUpdateExclusiveLock
465 : },
466 : -1, 0.0, 1.0
467 : },
468 :
469 : {
470 : {
471 : "seq_page_cost",
472 : "Sets the planner's estimate of the cost of a sequentially fetched disk page.",
473 : RELOPT_KIND_TABLESPACE,
474 : ShareUpdateExclusiveLock
475 : },
476 : -1, 0.0, DBL_MAX
477 : },
478 : {
479 : {
480 : "random_page_cost",
481 : "Sets the planner's estimate of the cost of a nonsequentially fetched disk page.",
482 : RELOPT_KIND_TABLESPACE,
483 : ShareUpdateExclusiveLock
484 : },
485 : -1, 0.0, DBL_MAX
486 : },
487 : {
488 : {
489 : "n_distinct",
490 : "Sets the planner's estimate of the number of distinct values appearing in a column (excluding child relations).",
491 : RELOPT_KIND_ATTRIBUTE,
492 : ShareUpdateExclusiveLock
493 : },
494 : 0, -1.0, DBL_MAX
495 : },
496 : {
497 : {
498 : "n_distinct_inherited",
499 : "Sets the planner's estimate of the number of distinct values appearing in a column (including child relations).",
500 : RELOPT_KIND_ATTRIBUTE,
501 : ShareUpdateExclusiveLock
502 : },
503 : 0, -1.0, DBL_MAX
504 : },
505 : {
506 : {
507 : "vacuum_cleanup_index_scale_factor",
508 : "Deprecated B-Tree parameter.",
509 : RELOPT_KIND_BTREE,
510 : ShareUpdateExclusiveLock
511 : },
512 : -1, 0.0, 1e10
513 : },
514 : /* list terminator */
515 : {{NULL}}
516 : };
517 :
518 : /* values from StdRdOptIndexCleanup */
519 : static relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
520 : {
521 : {"auto", STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO},
522 : {"on", STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON},
523 : {"off", STDRD_OPTION_VACUUM_INDEX_CLEANUP_OFF},
524 : {"true", STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON},
525 : {"false", STDRD_OPTION_VACUUM_INDEX_CLEANUP_OFF},
526 : {"yes", STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON},
527 : {"no", STDRD_OPTION_VACUUM_INDEX_CLEANUP_OFF},
528 : {"1", STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON},
529 : {"0", STDRD_OPTION_VACUUM_INDEX_CLEANUP_OFF},
530 : {(const char *) NULL} /* list terminator */
531 : };
532 :
533 : /* values from GistOptBufferingMode */
534 : static relopt_enum_elt_def gistBufferingOptValues[] =
535 : {
536 : {"auto", GIST_OPTION_BUFFERING_AUTO},
537 : {"on", GIST_OPTION_BUFFERING_ON},
538 : {"off", GIST_OPTION_BUFFERING_OFF},
539 : {(const char *) NULL} /* list terminator */
540 : };
541 :
542 : /* values from ViewOptCheckOption */
543 : static relopt_enum_elt_def viewCheckOptValues[] =
544 : {
545 : /* no value for NOT_SET */
546 : {"local", VIEW_OPTION_CHECK_OPTION_LOCAL},
547 : {"cascaded", VIEW_OPTION_CHECK_OPTION_CASCADED},
548 : {(const char *) NULL} /* list terminator */
549 : };
550 :
551 : static relopt_enum enumRelOpts[] =
552 : {
553 : {
554 : {
555 : "vacuum_index_cleanup",
556 : "Controls index vacuuming and index cleanup",
557 : RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
558 : ShareUpdateExclusiveLock
559 : },
560 : StdRdOptIndexCleanupValues,
561 : STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO,
562 : gettext_noop("Valid values are \"on\", \"off\", and \"auto\".")
563 : },
564 : {
565 : {
566 : "buffering",
567 : "Enables buffering build for this GiST index",
568 : RELOPT_KIND_GIST,
569 : AccessExclusiveLock
570 : },
571 : gistBufferingOptValues,
572 : GIST_OPTION_BUFFERING_AUTO,
573 : gettext_noop("Valid values are \"on\", \"off\", and \"auto\".")
574 : },
575 : {
576 : {
577 : "check_option",
578 : "View has WITH CHECK OPTION defined (local or cascaded).",
579 : RELOPT_KIND_VIEW,
580 : AccessExclusiveLock
581 : },
582 : viewCheckOptValues,
583 : VIEW_OPTION_CHECK_OPTION_NOT_SET,
584 : gettext_noop("Valid values are \"local\" and \"cascaded\".")
585 : },
586 : /* list terminator */
587 : {{NULL}}
588 : };
589 :
590 : static relopt_string stringRelOpts[] =
591 : {
592 : /* list terminator */
593 : {{NULL}}
594 : };
595 :
596 : static relopt_gen **relOpts = NULL;
597 : static uint32 last_assigned_kind = RELOPT_KIND_LAST_DEFAULT;
598 :
599 : static int num_custom_options = 0;
600 : static relopt_gen **custom_options = NULL;
601 : static bool need_initialization = true;
602 :
603 : static void initialize_reloptions(void);
604 : static void parse_one_reloption(relopt_value *option, char *text_str,
605 : int text_len, bool validate);
606 :
607 : /*
608 : * Get the length of a string reloption (either default or the user-defined
609 : * value). This is used for allocation purposes when building a set of
610 : * relation options.
611 : */
612 : #define GET_STRING_RELOPTION_LEN(option) \
613 : ((option).isset ? strlen((option).string_val) : \
614 : ((relopt_string *) (option).gen)->default_len)
615 :
616 : /*
617 : * initialize_reloptions
618 : * initialization routine, must be called before parsing
619 : *
620 : * Initialize the relOpts array and fill each variable's type and name length.
621 : */
622 : static void
623 5278 : initialize_reloptions(void)
624 : {
625 : int i;
626 : int j;
627 :
628 5278 : j = 0;
629 42224 : for (i = 0; boolRelOpts[i].gen.name; i++)
630 : {
631 : Assert(DoLockModesConflict(boolRelOpts[i].gen.lockmode,
632 : boolRelOpts[i].gen.lockmode));
633 36946 : j++;
634 : }
635 10556 : for (i = 0; ternaryRelOpts[i].gen.name; i++)
636 : {
637 : Assert(DoLockModesConflict(ternaryRelOpts[i].gen.lockmode,
638 : ternaryRelOpts[i].gen.lockmode));
639 5278 : j++;
640 : }
641 :
642 137228 : for (i = 0; intRelOpts[i].gen.name; i++)
643 : {
644 : Assert(DoLockModesConflict(intRelOpts[i].gen.lockmode,
645 : intRelOpts[i].gen.lockmode));
646 131950 : j++;
647 : }
648 58058 : for (i = 0; realRelOpts[i].gen.name; i++)
649 : {
650 : Assert(DoLockModesConflict(realRelOpts[i].gen.lockmode,
651 : realRelOpts[i].gen.lockmode));
652 52780 : j++;
653 : }
654 21112 : for (i = 0; enumRelOpts[i].gen.name; i++)
655 : {
656 : Assert(DoLockModesConflict(enumRelOpts[i].gen.lockmode,
657 : enumRelOpts[i].gen.lockmode));
658 15834 : j++;
659 : }
660 5278 : for (i = 0; stringRelOpts[i].gen.name; i++)
661 : {
662 : Assert(DoLockModesConflict(stringRelOpts[i].gen.lockmode,
663 : stringRelOpts[i].gen.lockmode));
664 0 : j++;
665 : }
666 5278 : j += num_custom_options;
667 :
668 5278 : if (relOpts)
669 0 : pfree(relOpts);
670 10556 : relOpts = MemoryContextAlloc(TopMemoryContext,
671 5278 : (j + 1) * sizeof(relopt_gen *));
672 :
673 5278 : j = 0;
674 42224 : for (i = 0; boolRelOpts[i].gen.name; i++)
675 : {
676 36946 : relOpts[j] = &boolRelOpts[i].gen;
677 36946 : relOpts[j]->type = RELOPT_TYPE_BOOL;
678 36946 : relOpts[j]->namelen = strlen(relOpts[j]->name);
679 36946 : j++;
680 : }
681 :
682 10556 : for (i = 0; ternaryRelOpts[i].gen.name; i++)
683 : {
684 5278 : relOpts[j] = &ternaryRelOpts[i].gen;
685 5278 : relOpts[j]->type = RELOPT_TYPE_TERNARY;
686 5278 : relOpts[j]->namelen = strlen(relOpts[j]->name);
687 5278 : j++;
688 : }
689 :
690 137228 : for (i = 0; intRelOpts[i].gen.name; i++)
691 : {
692 131950 : relOpts[j] = &intRelOpts[i].gen;
693 131950 : relOpts[j]->type = RELOPT_TYPE_INT;
694 131950 : relOpts[j]->namelen = strlen(relOpts[j]->name);
695 131950 : j++;
696 : }
697 :
698 58058 : for (i = 0; realRelOpts[i].gen.name; i++)
699 : {
700 52780 : relOpts[j] = &realRelOpts[i].gen;
701 52780 : relOpts[j]->type = RELOPT_TYPE_REAL;
702 52780 : relOpts[j]->namelen = strlen(relOpts[j]->name);
703 52780 : j++;
704 : }
705 :
706 21112 : for (i = 0; enumRelOpts[i].gen.name; i++)
707 : {
708 15834 : relOpts[j] = &enumRelOpts[i].gen;
709 15834 : relOpts[j]->type = RELOPT_TYPE_ENUM;
710 15834 : relOpts[j]->namelen = strlen(relOpts[j]->name);
711 15834 : j++;
712 : }
713 :
714 5278 : for (i = 0; stringRelOpts[i].gen.name; i++)
715 : {
716 0 : relOpts[j] = &stringRelOpts[i].gen;
717 0 : relOpts[j]->type = RELOPT_TYPE_STRING;
718 0 : relOpts[j]->namelen = strlen(relOpts[j]->name);
719 0 : j++;
720 : }
721 :
722 8387 : for (i = 0; i < num_custom_options; i++)
723 : {
724 3109 : relOpts[j] = custom_options[i];
725 3109 : j++;
726 : }
727 :
728 : /* add a list terminator */
729 5278 : relOpts[j] = NULL;
730 :
731 : /* flag the work is complete */
732 5278 : need_initialization = false;
733 5278 : }
734 :
735 : /*
736 : * add_reloption_kind
737 : * Create a new relopt_kind value, to be used in custom reloptions by
738 : * user-defined AMs.
739 : */
740 : relopt_kind
741 96 : add_reloption_kind(void)
742 : {
743 : /* don't hand out the last bit so that the enum's behavior is portable */
744 96 : if (last_assigned_kind >= RELOPT_KIND_MAX)
745 0 : ereport(ERROR,
746 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
747 : errmsg("user-defined relation parameter types limit exceeded")));
748 96 : last_assigned_kind <<= 1;
749 96 : return (relopt_kind) last_assigned_kind;
750 : }
751 :
752 : /*
753 : * add_reloption
754 : * Add an already-created custom reloption to the list, and recompute the
755 : * main parser table.
756 : */
757 : static void
758 3142 : add_reloption(relopt_gen *newoption)
759 : {
760 : static int max_custom_options = 0;
761 :
762 3142 : if (num_custom_options >= max_custom_options)
763 : {
764 : MemoryContext oldcxt;
765 :
766 381 : oldcxt = MemoryContextSwitchTo(TopMemoryContext);
767 :
768 381 : if (max_custom_options == 0)
769 : {
770 96 : max_custom_options = 8;
771 96 : custom_options = palloc(max_custom_options * sizeof(relopt_gen *));
772 : }
773 : else
774 : {
775 285 : max_custom_options *= 2;
776 285 : custom_options = repalloc(custom_options,
777 : max_custom_options * sizeof(relopt_gen *));
778 : }
779 381 : MemoryContextSwitchTo(oldcxt);
780 : }
781 3142 : custom_options[num_custom_options++] = newoption;
782 :
783 3142 : need_initialization = true;
784 3142 : }
785 :
786 : /*
787 : * init_local_reloptions
788 : * Initialize local reloptions that will parsed into bytea structure of
789 : * 'relopt_struct_size'.
790 : */
791 : void
792 3218 : init_local_reloptions(local_relopts *relopts, Size relopt_struct_size)
793 : {
794 3218 : relopts->options = NIL;
795 3218 : relopts->validators = NIL;
796 3218 : relopts->relopt_struct_size = relopt_struct_size;
797 3218 : }
798 :
799 : /*
800 : * register_reloptions_validator
801 : * Register custom validation callback that will be called at the end of
802 : * build_local_reloptions().
803 : */
804 : void
805 14 : register_reloptions_validator(local_relopts *relopts, relopts_validator validator)
806 : {
807 14 : relopts->validators = lappend(relopts->validators, validator);
808 14 : }
809 :
810 : /*
811 : * add_local_reloption
812 : * Add an already-created custom reloption to the local list.
813 : */
814 : static void
815 2082 : add_local_reloption(local_relopts *relopts, relopt_gen *newoption, int offset)
816 : {
817 2082 : local_relopt *opt = palloc_object(local_relopt);
818 :
819 : Assert(offset < relopts->relopt_struct_size);
820 :
821 2082 : opt->option = newoption;
822 2082 : opt->offset = offset;
823 :
824 2082 : relopts->options = lappend(relopts->options, opt);
825 2082 : }
826 :
827 : /*
828 : * allocate_reloption
829 : * Allocate a new reloption and initialize the type-agnostic fields
830 : * (for types other than string)
831 : */
832 : static relopt_gen *
833 5224 : allocate_reloption(uint32 kinds, int type, const char *name, const char *desc,
834 : LOCKMODE lockmode)
835 : {
836 : MemoryContext oldcxt;
837 : size_t size;
838 : relopt_gen *newoption;
839 :
840 5224 : if (kinds != RELOPT_KIND_LOCAL)
841 3142 : oldcxt = MemoryContextSwitchTo(TopMemoryContext);
842 : else
843 2082 : oldcxt = NULL;
844 :
845 5224 : switch (type)
846 : {
847 1 : case RELOPT_TYPE_BOOL:
848 1 : size = sizeof(relopt_bool);
849 1 : break;
850 1 : case RELOPT_TYPE_TERNARY:
851 1 : size = sizeof(relopt_ternary);
852 1 : break;
853 4272 : case RELOPT_TYPE_INT:
854 4272 : size = sizeof(relopt_int);
855 4272 : break;
856 947 : case RELOPT_TYPE_REAL:
857 947 : size = sizeof(relopt_real);
858 947 : break;
859 1 : case RELOPT_TYPE_ENUM:
860 1 : size = sizeof(relopt_enum);
861 1 : break;
862 2 : case RELOPT_TYPE_STRING:
863 2 : size = sizeof(relopt_string);
864 2 : break;
865 0 : default:
866 0 : elog(ERROR, "unsupported reloption type %d", type);
867 : return NULL; /* keep compiler quiet */
868 : }
869 :
870 5224 : newoption = palloc(size);
871 :
872 5224 : newoption->name = pstrdup(name);
873 5224 : if (desc)
874 5223 : newoption->desc = pstrdup(desc);
875 : else
876 1 : newoption->desc = NULL;
877 5224 : newoption->kinds = kinds;
878 5224 : newoption->namelen = strlen(name);
879 5224 : newoption->type = type;
880 5224 : newoption->lockmode = lockmode;
881 :
882 5224 : if (oldcxt != NULL)
883 3142 : MemoryContextSwitchTo(oldcxt);
884 :
885 5224 : return newoption;
886 : }
887 :
888 : /*
889 : * init_bool_reloption
890 : * Allocate and initialize a new boolean reloption
891 : */
892 : static relopt_bool *
893 1 : init_bool_reloption(uint32 kinds, const char *name, const char *desc,
894 : bool default_val, LOCKMODE lockmode)
895 : {
896 : relopt_bool *newoption;
897 :
898 1 : newoption = (relopt_bool *) allocate_reloption(kinds, RELOPT_TYPE_BOOL,
899 : name, desc, lockmode);
900 1 : newoption->default_val = default_val;
901 :
902 1 : return newoption;
903 : }
904 :
905 : /*
906 : * add_bool_reloption
907 : * Add a new boolean reloption
908 : */
909 : void
910 1 : add_bool_reloption(uint32 kinds, const char *name, const char *desc,
911 : bool default_val, LOCKMODE lockmode)
912 : {
913 1 : relopt_bool *newoption = init_bool_reloption(kinds, name, desc,
914 : default_val, lockmode);
915 :
916 1 : add_reloption((relopt_gen *) newoption);
917 1 : }
918 :
919 : /*
920 : * add_local_bool_reloption
921 : * Add a new boolean local reloption
922 : *
923 : * 'offset' is offset of bool-typed field.
924 : */
925 : void
926 0 : add_local_bool_reloption(local_relopts *relopts, const char *name,
927 : const char *desc, bool default_val, int offset)
928 : {
929 0 : relopt_bool *newoption = init_bool_reloption(RELOPT_KIND_LOCAL,
930 : name, desc,
931 : default_val, 0);
932 :
933 0 : add_local_reloption(relopts, (relopt_gen *) newoption, offset);
934 0 : }
935 :
936 : /*
937 : * init_ternary_reloption
938 : * Allocate and initialize a new ternary reloption
939 : */
940 : static relopt_ternary *
941 1 : init_ternary_reloption(uint32 kinds, const char *name, const char *desc,
942 : LOCKMODE lockmode)
943 : {
944 : relopt_ternary *newoption;
945 :
946 : newoption = (relopt_ternary *)
947 1 : allocate_reloption(kinds, RELOPT_TYPE_TERNARY, name, desc, lockmode);
948 :
949 1 : return newoption;
950 : }
951 :
952 : /*
953 : * add_ternary_reloption
954 : * Add a new ternary reloption
955 : */
956 : void
957 1 : add_ternary_reloption(uint32 kinds, const char *name, const char *desc,
958 : LOCKMODE lockmode)
959 : {
960 : relopt_ternary *newoption;
961 :
962 : newoption =
963 1 : init_ternary_reloption(kinds, name, desc, lockmode);
964 :
965 1 : add_reloption((relopt_gen *) newoption);
966 1 : }
967 :
968 : /*
969 : * add_local_ternary_reloption
970 : * Add a new ternary local reloption
971 : *
972 : * 'offset' is offset of ternary-typed field.
973 : */
974 : void
975 0 : add_local_ternary_reloption(local_relopts *relopts, const char *name,
976 : const char *desc, int offset)
977 : {
978 : relopt_ternary *newoption;
979 :
980 : newoption =
981 0 : init_ternary_reloption(RELOPT_KIND_LOCAL, name, desc, 0);
982 :
983 0 : add_local_reloption(relopts, (relopt_gen *) newoption, offset);
984 0 : }
985 :
986 : /*
987 : * init_real_reloption
988 : * Allocate and initialize a new integer reloption
989 : */
990 : static relopt_int *
991 4272 : init_int_reloption(uint32 kinds, const char *name, const char *desc,
992 : int default_val, int min_val, int max_val,
993 : LOCKMODE lockmode)
994 : {
995 : relopt_int *newoption;
996 :
997 4272 : newoption = (relopt_int *) allocate_reloption(kinds, RELOPT_TYPE_INT,
998 : name, desc, lockmode);
999 4272 : newoption->default_val = default_val;
1000 4272 : newoption->min = min_val;
1001 4272 : newoption->max = max_val;
1002 :
1003 4272 : return newoption;
1004 : }
1005 :
1006 : /*
1007 : * add_int_reloption
1008 : * Add a new integer reloption
1009 : */
1010 : void
1011 3136 : add_int_reloption(uint32 kinds, const char *name, const char *desc, int default_val,
1012 : int min_val, int max_val, LOCKMODE lockmode)
1013 : {
1014 3136 : relopt_int *newoption = init_int_reloption(kinds, name, desc,
1015 : default_val, min_val,
1016 : max_val, lockmode);
1017 :
1018 3136 : add_reloption((relopt_gen *) newoption);
1019 3136 : }
1020 :
1021 : /*
1022 : * add_local_int_reloption
1023 : * Add a new local integer reloption
1024 : *
1025 : * 'offset' is offset of int-typed field.
1026 : */
1027 : void
1028 1136 : add_local_int_reloption(local_relopts *relopts, const char *name,
1029 : const char *desc, int default_val, int min_val,
1030 : int max_val, int offset)
1031 : {
1032 1136 : relopt_int *newoption = init_int_reloption(RELOPT_KIND_LOCAL,
1033 : name, desc, default_val,
1034 : min_val, max_val, 0);
1035 :
1036 1136 : add_local_reloption(relopts, (relopt_gen *) newoption, offset);
1037 1136 : }
1038 :
1039 : /*
1040 : * init_real_reloption
1041 : * Allocate and initialize a new real reloption
1042 : */
1043 : static relopt_real *
1044 947 : init_real_reloption(uint32 kinds, const char *name, const char *desc,
1045 : double default_val, double min_val, double max_val,
1046 : LOCKMODE lockmode)
1047 : {
1048 : relopt_real *newoption;
1049 :
1050 947 : newoption = (relopt_real *) allocate_reloption(kinds, RELOPT_TYPE_REAL,
1051 : name, desc, lockmode);
1052 947 : newoption->default_val = default_val;
1053 947 : newoption->min = min_val;
1054 947 : newoption->max = max_val;
1055 :
1056 947 : return newoption;
1057 : }
1058 :
1059 : /*
1060 : * add_real_reloption
1061 : * Add a new float reloption
1062 : */
1063 : void
1064 1 : add_real_reloption(uint32 kinds, const char *name, const char *desc,
1065 : double default_val, double min_val, double max_val,
1066 : LOCKMODE lockmode)
1067 : {
1068 1 : relopt_real *newoption = init_real_reloption(kinds, name, desc,
1069 : default_val, min_val,
1070 : max_val, lockmode);
1071 :
1072 1 : add_reloption((relopt_gen *) newoption);
1073 1 : }
1074 :
1075 : /*
1076 : * add_local_real_reloption
1077 : * Add a new local float reloption
1078 : *
1079 : * 'offset' is offset of double-typed field.
1080 : */
1081 : void
1082 946 : add_local_real_reloption(local_relopts *relopts, const char *name,
1083 : const char *desc, double default_val,
1084 : double min_val, double max_val, int offset)
1085 : {
1086 946 : relopt_real *newoption = init_real_reloption(RELOPT_KIND_LOCAL,
1087 : name, desc,
1088 : default_val, min_val,
1089 : max_val, 0);
1090 :
1091 946 : add_local_reloption(relopts, (relopt_gen *) newoption, offset);
1092 946 : }
1093 :
1094 : /*
1095 : * init_enum_reloption
1096 : * Allocate and initialize a new enum reloption
1097 : */
1098 : static relopt_enum *
1099 1 : init_enum_reloption(uint32 kinds, const char *name, const char *desc,
1100 : relopt_enum_elt_def *members, int default_val,
1101 : const char *detailmsg, LOCKMODE lockmode)
1102 : {
1103 : relopt_enum *newoption;
1104 :
1105 1 : newoption = (relopt_enum *) allocate_reloption(kinds, RELOPT_TYPE_ENUM,
1106 : name, desc, lockmode);
1107 1 : newoption->members = members;
1108 1 : newoption->default_val = default_val;
1109 1 : newoption->detailmsg = detailmsg;
1110 :
1111 1 : return newoption;
1112 : }
1113 :
1114 :
1115 : /*
1116 : * add_enum_reloption
1117 : * Add a new enum reloption
1118 : *
1119 : * The members array must have a terminating NULL entry.
1120 : *
1121 : * The detailmsg is shown when unsupported values are passed, and has this
1122 : * form: "Valid values are \"foo\", \"bar\", and \"bar\"."
1123 : *
1124 : * The members array and detailmsg are not copied -- caller must ensure that
1125 : * they are valid throughout the life of the process.
1126 : */
1127 : void
1128 1 : add_enum_reloption(uint32 kinds, const char *name, const char *desc,
1129 : relopt_enum_elt_def *members, int default_val,
1130 : const char *detailmsg, LOCKMODE lockmode)
1131 : {
1132 1 : relopt_enum *newoption = init_enum_reloption(kinds, name, desc,
1133 : members, default_val,
1134 : detailmsg, lockmode);
1135 :
1136 1 : add_reloption((relopt_gen *) newoption);
1137 1 : }
1138 :
1139 : /*
1140 : * add_local_enum_reloption
1141 : * Add a new local enum reloption
1142 : *
1143 : * 'offset' is offset of int-typed field.
1144 : */
1145 : void
1146 0 : add_local_enum_reloption(local_relopts *relopts, const char *name,
1147 : const char *desc, relopt_enum_elt_def *members,
1148 : int default_val, const char *detailmsg, int offset)
1149 : {
1150 0 : relopt_enum *newoption = init_enum_reloption(RELOPT_KIND_LOCAL,
1151 : name, desc,
1152 : members, default_val,
1153 : detailmsg, 0);
1154 :
1155 0 : add_local_reloption(relopts, (relopt_gen *) newoption, offset);
1156 0 : }
1157 :
1158 : /*
1159 : * init_string_reloption
1160 : * Allocate and initialize a new string reloption
1161 : */
1162 : static relopt_string *
1163 2 : init_string_reloption(uint32 kinds, const char *name, const char *desc,
1164 : const char *default_val,
1165 : validate_string_relopt validator,
1166 : fill_string_relopt filler,
1167 : LOCKMODE lockmode)
1168 : {
1169 : relopt_string *newoption;
1170 :
1171 : /* make sure the validator/default combination is sane */
1172 2 : if (validator)
1173 2 : (validator) (default_val);
1174 :
1175 2 : newoption = (relopt_string *) allocate_reloption(kinds, RELOPT_TYPE_STRING,
1176 : name, desc, lockmode);
1177 2 : newoption->validate_cb = validator;
1178 2 : newoption->fill_cb = filler;
1179 2 : if (default_val)
1180 : {
1181 1 : if (kinds == RELOPT_KIND_LOCAL)
1182 0 : newoption->default_val = strdup(default_val);
1183 : else
1184 1 : newoption->default_val = MemoryContextStrdup(TopMemoryContext, default_val);
1185 1 : newoption->default_len = strlen(default_val);
1186 1 : newoption->default_isnull = false;
1187 : }
1188 : else
1189 : {
1190 1 : newoption->default_val = "";
1191 1 : newoption->default_len = 0;
1192 1 : newoption->default_isnull = true;
1193 : }
1194 :
1195 2 : return newoption;
1196 : }
1197 :
1198 : /*
1199 : * add_string_reloption
1200 : * Add a new string reloption
1201 : *
1202 : * "validator" is an optional function pointer that can be used to test the
1203 : * validity of the values. It must elog(ERROR) when the argument string is
1204 : * not acceptable for the variable. Note that the default value must pass
1205 : * the validation.
1206 : */
1207 : void
1208 2 : add_string_reloption(uint32 kinds, const char *name, const char *desc,
1209 : const char *default_val, validate_string_relopt validator,
1210 : LOCKMODE lockmode)
1211 : {
1212 2 : relopt_string *newoption = init_string_reloption(kinds, name, desc,
1213 : default_val,
1214 : validator, NULL,
1215 : lockmode);
1216 :
1217 2 : add_reloption((relopt_gen *) newoption);
1218 2 : }
1219 :
1220 : /*
1221 : * add_local_string_reloption
1222 : * Add a new local string reloption
1223 : *
1224 : * 'offset' is offset of int-typed field that will store offset of string value
1225 : * in the resulting bytea structure.
1226 : */
1227 : void
1228 0 : add_local_string_reloption(local_relopts *relopts, const char *name,
1229 : const char *desc, const char *default_val,
1230 : validate_string_relopt validator,
1231 : fill_string_relopt filler, int offset)
1232 : {
1233 0 : relopt_string *newoption = init_string_reloption(RELOPT_KIND_LOCAL,
1234 : name, desc,
1235 : default_val,
1236 : validator, filler,
1237 : 0);
1238 :
1239 0 : add_local_reloption(relopts, (relopt_gen *) newoption, offset);
1240 0 : }
1241 :
1242 : /*
1243 : * Transform a relation options list (list of DefElem) into the text array
1244 : * format that is kept in pg_class.reloptions, including only those options
1245 : * that are in the passed namespace. The output values do not include the
1246 : * namespace.
1247 : *
1248 : * This is used for three cases: CREATE TABLE/INDEX, ALTER TABLE SET, and
1249 : * ALTER TABLE RESET. In the ALTER cases, oldOptions is the existing
1250 : * reloptions value (possibly NULL), and we replace or remove entries
1251 : * as needed.
1252 : *
1253 : * If acceptOidsOff is true, then we allow oids = false, but throw error when
1254 : * on. This is solely needed for backwards compatibility.
1255 : *
1256 : * Note that this is not responsible for determining whether the options
1257 : * are valid, but it does check that namespaces for all the options given are
1258 : * listed in validnsps. The NULL namespace is always valid and need not be
1259 : * explicitly listed. Passing a NULL pointer means that only the NULL
1260 : * namespace is valid.
1261 : *
1262 : * Both oldOptions and the result are text arrays (or NULL for "default"),
1263 : * but we declare them as Datums to avoid including array.h in reloptions.h.
1264 : */
1265 : Datum
1266 88430 : transformRelOptions(Datum oldOptions, List *defList, const char *nameSpace,
1267 : const char *const validnsps[], bool acceptOidsOff, bool isReset)
1268 : {
1269 : Datum result;
1270 : ArrayBuildState *astate;
1271 : ListCell *cell;
1272 :
1273 : /* no change if empty list */
1274 88430 : if (defList == NIL)
1275 85874 : return oldOptions;
1276 :
1277 : /* We build new array using accumArrayResult */
1278 2556 : astate = NULL;
1279 :
1280 : /* Copy any oldOptions that aren't to be replaced */
1281 2556 : if (DatumGetPointer(oldOptions) != NULL)
1282 : {
1283 229 : ArrayType *array = DatumGetArrayTypeP(oldOptions);
1284 : Datum *oldoptions;
1285 : int noldoptions;
1286 : int i;
1287 :
1288 229 : deconstruct_array_builtin(array, TEXTOID, &oldoptions, NULL, &noldoptions);
1289 :
1290 598 : for (i = 0; i < noldoptions; i++)
1291 : {
1292 369 : char *text_str = VARDATA(DatumGetPointer(oldoptions[i]));
1293 369 : int text_len = VARSIZE(DatumGetPointer(oldoptions[i])) - VARHDRSZ;
1294 :
1295 : /* Search for a match in defList */
1296 556 : foreach(cell, defList)
1297 : {
1298 403 : DefElem *def = (DefElem *) lfirst(cell);
1299 : int kw_len;
1300 :
1301 : /* ignore if not in the same namespace */
1302 403 : if (nameSpace == NULL)
1303 : {
1304 371 : if (def->defnamespace != NULL)
1305 0 : continue;
1306 : }
1307 32 : else if (def->defnamespace == NULL)
1308 20 : continue;
1309 12 : else if (strcmp(def->defnamespace, nameSpace) != 0)
1310 0 : continue;
1311 :
1312 383 : kw_len = strlen(def->defname);
1313 383 : if (text_len > kw_len && text_str[kw_len] == '=' &&
1314 228 : strncmp(text_str, def->defname, kw_len) == 0)
1315 216 : break;
1316 : }
1317 369 : if (!cell)
1318 : {
1319 : /* No match, so keep old option */
1320 153 : astate = accumArrayResult(astate, oldoptions[i],
1321 : false, TEXTOID,
1322 : CurrentMemoryContext);
1323 : }
1324 : }
1325 : }
1326 :
1327 : /*
1328 : * If CREATE/SET, add new options to array; if RESET, just check that the
1329 : * user didn't say RESET (option=val). (Must do this because the grammar
1330 : * doesn't enforce it.)
1331 : */
1332 5358 : foreach(cell, defList)
1333 : {
1334 2826 : DefElem *def = (DefElem *) lfirst(cell);
1335 :
1336 2826 : if (isReset)
1337 : {
1338 172 : if (def->arg != NULL)
1339 8 : ereport(ERROR,
1340 : (errcode(ERRCODE_SYNTAX_ERROR),
1341 : errmsg("RESET must not include values for parameters")));
1342 : }
1343 : else
1344 : {
1345 : const char *name;
1346 : const char *value;
1347 : text *t;
1348 : Size len;
1349 :
1350 : /*
1351 : * Error out if the namespace is not valid. A NULL namespace is
1352 : * always valid.
1353 : */
1354 2654 : if (def->defnamespace != NULL)
1355 : {
1356 84 : bool valid = false;
1357 : int i;
1358 :
1359 84 : if (validnsps)
1360 : {
1361 84 : for (i = 0; validnsps[i]; i++)
1362 : {
1363 80 : if (strcmp(def->defnamespace, validnsps[i]) == 0)
1364 : {
1365 76 : valid = true;
1366 76 : break;
1367 : }
1368 : }
1369 : }
1370 :
1371 84 : if (!valid)
1372 8 : ereport(ERROR,
1373 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1374 : errmsg("unrecognized parameter namespace \"%s\"",
1375 : def->defnamespace)));
1376 : }
1377 :
1378 : /* ignore if not in the same namespace */
1379 2646 : if (nameSpace == NULL)
1380 : {
1381 1915 : if (def->defnamespace != NULL)
1382 38 : continue;
1383 : }
1384 731 : else if (def->defnamespace == NULL)
1385 693 : continue;
1386 38 : else if (strcmp(def->defnamespace, nameSpace) != 0)
1387 0 : continue;
1388 :
1389 : /*
1390 : * Flatten the DefElem into a text string like "name=arg". If we
1391 : * have just "name", assume "name=true" is meant. Note: the
1392 : * namespace is not output.
1393 : */
1394 1915 : name = def->defname;
1395 1915 : if (def->arg != NULL)
1396 1669 : value = defGetString(def);
1397 : else
1398 246 : value = "true";
1399 :
1400 : /* Insist that name not contain "=", else "a=b=c" is ambiguous */
1401 1915 : if (strchr(name, '=') != NULL)
1402 0 : ereport(ERROR,
1403 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1404 : errmsg("invalid option name \"%s\": must not contain \"=\"",
1405 : name)));
1406 :
1407 : /*
1408 : * This is not a great place for this test, but there's no other
1409 : * convenient place to filter the option out. As WITH (oids =
1410 : * false) will be removed someday, this seems like an acceptable
1411 : * amount of ugly.
1412 : */
1413 1915 : if (acceptOidsOff && def->defnamespace == NULL &&
1414 1015 : strcmp(name, "oids") == 0)
1415 : {
1416 12 : if (defGetBoolean(def))
1417 8 : ereport(ERROR,
1418 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1419 : errmsg("tables declared WITH OIDS are not supported")));
1420 : /* skip over option, reloptions machinery doesn't know it */
1421 4 : continue;
1422 : }
1423 :
1424 1903 : len = VARHDRSZ + strlen(name) + 1 + strlen(value);
1425 : /* +1 leaves room for sprintf's trailing null */
1426 1903 : t = (text *) palloc(len + 1);
1427 1903 : SET_VARSIZE(t, len);
1428 1903 : sprintf(VARDATA(t), "%s=%s", name, value);
1429 :
1430 1903 : astate = accumArrayResult(astate, PointerGetDatum(t),
1431 : false, TEXTOID,
1432 : CurrentMemoryContext);
1433 : }
1434 : }
1435 :
1436 2532 : if (astate)
1437 1789 : result = makeArrayResult(astate, CurrentMemoryContext);
1438 : else
1439 743 : result = (Datum) 0;
1440 :
1441 2532 : return result;
1442 : }
1443 :
1444 :
1445 : /*
1446 : * Convert the text-array format of reloptions into a List of DefElem.
1447 : * This is the inverse of transformRelOptions().
1448 : */
1449 : List *
1450 17272 : untransformRelOptions(Datum options)
1451 : {
1452 17272 : List *result = NIL;
1453 : ArrayType *array;
1454 : Datum *optiondatums;
1455 : int noptions;
1456 : int i;
1457 :
1458 : /* Nothing to do if no options */
1459 17272 : if (DatumGetPointer(options) == NULL)
1460 3416 : return result;
1461 :
1462 13856 : array = DatumGetArrayTypeP(options);
1463 :
1464 13856 : deconstruct_array_builtin(array, TEXTOID, &optiondatums, NULL, &noptions);
1465 :
1466 41686 : for (i = 0; i < noptions; i++)
1467 : {
1468 : char *s;
1469 : char *p;
1470 27830 : Node *val = NULL;
1471 :
1472 27830 : s = TextDatumGetCString(optiondatums[i]);
1473 27830 : p = strchr(s, '=');
1474 27830 : if (p)
1475 : {
1476 27830 : *p++ = '\0';
1477 27830 : val = (Node *) makeString(p);
1478 : }
1479 27830 : result = lappend(result, makeDefElem(s, val, -1));
1480 : }
1481 :
1482 13856 : return result;
1483 : }
1484 :
1485 : /*
1486 : * Extract and parse reloptions from a pg_class tuple.
1487 : *
1488 : * This is a low-level routine, expected to be used by relcache code and
1489 : * callers that do not have a table's relcache entry (e.g. autovacuum). For
1490 : * other uses, consider grabbing the rd_options pointer from the relcache entry
1491 : * instead.
1492 : *
1493 : * tupdesc is pg_class' tuple descriptor. amoptions is a pointer to the index
1494 : * AM's options parser function in the case of a tuple corresponding to an
1495 : * index, or NULL otherwise.
1496 : */
1497 : bytea *
1498 1387966 : extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
1499 : amoptions_function amoptions)
1500 : {
1501 : bytea *options;
1502 : bool isnull;
1503 : Datum datum;
1504 : Form_pg_class classForm;
1505 :
1506 1387966 : datum = fastgetattr(tuple,
1507 : Anum_pg_class_reloptions,
1508 : tupdesc,
1509 : &isnull);
1510 1387966 : if (isnull)
1511 1373154 : return NULL;
1512 :
1513 14812 : classForm = (Form_pg_class) GETSTRUCT(tuple);
1514 :
1515 : /* Parse into appropriate format; don't error out here */
1516 14812 : switch (classForm->relkind)
1517 : {
1518 11903 : case RELKIND_RELATION:
1519 : case RELKIND_TOASTVALUE:
1520 : case RELKIND_MATVIEW:
1521 11903 : options = heap_reloptions(classForm->relkind, datum, false);
1522 11903 : break;
1523 0 : case RELKIND_PARTITIONED_TABLE:
1524 0 : options = partitioned_table_reloptions(datum, false);
1525 0 : break;
1526 1480 : case RELKIND_VIEW:
1527 1480 : options = view_reloptions(datum, false);
1528 1480 : break;
1529 1429 : case RELKIND_INDEX:
1530 : case RELKIND_PARTITIONED_INDEX:
1531 1429 : options = index_reloptions(amoptions, datum, false);
1532 1429 : break;
1533 0 : case RELKIND_FOREIGN_TABLE:
1534 0 : options = NULL;
1535 0 : break;
1536 0 : default:
1537 : Assert(false); /* can't get here */
1538 0 : options = NULL; /* keep compiler quiet */
1539 0 : break;
1540 : }
1541 :
1542 14812 : return options;
1543 : }
1544 :
1545 : static void
1546 16792 : parseRelOptionsInternal(Datum options, bool validate,
1547 : relopt_value *reloptions, int numoptions)
1548 : {
1549 16792 : ArrayType *array = DatumGetArrayTypeP(options);
1550 : Datum *optiondatums;
1551 : int noptions;
1552 : int i;
1553 :
1554 16792 : deconstruct_array_builtin(array, TEXTOID, &optiondatums, NULL, &noptions);
1555 :
1556 34829 : for (i = 0; i < noptions; i++)
1557 : {
1558 18246 : char *text_str = VARDATA(DatumGetPointer(optiondatums[i]));
1559 18246 : int text_len = VARSIZE(DatumGetPointer(optiondatums[i])) - VARHDRSZ;
1560 : int j;
1561 :
1562 : /* Search for a match in reloptions */
1563 78296 : for (j = 0; j < numoptions; j++)
1564 : {
1565 78240 : int kw_len = reloptions[j].gen->namelen;
1566 :
1567 78240 : if (text_len > kw_len && text_str[kw_len] == '=' &&
1568 19226 : strncmp(text_str, reloptions[j].gen->name, kw_len) == 0)
1569 : {
1570 18190 : parse_one_reloption(&reloptions[j], text_str, text_len,
1571 : validate);
1572 18025 : break;
1573 : }
1574 : }
1575 :
1576 18081 : if (j >= numoptions && validate)
1577 : {
1578 : char *s;
1579 : char *p;
1580 :
1581 44 : s = TextDatumGetCString(optiondatums[i]);
1582 44 : p = strchr(s, '=');
1583 44 : if (p)
1584 44 : *p = '\0';
1585 44 : ereport(ERROR,
1586 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1587 : errmsg("unrecognized parameter \"%s\"", s)));
1588 : }
1589 : }
1590 :
1591 : /* It's worth avoiding memory leaks in this function */
1592 16583 : pfree(optiondatums);
1593 :
1594 16583 : if (((void *) array) != DatumGetPointer(options))
1595 14995 : pfree(array);
1596 16583 : }
1597 :
1598 : /*
1599 : * Interpret reloptions that are given in text-array format.
1600 : *
1601 : * options is a reloption text array as constructed by transformRelOptions.
1602 : * kind specifies the family of options to be processed.
1603 : *
1604 : * The return value is a relopt_value * array on which the options actually
1605 : * set in the options array are marked with isset=true. The length of this
1606 : * array is returned in *numrelopts. Options not set are also present in the
1607 : * array; this is so that the caller can easily locate the default values.
1608 : *
1609 : * If there are no options of the given kind, numrelopts is set to 0 and NULL
1610 : * is returned (unless options are illegally supplied despite none being
1611 : * defined, in which case an error occurs).
1612 : *
1613 : * Note: values of type int, bool and real are allocated as part of the
1614 : * returned array. Values of type string are allocated separately and must
1615 : * be freed by the caller.
1616 : */
1617 : static relopt_value *
1618 75963 : parseRelOptions(Datum options, bool validate, relopt_kind kind,
1619 : int *numrelopts)
1620 : {
1621 75963 : relopt_value *reloptions = NULL;
1622 75963 : int numoptions = 0;
1623 : int i;
1624 : int j;
1625 :
1626 75963 : if (need_initialization)
1627 5269 : initialize_reloptions();
1628 :
1629 : /* Build a list of expected options, based on kind */
1630 :
1631 3575363 : for (i = 0; relOpts[i]; i++)
1632 3499400 : if (relOpts[i]->kinds & kind)
1633 1438847 : numoptions++;
1634 :
1635 75963 : if (numoptions > 0)
1636 : {
1637 75963 : reloptions = palloc(numoptions * sizeof(relopt_value));
1638 :
1639 3575363 : for (i = 0, j = 0; relOpts[i]; i++)
1640 : {
1641 3499400 : if (relOpts[i]->kinds & kind)
1642 : {
1643 1438847 : reloptions[j].gen = relOpts[i];
1644 1438847 : reloptions[j].isset = false;
1645 1438847 : j++;
1646 : }
1647 : }
1648 : }
1649 :
1650 : /* Done if no options */
1651 75963 : if (DatumGetPointer(options) != NULL)
1652 16513 : parseRelOptionsInternal(options, validate, reloptions, numoptions);
1653 :
1654 75807 : *numrelopts = numoptions;
1655 75807 : return reloptions;
1656 : }
1657 :
1658 : /* Parse local unregistered options. */
1659 : static relopt_value *
1660 1609 : parseLocalRelOptions(local_relopts *relopts, Datum options, bool validate)
1661 : {
1662 1609 : int nopts = list_length(relopts->options);
1663 1609 : relopt_value *values = palloc_array(relopt_value, nopts);
1664 : ListCell *lc;
1665 1609 : int i = 0;
1666 :
1667 3691 : foreach(lc, relopts->options)
1668 : {
1669 2082 : local_relopt *opt = lfirst(lc);
1670 :
1671 2082 : values[i].gen = opt->option;
1672 2082 : values[i].isset = false;
1673 :
1674 2082 : i++;
1675 : }
1676 :
1677 1609 : if (options != (Datum) 0)
1678 279 : parseRelOptionsInternal(options, validate, values, nopts);
1679 :
1680 1556 : return values;
1681 : }
1682 :
1683 : /*
1684 : * Subroutine for parseRelOptions, to parse and validate a single option's
1685 : * value
1686 : */
1687 : static void
1688 18190 : parse_one_reloption(relopt_value *option, char *text_str, int text_len,
1689 : bool validate)
1690 : {
1691 : char *value;
1692 : int value_len;
1693 : bool parsed;
1694 18190 : bool nofree = false;
1695 :
1696 18190 : if (option->isset && validate)
1697 8 : ereport(ERROR,
1698 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1699 : errmsg("parameter \"%s\" specified more than once",
1700 : option->gen->name)));
1701 :
1702 18182 : value_len = text_len - option->gen->namelen - 1;
1703 18182 : value = (char *) palloc(value_len + 1);
1704 18182 : memcpy(value, text_str + option->gen->namelen + 1, value_len);
1705 18182 : value[value_len] = '\0';
1706 :
1707 18182 : switch (option->gen->type)
1708 : {
1709 10984 : case RELOPT_TYPE_BOOL:
1710 : {
1711 10984 : parsed = parse_bool(value, &option->bool_val);
1712 10984 : if (validate && !parsed)
1713 23 : ereport(ERROR,
1714 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1715 : errmsg("invalid value for boolean option \"%s\": %s",
1716 : option->gen->name, value)));
1717 : }
1718 10961 : break;
1719 232 : case RELOPT_TYPE_TERNARY:
1720 : {
1721 : bool b;
1722 :
1723 232 : parsed = parse_bool(value, &b);
1724 232 : option->ternary_val = b ? PG_TERNARY_TRUE :
1725 : PG_TERNARY_FALSE;
1726 232 : if (validate && !parsed)
1727 3 : ereport(ERROR,
1728 : errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1729 : errmsg("invalid value for boolean option \"%s\": %s",
1730 : option->gen->name, value));
1731 : }
1732 229 : break;
1733 5786 : case RELOPT_TYPE_INT:
1734 : {
1735 5786 : relopt_int *optint = (relopt_int *) option->gen;
1736 :
1737 5786 : parsed = parse_int(value, &option->int_val, 0, NULL);
1738 5786 : if (validate && !parsed)
1739 14 : ereport(ERROR,
1740 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1741 : errmsg("invalid value for integer option \"%s\": %s",
1742 : option->gen->name, value)));
1743 5772 : if (validate && (option->int_val < optint->min ||
1744 560 : option->int_val > optint->max))
1745 75 : ereport(ERROR,
1746 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1747 : errmsg("value %s out of bounds for option \"%s\"",
1748 : value, option->gen->name),
1749 : errdetail("Valid values are between \"%d\" and \"%d\".",
1750 : optint->min, optint->max)));
1751 : }
1752 5697 : break;
1753 309 : case RELOPT_TYPE_REAL:
1754 : {
1755 309 : relopt_real *optreal = (relopt_real *) option->gen;
1756 :
1757 309 : parsed = parse_real(value, &option->real_val, 0, NULL);
1758 309 : if (validate && !parsed)
1759 10 : ereport(ERROR,
1760 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1761 : errmsg("invalid value for floating point option \"%s\": %s",
1762 : option->gen->name, value)));
1763 299 : if (validate && (option->real_val < optreal->min ||
1764 113 : option->real_val > optreal->max))
1765 20 : ereport(ERROR,
1766 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1767 : errmsg("value %s out of bounds for option \"%s\"",
1768 : value, option->gen->name),
1769 : errdetail("Valid values are between \"%f\" and \"%f\".",
1770 : optreal->min, optreal->max)));
1771 : }
1772 279 : break;
1773 777 : case RELOPT_TYPE_ENUM:
1774 : {
1775 777 : relopt_enum *optenum = (relopt_enum *) option->gen;
1776 : relopt_enum_elt_def *elt;
1777 :
1778 777 : parsed = false;
1779 1757 : for (elt = optenum->members; elt->string_val; elt++)
1780 : {
1781 1745 : if (pg_strcasecmp(value, elt->string_val) == 0)
1782 : {
1783 765 : option->enum_val = elt->symbol_val;
1784 765 : parsed = true;
1785 765 : break;
1786 : }
1787 : }
1788 777 : if (validate && !parsed)
1789 12 : ereport(ERROR,
1790 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1791 : errmsg("invalid value for enum option \"%s\": %s",
1792 : option->gen->name, value),
1793 : optenum->detailmsg ?
1794 : errdetail_internal("%s", _(optenum->detailmsg)) : 0));
1795 :
1796 : /*
1797 : * If value is not among the allowed string values, but we are
1798 : * not asked to validate, just use the default numeric value.
1799 : */
1800 765 : if (!parsed)
1801 0 : option->enum_val = optenum->default_val;
1802 : }
1803 765 : break;
1804 94 : case RELOPT_TYPE_STRING:
1805 : {
1806 94 : relopt_string *optstring = (relopt_string *) option->gen;
1807 :
1808 94 : option->string_val = value;
1809 94 : nofree = true;
1810 94 : if (validate && optstring->validate_cb)
1811 32 : (optstring->validate_cb) (value);
1812 94 : parsed = true;
1813 : }
1814 94 : break;
1815 0 : default:
1816 0 : elog(ERROR, "unsupported reloption type %d", option->gen->type);
1817 : parsed = true; /* quiet compiler */
1818 : break;
1819 : }
1820 :
1821 18025 : if (parsed)
1822 18025 : option->isset = true;
1823 18025 : if (!nofree)
1824 17931 : pfree(value);
1825 18025 : }
1826 :
1827 : /*
1828 : * Given the result from parseRelOptions, allocate a struct that's of the
1829 : * specified base size plus any extra space that's needed for string variables.
1830 : *
1831 : * "base" should be sizeof(struct) of the reloptions struct (StdRdOptions or
1832 : * equivalent).
1833 : */
1834 : static void *
1835 77363 : allocateReloptStruct(Size base, relopt_value *options, int numoptions)
1836 : {
1837 77363 : Size size = base;
1838 : int i;
1839 :
1840 1516147 : for (i = 0; i < numoptions; i++)
1841 : {
1842 1438784 : relopt_value *optval = &options[i];
1843 :
1844 1438784 : if (optval->gen->type == RELOPT_TYPE_STRING)
1845 : {
1846 134 : relopt_string *optstr = (relopt_string *) optval->gen;
1847 :
1848 134 : if (optstr->fill_cb)
1849 : {
1850 0 : const char *val = optval->isset ? optval->string_val :
1851 0 : optstr->default_isnull ? NULL : optstr->default_val;
1852 :
1853 0 : size += optstr->fill_cb(val, NULL);
1854 : }
1855 : else
1856 134 : size += GET_STRING_RELOPTION_LEN(*optval) + 1;
1857 : }
1858 : }
1859 :
1860 77363 : return palloc0(size);
1861 : }
1862 :
1863 : /*
1864 : * Given the result of parseRelOptions and a parsing table, fill in the
1865 : * struct (previously allocated with allocateReloptStruct) with the parsed
1866 : * values.
1867 : *
1868 : * rdopts is the pointer to the allocated struct to be filled.
1869 : * basesize is the sizeof(struct) that was passed to allocateReloptStruct.
1870 : * options, of length numoptions, is parseRelOptions' output.
1871 : * elems, of length numelems, is the table describing the allowed options.
1872 : * When validate is true, it is expected that all options appear in elems.
1873 : */
1874 : static void
1875 77363 : fillRelOptions(void *rdopts, Size basesize,
1876 : relopt_value *options, int numoptions,
1877 : bool validate,
1878 : const relopt_parse_elt *elems, int numelems)
1879 : {
1880 : int i;
1881 77363 : int offset = basesize;
1882 :
1883 1516147 : for (i = 0; i < numoptions; i++)
1884 : {
1885 : int j;
1886 1438784 : bool found = false;
1887 :
1888 18888913 : for (j = 0; j < numelems; j++)
1889 : {
1890 18888913 : if (strcmp(options[i].gen->name, elems[j].optname) == 0)
1891 : {
1892 : relopt_string *optstring;
1893 1438784 : char *itempos = ((char *) rdopts) + elems[j].offset;
1894 : char *string_val;
1895 :
1896 1438784 : switch (options[i].gen->type)
1897 : {
1898 122989 : case RELOPT_TYPE_BOOL:
1899 245978 : *(bool *) itempos = options[i].isset ?
1900 10960 : options[i].bool_val :
1901 122989 : ((relopt_bool *) options[i].gen)->default_val;
1902 122989 : break;
1903 61679 : case RELOPT_TYPE_TERNARY:
1904 123358 : *(pg_ternary *) itempos = options[i].isset ?
1905 61679 : options[i].ternary_val : PG_TERNARY_UNSET;
1906 61679 : break;
1907 896668 : case RELOPT_TYPE_INT:
1908 1793336 : *(int *) itempos = options[i].isset ?
1909 896668 : options[i].int_val :
1910 890988 : ((relopt_int *) options[i].gen)->default_val;
1911 896668 : break;
1912 283141 : case RELOPT_TYPE_REAL:
1913 566282 : *(double *) itempos = options[i].isset ?
1914 283141 : options[i].real_val :
1915 282871 : ((relopt_real *) options[i].gen)->default_val;
1916 283141 : break;
1917 74173 : case RELOPT_TYPE_ENUM:
1918 148346 : *(int *) itempos = options[i].isset ?
1919 74173 : options[i].enum_val :
1920 73408 : ((relopt_enum *) options[i].gen)->default_val;
1921 74173 : break;
1922 134 : case RELOPT_TYPE_STRING:
1923 134 : optstring = (relopt_string *) options[i].gen;
1924 134 : if (options[i].isset)
1925 92 : string_val = options[i].string_val;
1926 42 : else if (!optstring->default_isnull)
1927 18 : string_val = optstring->default_val;
1928 : else
1929 24 : string_val = NULL;
1930 :
1931 134 : if (optstring->fill_cb)
1932 : {
1933 : Size size =
1934 0 : optstring->fill_cb(string_val,
1935 : (char *) rdopts + offset);
1936 :
1937 0 : if (size)
1938 : {
1939 0 : *(int *) itempos = offset;
1940 0 : offset += size;
1941 : }
1942 : else
1943 0 : *(int *) itempos = 0;
1944 : }
1945 134 : else if (string_val == NULL)
1946 24 : *(int *) itempos = 0;
1947 : else
1948 : {
1949 110 : strcpy((char *) rdopts + offset, string_val);
1950 110 : *(int *) itempos = offset;
1951 110 : offset += strlen(string_val) + 1;
1952 : }
1953 134 : break;
1954 0 : default:
1955 0 : elog(ERROR, "unsupported reloption type %d",
1956 : options[i].gen->type);
1957 : break;
1958 : }
1959 1438784 : found = true;
1960 1438784 : break;
1961 : }
1962 : }
1963 1438784 : if (validate && !found)
1964 0 : elog(ERROR, "reloption \"%s\" not found in parse table",
1965 : options[i].gen->name);
1966 : }
1967 77363 : SET_VARSIZE(rdopts, offset);
1968 77363 : }
1969 :
1970 :
1971 : /*
1972 : * Option parser for anything that uses StdRdOptions.
1973 : */
1974 : bytea *
1975 61680 : default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
1976 : {
1977 : static const relopt_parse_elt tab[] = {
1978 : {"fillfactor", RELOPT_TYPE_INT, offsetof(StdRdOptions, fillfactor)},
1979 : {"autovacuum_enabled", RELOPT_TYPE_BOOL,
1980 : offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, enabled)},
1981 : {"autovacuum_parallel_workers", RELOPT_TYPE_INT,
1982 : offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, autovacuum_parallel_workers)},
1983 : {"autovacuum_vacuum_threshold", RELOPT_TYPE_INT,
1984 : offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_threshold)},
1985 : {"autovacuum_vacuum_max_threshold", RELOPT_TYPE_INT,
1986 : offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_max_threshold)},
1987 : {"autovacuum_vacuum_insert_threshold", RELOPT_TYPE_INT,
1988 : offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_ins_threshold)},
1989 : {"autovacuum_analyze_threshold", RELOPT_TYPE_INT,
1990 : offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, analyze_threshold)},
1991 : {"autovacuum_vacuum_cost_limit", RELOPT_TYPE_INT,
1992 : offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_cost_limit)},
1993 : {"autovacuum_freeze_min_age", RELOPT_TYPE_INT,
1994 : offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_min_age)},
1995 : {"autovacuum_freeze_max_age", RELOPT_TYPE_INT,
1996 : offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_max_age)},
1997 : {"autovacuum_freeze_table_age", RELOPT_TYPE_INT,
1998 : offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_table_age)},
1999 : {"autovacuum_multixact_freeze_min_age", RELOPT_TYPE_INT,
2000 : offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_min_age)},
2001 : {"autovacuum_multixact_freeze_max_age", RELOPT_TYPE_INT,
2002 : offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_max_age)},
2003 : {"autovacuum_multixact_freeze_table_age", RELOPT_TYPE_INT,
2004 : offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_table_age)},
2005 : {"log_autovacuum_min_duration", RELOPT_TYPE_INT,
2006 : offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, log_vacuum_min_duration)},
2007 : {"log_autoanalyze_min_duration", RELOPT_TYPE_INT,
2008 : offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, log_analyze_min_duration)},
2009 : {"toast_tuple_target", RELOPT_TYPE_INT,
2010 : offsetof(StdRdOptions, toast_tuple_target)},
2011 : {"autovacuum_vacuum_cost_delay", RELOPT_TYPE_REAL,
2012 : offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_cost_delay)},
2013 : {"autovacuum_vacuum_scale_factor", RELOPT_TYPE_REAL,
2014 : offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_scale_factor)},
2015 : {"autovacuum_vacuum_insert_scale_factor", RELOPT_TYPE_REAL,
2016 : offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_ins_scale_factor)},
2017 : {"autovacuum_analyze_scale_factor", RELOPT_TYPE_REAL,
2018 : offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, analyze_scale_factor)},
2019 : {"user_catalog_table", RELOPT_TYPE_BOOL,
2020 : offsetof(StdRdOptions, user_catalog_table)},
2021 : {"parallel_workers", RELOPT_TYPE_INT,
2022 : offsetof(StdRdOptions, parallel_workers)},
2023 : {"vacuum_index_cleanup", RELOPT_TYPE_ENUM,
2024 : offsetof(StdRdOptions, vacuum_index_cleanup)},
2025 : {"vacuum_truncate", RELOPT_TYPE_TERNARY,
2026 : offsetof(StdRdOptions, vacuum_truncate)},
2027 : {"vacuum_max_eager_freeze_failure_rate", RELOPT_TYPE_REAL,
2028 : offsetof(StdRdOptions, vacuum_max_eager_freeze_failure_rate)}
2029 : };
2030 :
2031 61680 : return (bytea *) build_reloptions(reloptions, validate, kind,
2032 : sizeof(StdRdOptions),
2033 : tab, lengthof(tab));
2034 : }
2035 :
2036 : /*
2037 : * build_reloptions
2038 : *
2039 : * Parses "reloptions" provided by the caller, returning them in a
2040 : * structure containing the parsed options. The parsing is done with
2041 : * the help of a parsing table describing the allowed options, defined
2042 : * by "relopt_elems" of length "num_relopt_elems".
2043 : *
2044 : * "validate" must be true if reloptions value is freshly built by
2045 : * transformRelOptions(), as opposed to being read from the catalog, in which
2046 : * case the values contained in it must already be valid.
2047 : *
2048 : * NULL is returned if the passed-in options did not match any of the options
2049 : * in the parsing table, unless validate is true in which case an error would
2050 : * be reported.
2051 : */
2052 : void *
2053 75963 : build_reloptions(Datum reloptions, bool validate,
2054 : relopt_kind kind,
2055 : Size relopt_struct_size,
2056 : const relopt_parse_elt *relopt_elems,
2057 : int num_relopt_elems)
2058 : {
2059 : int numoptions;
2060 : relopt_value *options;
2061 : void *rdopts;
2062 :
2063 : /* parse options specific to given relation option kind */
2064 75963 : options = parseRelOptions(reloptions, validate, kind, &numoptions);
2065 : Assert(numoptions <= num_relopt_elems);
2066 :
2067 : /* if none set, we're done */
2068 75807 : if (numoptions == 0)
2069 : {
2070 : Assert(options == NULL);
2071 0 : return NULL;
2072 : }
2073 :
2074 : /* allocate and fill the structure */
2075 75807 : rdopts = allocateReloptStruct(relopt_struct_size, options, numoptions);
2076 75807 : fillRelOptions(rdopts, relopt_struct_size, options, numoptions,
2077 : validate, relopt_elems, num_relopt_elems);
2078 :
2079 75807 : pfree(options);
2080 :
2081 75807 : return rdopts;
2082 : }
2083 :
2084 : /*
2085 : * Parse local options, allocate a bytea struct that's of the specified
2086 : * 'base_size' plus any extra space that's needed for string variables,
2087 : * fill its option's fields located at the given offsets and return it.
2088 : */
2089 : void *
2090 1609 : build_local_reloptions(local_relopts *relopts, Datum options, bool validate)
2091 : {
2092 1609 : int noptions = list_length(relopts->options);
2093 1609 : relopt_parse_elt *elems = palloc_array(relopt_parse_elt, noptions);
2094 : relopt_value *vals;
2095 : void *opts;
2096 1609 : int i = 0;
2097 : ListCell *lc;
2098 :
2099 3691 : foreach(lc, relopts->options)
2100 : {
2101 2082 : local_relopt *opt = lfirst(lc);
2102 :
2103 2082 : elems[i].optname = opt->option->name;
2104 2082 : elems[i].opttype = opt->option->type;
2105 2082 : elems[i].offset = opt->offset;
2106 :
2107 2082 : i++;
2108 : }
2109 :
2110 1609 : vals = parseLocalRelOptions(relopts, options, validate);
2111 1556 : opts = allocateReloptStruct(relopts->relopt_struct_size, vals, noptions);
2112 1556 : fillRelOptions(opts, relopts->relopt_struct_size, vals, noptions, validate,
2113 : elems, noptions);
2114 :
2115 1556 : if (validate)
2116 412 : foreach(lc, relopts->validators)
2117 3 : ((relopts_validator) lfirst(lc)) (opts, vals, noptions);
2118 :
2119 1555 : if (elems)
2120 1555 : pfree(elems);
2121 :
2122 1555 : return opts;
2123 : }
2124 :
2125 : /*
2126 : * Option parser for partitioned tables
2127 : */
2128 : bytea *
2129 3559 : partitioned_table_reloptions(Datum reloptions, bool validate)
2130 : {
2131 3559 : if (validate && reloptions)
2132 8 : ereport(ERROR,
2133 : errcode(ERRCODE_WRONG_OBJECT_TYPE),
2134 : errmsg("cannot specify storage parameters for a partitioned table"),
2135 : errhint("Specify storage parameters for its leaf partitions instead."));
2136 3551 : return NULL;
2137 : }
2138 :
2139 : /*
2140 : * Option parser for views
2141 : */
2142 : bytea *
2143 12409 : view_reloptions(Datum reloptions, bool validate)
2144 : {
2145 : static const relopt_parse_elt tab[] = {
2146 : {"security_barrier", RELOPT_TYPE_BOOL,
2147 : offsetof(ViewOptions, security_barrier)},
2148 : {"security_invoker", RELOPT_TYPE_BOOL,
2149 : offsetof(ViewOptions, security_invoker)},
2150 : {"check_option", RELOPT_TYPE_ENUM,
2151 : offsetof(ViewOptions, check_option)}
2152 : };
2153 :
2154 12409 : return (bytea *) build_reloptions(reloptions, validate,
2155 : RELOPT_KIND_VIEW,
2156 : sizeof(ViewOptions),
2157 : tab, lengthof(tab));
2158 : }
2159 :
2160 : /*
2161 : * Parse options for heaps, views and toast tables.
2162 : */
2163 : bytea *
2164 65698 : heap_reloptions(char relkind, Datum reloptions, bool validate)
2165 : {
2166 : StdRdOptions *rdopts;
2167 :
2168 65698 : switch (relkind)
2169 : {
2170 26320 : case RELKIND_TOASTVALUE:
2171 : rdopts = (StdRdOptions *)
2172 26320 : default_reloptions(reloptions, validate, RELOPT_KIND_TOAST);
2173 26316 : if (rdopts != NULL)
2174 : {
2175 : /* adjust default-only parameters for TOAST relations */
2176 26316 : rdopts->fillfactor = 100;
2177 26316 : rdopts->autovacuum.analyze_threshold = -1;
2178 26316 : rdopts->autovacuum.analyze_scale_factor = -1;
2179 : }
2180 26316 : return (bytea *) rdopts;
2181 35360 : case RELKIND_RELATION:
2182 : case RELKIND_MATVIEW:
2183 35360 : return default_reloptions(reloptions, validate, RELOPT_KIND_HEAP);
2184 4018 : default:
2185 : /* other relkinds are not supported */
2186 4018 : return NULL;
2187 : }
2188 : }
2189 :
2190 :
2191 : /*
2192 : * Parse options for indexes.
2193 : *
2194 : * amoptions index AM's option parser function
2195 : * reloptions options as text[] datum
2196 : * validate error flag
2197 : */
2198 : bytea *
2199 21351 : index_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
2200 : {
2201 : Assert(amoptions != NULL);
2202 :
2203 : /* Assume function is strict */
2204 21351 : if (DatumGetPointer(reloptions) == NULL)
2205 19598 : return NULL;
2206 :
2207 1753 : return amoptions(reloptions, validate);
2208 : }
2209 :
2210 : /*
2211 : * Option parser for attribute reloptions
2212 : */
2213 : bytea *
2214 25 : attribute_reloptions(Datum reloptions, bool validate)
2215 : {
2216 : static const relopt_parse_elt tab[] = {
2217 : {"n_distinct", RELOPT_TYPE_REAL, offsetof(AttributeOpts, n_distinct)},
2218 : {"n_distinct_inherited", RELOPT_TYPE_REAL, offsetof(AttributeOpts, n_distinct_inherited)}
2219 : };
2220 :
2221 25 : return (bytea *) build_reloptions(reloptions, validate,
2222 : RELOPT_KIND_ATTRIBUTE,
2223 : sizeof(AttributeOpts),
2224 : tab, lengthof(tab));
2225 : }
2226 :
2227 : /*
2228 : * Option parser for tablespace reloptions
2229 : */
2230 : bytea *
2231 96 : tablespace_reloptions(Datum reloptions, bool validate)
2232 : {
2233 : static const relopt_parse_elt tab[] = {
2234 : {"random_page_cost", RELOPT_TYPE_REAL, offsetof(TableSpaceOpts, random_page_cost)},
2235 : {"seq_page_cost", RELOPT_TYPE_REAL, offsetof(TableSpaceOpts, seq_page_cost)},
2236 : {"effective_io_concurrency", RELOPT_TYPE_INT, offsetof(TableSpaceOpts, effective_io_concurrency)},
2237 : {"maintenance_io_concurrency", RELOPT_TYPE_INT, offsetof(TableSpaceOpts, maintenance_io_concurrency)}
2238 : };
2239 :
2240 96 : return (bytea *) build_reloptions(reloptions, validate,
2241 : RELOPT_KIND_TABLESPACE,
2242 : sizeof(TableSpaceOpts),
2243 : tab, lengthof(tab));
2244 : }
2245 :
2246 : /*
2247 : * Determine the required LOCKMODE from an option list.
2248 : *
2249 : * Called from AlterTableGetLockLevel(), see that function
2250 : * for a longer explanation of how this works.
2251 : */
2252 : LOCKMODE
2253 496 : AlterTableGetRelOptionsLockLevel(List *defList)
2254 : {
2255 496 : LOCKMODE lockmode = NoLock;
2256 : ListCell *cell;
2257 :
2258 496 : if (defList == NIL)
2259 0 : return AccessExclusiveLock;
2260 :
2261 496 : if (need_initialization)
2262 9 : initialize_reloptions();
2263 :
2264 1017 : foreach(cell, defList)
2265 : {
2266 521 : DefElem *def = (DefElem *) lfirst(cell);
2267 : int i;
2268 :
2269 24814 : for (i = 0; relOpts[i]; i++)
2270 : {
2271 24293 : if (strncmp(relOpts[i]->name,
2272 24293 : def->defname,
2273 24293 : relOpts[i]->namelen + 1) == 0)
2274 : {
2275 741 : if (lockmode < relOpts[i]->lockmode)
2276 492 : lockmode = relOpts[i]->lockmode;
2277 : }
2278 : }
2279 : }
2280 :
2281 496 : return lockmode;
2282 : }
|