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