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 autovacuum actions will be logged",
326 : RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
327 : ShareUpdateExclusiveLock
328 : },
329 : -1, -1, INT_MAX
330 : },
331 : {
332 : {
333 : "toast_tuple_target",
334 : "Sets the target tuple length at which external columns will be toasted",
335 : RELOPT_KIND_HEAP,
336 : ShareUpdateExclusiveLock
337 : },
338 : TOAST_TUPLE_TARGET, 128, TOAST_TUPLE_TARGET_MAIN
339 : },
340 : {
341 : {
342 : "pages_per_range",
343 : "Number of pages that each page range covers in a BRIN index",
344 : RELOPT_KIND_BRIN,
345 : AccessExclusiveLock
346 : }, 128, 1, 131072
347 : },
348 : {
349 : {
350 : "gin_pending_list_limit",
351 : "Maximum size of the pending list for this GIN index, in kilobytes.",
352 : RELOPT_KIND_GIN,
353 : AccessExclusiveLock
354 : },
355 : -1, 64, MAX_KILOBYTES
356 : },
357 : {
358 : {
359 : "effective_io_concurrency",
360 : "Number of simultaneous requests that can be handled efficiently by the disk subsystem.",
361 : RELOPT_KIND_TABLESPACE,
362 : ShareUpdateExclusiveLock
363 : },
364 : #ifdef USE_PREFETCH
365 : -1, 0, MAX_IO_CONCURRENCY
366 : #else
367 : 0, 0, 0
368 : #endif
369 : },
370 : {
371 : {
372 : "maintenance_io_concurrency",
373 : "Number of simultaneous requests that can be handled efficiently by the disk subsystem for maintenance work.",
374 : RELOPT_KIND_TABLESPACE,
375 : ShareUpdateExclusiveLock
376 : },
377 : #ifdef USE_PREFETCH
378 : -1, 0, MAX_IO_CONCURRENCY
379 : #else
380 : 0, 0, 0
381 : #endif
382 : },
383 : {
384 : {
385 : "parallel_workers",
386 : "Number of parallel processes that can be used per executor node for this relation.",
387 : RELOPT_KIND_HEAP,
388 : ShareUpdateExclusiveLock
389 : },
390 : -1, 0, 1024
391 : },
392 :
393 : /* list terminator */
394 : {{NULL}}
395 : };
396 :
397 : static relopt_real realRelOpts[] =
398 : {
399 : {
400 : {
401 : "autovacuum_vacuum_cost_delay",
402 : "Vacuum cost delay in milliseconds, for autovacuum",
403 : RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
404 : ShareUpdateExclusiveLock
405 : },
406 : -1, 0.0, 100.0
407 : },
408 : {
409 : {
410 : "autovacuum_vacuum_scale_factor",
411 : "Number of tuple updates or deletes prior to vacuum as a fraction of reltuples",
412 : RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
413 : ShareUpdateExclusiveLock
414 : },
415 : -1, 0.0, 100.0
416 : },
417 : {
418 : {
419 : "autovacuum_vacuum_insert_scale_factor",
420 : "Number of tuple inserts prior to vacuum as a fraction of reltuples",
421 : RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
422 : ShareUpdateExclusiveLock
423 : },
424 : -1, 0.0, 100.0
425 : },
426 : {
427 : {
428 : "autovacuum_analyze_scale_factor",
429 : "Number of tuple inserts, updates or deletes prior to analyze as a fraction of reltuples",
430 : RELOPT_KIND_HEAP,
431 : ShareUpdateExclusiveLock
432 : },
433 : -1, 0.0, 100.0
434 : },
435 : {
436 : {
437 : "vacuum_max_eager_freeze_failure_rate",
438 : "Fraction of pages in a relation vacuum can scan and fail to freeze before disabling eager scanning.",
439 : RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
440 : ShareUpdateExclusiveLock
441 : },
442 : -1, 0.0, 1.0
443 : },
444 :
445 : {
446 : {
447 : "seq_page_cost",
448 : "Sets the planner's estimate of the cost of a sequentially fetched disk page.",
449 : RELOPT_KIND_TABLESPACE,
450 : ShareUpdateExclusiveLock
451 : },
452 : -1, 0.0, DBL_MAX
453 : },
454 : {
455 : {
456 : "random_page_cost",
457 : "Sets the planner's estimate of the cost of a nonsequentially fetched disk page.",
458 : RELOPT_KIND_TABLESPACE,
459 : ShareUpdateExclusiveLock
460 : },
461 : -1, 0.0, DBL_MAX
462 : },
463 : {
464 : {
465 : "n_distinct",
466 : "Sets the planner's estimate of the number of distinct values appearing in a column (excluding child relations).",
467 : RELOPT_KIND_ATTRIBUTE,
468 : ShareUpdateExclusiveLock
469 : },
470 : 0, -1.0, DBL_MAX
471 : },
472 : {
473 : {
474 : "n_distinct_inherited",
475 : "Sets the planner's estimate of the number of distinct values appearing in a column (including child relations).",
476 : RELOPT_KIND_ATTRIBUTE,
477 : ShareUpdateExclusiveLock
478 : },
479 : 0, -1.0, DBL_MAX
480 : },
481 : {
482 : {
483 : "vacuum_cleanup_index_scale_factor",
484 : "Deprecated B-Tree parameter.",
485 : RELOPT_KIND_BTREE,
486 : ShareUpdateExclusiveLock
487 : },
488 : -1, 0.0, 1e10
489 : },
490 : /* list terminator */
491 : {{NULL}}
492 : };
493 :
494 : /* values from StdRdOptIndexCleanup */
495 : static relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
496 : {
497 : {"auto", STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO},
498 : {"on", STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON},
499 : {"off", STDRD_OPTION_VACUUM_INDEX_CLEANUP_OFF},
500 : {"true", STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON},
501 : {"false", STDRD_OPTION_VACUUM_INDEX_CLEANUP_OFF},
502 : {"yes", STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON},
503 : {"no", STDRD_OPTION_VACUUM_INDEX_CLEANUP_OFF},
504 : {"1", STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON},
505 : {"0", STDRD_OPTION_VACUUM_INDEX_CLEANUP_OFF},
506 : {(const char *) NULL} /* list terminator */
507 : };
508 :
509 : /* values from GistOptBufferingMode */
510 : static relopt_enum_elt_def gistBufferingOptValues[] =
511 : {
512 : {"auto", GIST_OPTION_BUFFERING_AUTO},
513 : {"on", GIST_OPTION_BUFFERING_ON},
514 : {"off", GIST_OPTION_BUFFERING_OFF},
515 : {(const char *) NULL} /* list terminator */
516 : };
517 :
518 : /* values from ViewOptCheckOption */
519 : static relopt_enum_elt_def viewCheckOptValues[] =
520 : {
521 : /* no value for NOT_SET */
522 : {"local", VIEW_OPTION_CHECK_OPTION_LOCAL},
523 : {"cascaded", VIEW_OPTION_CHECK_OPTION_CASCADED},
524 : {(const char *) NULL} /* list terminator */
525 : };
526 :
527 : static relopt_enum enumRelOpts[] =
528 : {
529 : {
530 : {
531 : "vacuum_index_cleanup",
532 : "Controls index vacuuming and index cleanup",
533 : RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
534 : ShareUpdateExclusiveLock
535 : },
536 : StdRdOptIndexCleanupValues,
537 : STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO,
538 : gettext_noop("Valid values are \"on\", \"off\", and \"auto\".")
539 : },
540 : {
541 : {
542 : "buffering",
543 : "Enables buffering build for this GiST index",
544 : RELOPT_KIND_GIST,
545 : AccessExclusiveLock
546 : },
547 : gistBufferingOptValues,
548 : GIST_OPTION_BUFFERING_AUTO,
549 : gettext_noop("Valid values are \"on\", \"off\", and \"auto\".")
550 : },
551 : {
552 : {
553 : "check_option",
554 : "View has WITH CHECK OPTION defined (local or cascaded).",
555 : RELOPT_KIND_VIEW,
556 : AccessExclusiveLock
557 : },
558 : viewCheckOptValues,
559 : VIEW_OPTION_CHECK_OPTION_NOT_SET,
560 : gettext_noop("Valid values are \"local\" and \"cascaded\".")
561 : },
562 : /* list terminator */
563 : {{NULL}}
564 : };
565 :
566 : static relopt_string stringRelOpts[] =
567 : {
568 : /* list terminator */
569 : {{NULL}}
570 : };
571 :
572 : static relopt_gen **relOpts = NULL;
573 : static bits32 last_assigned_kind = RELOPT_KIND_LAST_DEFAULT;
574 :
575 : static int num_custom_options = 0;
576 : static relopt_gen **custom_options = NULL;
577 : static bool need_initialization = true;
578 :
579 : static void initialize_reloptions(void);
580 : static void parse_one_reloption(relopt_value *option, char *text_str,
581 : int text_len, bool validate);
582 :
583 : /*
584 : * Get the length of a string reloption (either default or the user-defined
585 : * value). This is used for allocation purposes when building a set of
586 : * relation options.
587 : */
588 : #define GET_STRING_RELOPTION_LEN(option) \
589 : ((option).isset ? strlen((option).values.string_val) : \
590 : ((relopt_string *) (option).gen)->default_len)
591 :
592 : /*
593 : * initialize_reloptions
594 : * initialization routine, must be called before parsing
595 : *
596 : * Initialize the relOpts array and fill each variable's type and name length.
597 : */
598 : static void
599 7728 : initialize_reloptions(void)
600 : {
601 : int i;
602 : int j;
603 :
604 7728 : j = 0;
605 69552 : for (i = 0; boolRelOpts[i].gen.name; i++)
606 : {
607 : Assert(DoLockModesConflict(boolRelOpts[i].gen.lockmode,
608 : boolRelOpts[i].gen.lockmode));
609 61824 : j++;
610 : }
611 185472 : for (i = 0; intRelOpts[i].gen.name; i++)
612 : {
613 : Assert(DoLockModesConflict(intRelOpts[i].gen.lockmode,
614 : intRelOpts[i].gen.lockmode));
615 177744 : j++;
616 : }
617 85008 : for (i = 0; realRelOpts[i].gen.name; i++)
618 : {
619 : Assert(DoLockModesConflict(realRelOpts[i].gen.lockmode,
620 : realRelOpts[i].gen.lockmode));
621 77280 : j++;
622 : }
623 30912 : for (i = 0; enumRelOpts[i].gen.name; i++)
624 : {
625 : Assert(DoLockModesConflict(enumRelOpts[i].gen.lockmode,
626 : enumRelOpts[i].gen.lockmode));
627 23184 : j++;
628 : }
629 7728 : for (i = 0; stringRelOpts[i].gen.name; i++)
630 : {
631 : Assert(DoLockModesConflict(stringRelOpts[i].gen.lockmode,
632 : stringRelOpts[i].gen.lockmode));
633 0 : j++;
634 : }
635 7728 : j += num_custom_options;
636 :
637 7728 : if (relOpts)
638 0 : pfree(relOpts);
639 15456 : relOpts = MemoryContextAlloc(TopMemoryContext,
640 7728 : (j + 1) * sizeof(relopt_gen *));
641 :
642 7728 : j = 0;
643 69552 : for (i = 0; boolRelOpts[i].gen.name; i++)
644 : {
645 61824 : relOpts[j] = &boolRelOpts[i].gen;
646 61824 : relOpts[j]->type = RELOPT_TYPE_BOOL;
647 61824 : relOpts[j]->namelen = strlen(relOpts[j]->name);
648 61824 : j++;
649 : }
650 :
651 185472 : for (i = 0; intRelOpts[i].gen.name; i++)
652 : {
653 177744 : relOpts[j] = &intRelOpts[i].gen;
654 177744 : relOpts[j]->type = RELOPT_TYPE_INT;
655 177744 : relOpts[j]->namelen = strlen(relOpts[j]->name);
656 177744 : j++;
657 : }
658 :
659 85008 : for (i = 0; realRelOpts[i].gen.name; i++)
660 : {
661 77280 : relOpts[j] = &realRelOpts[i].gen;
662 77280 : relOpts[j]->type = RELOPT_TYPE_REAL;
663 77280 : relOpts[j]->namelen = strlen(relOpts[j]->name);
664 77280 : j++;
665 : }
666 :
667 30912 : for (i = 0; enumRelOpts[i].gen.name; i++)
668 : {
669 23184 : relOpts[j] = &enumRelOpts[i].gen;
670 23184 : relOpts[j]->type = RELOPT_TYPE_ENUM;
671 23184 : relOpts[j]->namelen = strlen(relOpts[j]->name);
672 23184 : j++;
673 : }
674 :
675 7728 : for (i = 0; stringRelOpts[i].gen.name; i++)
676 : {
677 0 : relOpts[j] = &stringRelOpts[i].gen;
678 0 : relOpts[j]->type = RELOPT_TYPE_STRING;
679 0 : relOpts[j]->namelen = strlen(relOpts[j]->name);
680 0 : j++;
681 : }
682 :
683 13944 : for (i = 0; i < num_custom_options; i++)
684 : {
685 6216 : relOpts[j] = custom_options[i];
686 6216 : j++;
687 : }
688 :
689 : /* add a list terminator */
690 7728 : relOpts[j] = NULL;
691 :
692 : /* flag the work is complete */
693 7728 : need_initialization = false;
694 7728 : }
695 :
696 : /*
697 : * add_reloption_kind
698 : * Create a new relopt_kind value, to be used in custom reloptions by
699 : * user-defined AMs.
700 : */
701 : relopt_kind
702 192 : add_reloption_kind(void)
703 : {
704 : /* don't hand out the last bit so that the enum's behavior is portable */
705 192 : if (last_assigned_kind >= RELOPT_KIND_MAX)
706 0 : ereport(ERROR,
707 : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
708 : errmsg("user-defined relation parameter types limit exceeded")));
709 192 : last_assigned_kind <<= 1;
710 192 : return (relopt_kind) last_assigned_kind;
711 : }
712 :
713 : /*
714 : * add_reloption
715 : * Add an already-created custom reloption to the list, and recompute the
716 : * main parser table.
717 : */
718 : static void
719 6282 : add_reloption(relopt_gen *newoption)
720 : {
721 : static int max_custom_options = 0;
722 :
723 6282 : if (num_custom_options >= max_custom_options)
724 : {
725 : MemoryContext oldcxt;
726 :
727 762 : oldcxt = MemoryContextSwitchTo(TopMemoryContext);
728 :
729 762 : if (max_custom_options == 0)
730 : {
731 192 : max_custom_options = 8;
732 192 : custom_options = palloc(max_custom_options * sizeof(relopt_gen *));
733 : }
734 : else
735 : {
736 570 : max_custom_options *= 2;
737 570 : custom_options = repalloc(custom_options,
738 : max_custom_options * sizeof(relopt_gen *));
739 : }
740 762 : MemoryContextSwitchTo(oldcxt);
741 : }
742 6282 : custom_options[num_custom_options++] = newoption;
743 :
744 6282 : need_initialization = true;
745 6282 : }
746 :
747 : /*
748 : * init_local_reloptions
749 : * Initialize local reloptions that will parsed into bytea structure of
750 : * 'relopt_struct_size'.
751 : */
752 : void
753 4824 : init_local_reloptions(local_relopts *relopts, Size relopt_struct_size)
754 : {
755 4824 : relopts->options = NIL;
756 4824 : relopts->validators = NIL;
757 4824 : relopts->relopt_struct_size = relopt_struct_size;
758 4824 : }
759 :
760 : /*
761 : * register_reloptions_validator
762 : * Register custom validation callback that will be called at the end of
763 : * build_local_reloptions().
764 : */
765 : void
766 28 : register_reloptions_validator(local_relopts *relopts, relopts_validator validator)
767 : {
768 28 : relopts->validators = lappend(relopts->validators, validator);
769 28 : }
770 :
771 : /*
772 : * add_local_reloption
773 : * Add an already-created custom reloption to the local list.
774 : */
775 : static void
776 3116 : add_local_reloption(local_relopts *relopts, relopt_gen *newoption, int offset)
777 : {
778 3116 : local_relopt *opt = palloc(sizeof(*opt));
779 :
780 : Assert(offset < relopts->relopt_struct_size);
781 :
782 3116 : opt->option = newoption;
783 3116 : opt->offset = offset;
784 :
785 3116 : relopts->options = lappend(relopts->options, opt);
786 3116 : }
787 :
788 : /*
789 : * allocate_reloption
790 : * Allocate a new reloption and initialize the type-agnostic fields
791 : * (for types other than string)
792 : */
793 : static relopt_gen *
794 9398 : allocate_reloption(bits32 kinds, int type, const char *name, const char *desc,
795 : LOCKMODE lockmode)
796 : {
797 : MemoryContext oldcxt;
798 : size_t size;
799 : relopt_gen *newoption;
800 :
801 9398 : if (kinds != RELOPT_KIND_LOCAL)
802 6282 : oldcxt = MemoryContextSwitchTo(TopMemoryContext);
803 : else
804 3116 : oldcxt = NULL;
805 :
806 9398 : switch (type)
807 : {
808 2 : case RELOPT_TYPE_BOOL:
809 2 : size = sizeof(relopt_bool);
810 2 : break;
811 7980 : case RELOPT_TYPE_INT:
812 7980 : size = sizeof(relopt_int);
813 7980 : break;
814 1410 : case RELOPT_TYPE_REAL:
815 1410 : size = sizeof(relopt_real);
816 1410 : break;
817 2 : case RELOPT_TYPE_ENUM:
818 2 : size = sizeof(relopt_enum);
819 2 : break;
820 4 : case RELOPT_TYPE_STRING:
821 4 : size = sizeof(relopt_string);
822 4 : break;
823 0 : default:
824 0 : elog(ERROR, "unsupported reloption type %d", type);
825 : return NULL; /* keep compiler quiet */
826 : }
827 :
828 9398 : newoption = palloc(size);
829 :
830 9398 : newoption->name = pstrdup(name);
831 9398 : if (desc)
832 9396 : newoption->desc = pstrdup(desc);
833 : else
834 2 : newoption->desc = NULL;
835 9398 : newoption->kinds = kinds;
836 9398 : newoption->namelen = strlen(name);
837 9398 : newoption->type = type;
838 9398 : newoption->lockmode = lockmode;
839 :
840 9398 : if (oldcxt != NULL)
841 6282 : MemoryContextSwitchTo(oldcxt);
842 :
843 9398 : return newoption;
844 : }
845 :
846 : /*
847 : * init_bool_reloption
848 : * Allocate and initialize a new boolean reloption
849 : */
850 : static relopt_bool *
851 2 : init_bool_reloption(bits32 kinds, const char *name, const char *desc,
852 : bool default_val, LOCKMODE lockmode)
853 : {
854 : relopt_bool *newoption;
855 :
856 2 : newoption = (relopt_bool *) allocate_reloption(kinds, RELOPT_TYPE_BOOL,
857 : name, desc, lockmode);
858 2 : newoption->default_val = default_val;
859 :
860 2 : return newoption;
861 : }
862 :
863 : /*
864 : * add_bool_reloption
865 : * Add a new boolean reloption
866 : */
867 : void
868 2 : add_bool_reloption(bits32 kinds, const char *name, const char *desc,
869 : bool default_val, LOCKMODE lockmode)
870 : {
871 2 : relopt_bool *newoption = init_bool_reloption(kinds, name, desc,
872 : default_val, lockmode);
873 :
874 2 : add_reloption((relopt_gen *) newoption);
875 2 : }
876 :
877 : /*
878 : * add_local_bool_reloption
879 : * Add a new boolean local reloption
880 : *
881 : * 'offset' is offset of bool-typed field.
882 : */
883 : void
884 0 : add_local_bool_reloption(local_relopts *relopts, const char *name,
885 : const char *desc, bool default_val, int offset)
886 : {
887 0 : relopt_bool *newoption = init_bool_reloption(RELOPT_KIND_LOCAL,
888 : name, desc,
889 : default_val, 0);
890 :
891 0 : add_local_reloption(relopts, (relopt_gen *) newoption, offset);
892 0 : }
893 :
894 :
895 : /*
896 : * init_real_reloption
897 : * Allocate and initialize a new integer reloption
898 : */
899 : static relopt_int *
900 7980 : init_int_reloption(bits32 kinds, const char *name, const char *desc,
901 : int default_val, int min_val, int max_val,
902 : LOCKMODE lockmode)
903 : {
904 : relopt_int *newoption;
905 :
906 7980 : newoption = (relopt_int *) allocate_reloption(kinds, RELOPT_TYPE_INT,
907 : name, desc, lockmode);
908 7980 : newoption->default_val = default_val;
909 7980 : newoption->min = min_val;
910 7980 : newoption->max = max_val;
911 :
912 7980 : return newoption;
913 : }
914 :
915 : /*
916 : * add_int_reloption
917 : * Add a new integer reloption
918 : */
919 : void
920 6272 : add_int_reloption(bits32 kinds, const char *name, const char *desc, int default_val,
921 : int min_val, int max_val, LOCKMODE lockmode)
922 : {
923 6272 : relopt_int *newoption = init_int_reloption(kinds, name, desc,
924 : default_val, min_val,
925 : max_val, lockmode);
926 :
927 6272 : add_reloption((relopt_gen *) newoption);
928 6272 : }
929 :
930 : /*
931 : * add_local_int_reloption
932 : * Add a new local integer reloption
933 : *
934 : * 'offset' is offset of int-typed field.
935 : */
936 : void
937 1708 : add_local_int_reloption(local_relopts *relopts, const char *name,
938 : const char *desc, int default_val, int min_val,
939 : int max_val, int offset)
940 : {
941 1708 : relopt_int *newoption = init_int_reloption(RELOPT_KIND_LOCAL,
942 : name, desc, default_val,
943 : min_val, max_val, 0);
944 :
945 1708 : add_local_reloption(relopts, (relopt_gen *) newoption, offset);
946 1708 : }
947 :
948 : /*
949 : * init_real_reloption
950 : * Allocate and initialize a new real reloption
951 : */
952 : static relopt_real *
953 1410 : init_real_reloption(bits32 kinds, const char *name, const char *desc,
954 : double default_val, double min_val, double max_val,
955 : LOCKMODE lockmode)
956 : {
957 : relopt_real *newoption;
958 :
959 1410 : newoption = (relopt_real *) allocate_reloption(kinds, RELOPT_TYPE_REAL,
960 : name, desc, lockmode);
961 1410 : newoption->default_val = default_val;
962 1410 : newoption->min = min_val;
963 1410 : newoption->max = max_val;
964 :
965 1410 : return newoption;
966 : }
967 :
968 : /*
969 : * add_real_reloption
970 : * Add a new float reloption
971 : */
972 : void
973 2 : add_real_reloption(bits32 kinds, const char *name, const char *desc,
974 : double default_val, double min_val, double max_val,
975 : LOCKMODE lockmode)
976 : {
977 2 : relopt_real *newoption = init_real_reloption(kinds, name, desc,
978 : default_val, min_val,
979 : max_val, lockmode);
980 :
981 2 : add_reloption((relopt_gen *) newoption);
982 2 : }
983 :
984 : /*
985 : * add_local_real_reloption
986 : * Add a new local float reloption
987 : *
988 : * 'offset' is offset of double-typed field.
989 : */
990 : void
991 1408 : add_local_real_reloption(local_relopts *relopts, const char *name,
992 : const char *desc, double default_val,
993 : double min_val, double max_val, int offset)
994 : {
995 1408 : relopt_real *newoption = init_real_reloption(RELOPT_KIND_LOCAL,
996 : name, desc,
997 : default_val, min_val,
998 : max_val, 0);
999 :
1000 1408 : add_local_reloption(relopts, (relopt_gen *) newoption, offset);
1001 1408 : }
1002 :
1003 : /*
1004 : * init_enum_reloption
1005 : * Allocate and initialize a new enum reloption
1006 : */
1007 : static relopt_enum *
1008 2 : init_enum_reloption(bits32 kinds, const char *name, const char *desc,
1009 : relopt_enum_elt_def *members, int default_val,
1010 : const char *detailmsg, LOCKMODE lockmode)
1011 : {
1012 : relopt_enum *newoption;
1013 :
1014 2 : newoption = (relopt_enum *) allocate_reloption(kinds, RELOPT_TYPE_ENUM,
1015 : name, desc, lockmode);
1016 2 : newoption->members = members;
1017 2 : newoption->default_val = default_val;
1018 2 : newoption->detailmsg = detailmsg;
1019 :
1020 2 : return newoption;
1021 : }
1022 :
1023 :
1024 : /*
1025 : * add_enum_reloption
1026 : * Add a new enum reloption
1027 : *
1028 : * The members array must have a terminating NULL entry.
1029 : *
1030 : * The detailmsg is shown when unsupported values are passed, and has this
1031 : * form: "Valid values are \"foo\", \"bar\", and \"bar\"."
1032 : *
1033 : * The members array and detailmsg are not copied -- caller must ensure that
1034 : * they are valid throughout the life of the process.
1035 : */
1036 : void
1037 2 : add_enum_reloption(bits32 kinds, const char *name, const char *desc,
1038 : relopt_enum_elt_def *members, int default_val,
1039 : const char *detailmsg, LOCKMODE lockmode)
1040 : {
1041 2 : relopt_enum *newoption = init_enum_reloption(kinds, name, desc,
1042 : members, default_val,
1043 : detailmsg, lockmode);
1044 :
1045 2 : add_reloption((relopt_gen *) newoption);
1046 2 : }
1047 :
1048 : /*
1049 : * add_local_enum_reloption
1050 : * Add a new local enum reloption
1051 : *
1052 : * 'offset' is offset of int-typed field.
1053 : */
1054 : void
1055 0 : add_local_enum_reloption(local_relopts *relopts, const char *name,
1056 : const char *desc, relopt_enum_elt_def *members,
1057 : int default_val, const char *detailmsg, int offset)
1058 : {
1059 0 : relopt_enum *newoption = init_enum_reloption(RELOPT_KIND_LOCAL,
1060 : name, desc,
1061 : members, default_val,
1062 : detailmsg, 0);
1063 :
1064 0 : add_local_reloption(relopts, (relopt_gen *) newoption, offset);
1065 0 : }
1066 :
1067 : /*
1068 : * init_string_reloption
1069 : * Allocate and initialize a new string reloption
1070 : */
1071 : static relopt_string *
1072 4 : init_string_reloption(bits32 kinds, const char *name, const char *desc,
1073 : const char *default_val,
1074 : validate_string_relopt validator,
1075 : fill_string_relopt filler,
1076 : LOCKMODE lockmode)
1077 : {
1078 : relopt_string *newoption;
1079 :
1080 : /* make sure the validator/default combination is sane */
1081 4 : if (validator)
1082 4 : (validator) (default_val);
1083 :
1084 4 : newoption = (relopt_string *) allocate_reloption(kinds, RELOPT_TYPE_STRING,
1085 : name, desc, lockmode);
1086 4 : newoption->validate_cb = validator;
1087 4 : newoption->fill_cb = filler;
1088 4 : if (default_val)
1089 : {
1090 2 : if (kinds == RELOPT_KIND_LOCAL)
1091 0 : newoption->default_val = strdup(default_val);
1092 : else
1093 2 : newoption->default_val = MemoryContextStrdup(TopMemoryContext, default_val);
1094 2 : newoption->default_len = strlen(default_val);
1095 2 : newoption->default_isnull = false;
1096 : }
1097 : else
1098 : {
1099 2 : newoption->default_val = "";
1100 2 : newoption->default_len = 0;
1101 2 : newoption->default_isnull = true;
1102 : }
1103 :
1104 4 : return newoption;
1105 : }
1106 :
1107 : /*
1108 : * add_string_reloption
1109 : * Add a new string reloption
1110 : *
1111 : * "validator" is an optional function pointer that can be used to test the
1112 : * validity of the values. It must elog(ERROR) when the argument string is
1113 : * not acceptable for the variable. Note that the default value must pass
1114 : * the validation.
1115 : */
1116 : void
1117 4 : add_string_reloption(bits32 kinds, const char *name, const char *desc,
1118 : const char *default_val, validate_string_relopt validator,
1119 : LOCKMODE lockmode)
1120 : {
1121 4 : relopt_string *newoption = init_string_reloption(kinds, name, desc,
1122 : default_val,
1123 : validator, NULL,
1124 : lockmode);
1125 :
1126 4 : add_reloption((relopt_gen *) newoption);
1127 4 : }
1128 :
1129 : /*
1130 : * add_local_string_reloption
1131 : * Add a new local string reloption
1132 : *
1133 : * 'offset' is offset of int-typed field that will store offset of string value
1134 : * in the resulting bytea structure.
1135 : */
1136 : void
1137 0 : add_local_string_reloption(local_relopts *relopts, const char *name,
1138 : const char *desc, const char *default_val,
1139 : validate_string_relopt validator,
1140 : fill_string_relopt filler, int offset)
1141 : {
1142 0 : relopt_string *newoption = init_string_reloption(RELOPT_KIND_LOCAL,
1143 : name, desc,
1144 : default_val,
1145 : validator, filler,
1146 : 0);
1147 :
1148 0 : add_local_reloption(relopts, (relopt_gen *) newoption, offset);
1149 0 : }
1150 :
1151 : /*
1152 : * Transform a relation options list (list of DefElem) into the text array
1153 : * format that is kept in pg_class.reloptions, including only those options
1154 : * that are in the passed namespace. The output values do not include the
1155 : * namespace.
1156 : *
1157 : * This is used for three cases: CREATE TABLE/INDEX, ALTER TABLE SET, and
1158 : * ALTER TABLE RESET. In the ALTER cases, oldOptions is the existing
1159 : * reloptions value (possibly NULL), and we replace or remove entries
1160 : * as needed.
1161 : *
1162 : * If acceptOidsOff is true, then we allow oids = false, but throw error when
1163 : * on. This is solely needed for backwards compatibility.
1164 : *
1165 : * Note that this is not responsible for determining whether the options
1166 : * are valid, but it does check that namespaces for all the options given are
1167 : * listed in validnsps. The NULL namespace is always valid and need not be
1168 : * explicitly listed. Passing a NULL pointer means that only the NULL
1169 : * namespace is valid.
1170 : *
1171 : * Both oldOptions and the result are text arrays (or NULL for "default"),
1172 : * but we declare them as Datums to avoid including array.h in reloptions.h.
1173 : */
1174 : Datum
1175 126118 : transformRelOptions(Datum oldOptions, List *defList, const char *namspace,
1176 : const char *const validnsps[], bool acceptOidsOff, bool isReset)
1177 : {
1178 : Datum result;
1179 : ArrayBuildState *astate;
1180 : ListCell *cell;
1181 :
1182 : /* no change if empty list */
1183 126118 : if (defList == NIL)
1184 122636 : return oldOptions;
1185 :
1186 : /* We build new array using accumArrayResult */
1187 3482 : astate = NULL;
1188 :
1189 : /* Copy any oldOptions that aren't to be replaced */
1190 3482 : if (PointerIsValid(DatumGetPointer(oldOptions)))
1191 : {
1192 342 : ArrayType *array = DatumGetArrayTypeP(oldOptions);
1193 : Datum *oldoptions;
1194 : int noldoptions;
1195 : int i;
1196 :
1197 342 : deconstruct_array_builtin(array, TEXTOID, &oldoptions, NULL, &noldoptions);
1198 :
1199 874 : for (i = 0; i < noldoptions; i++)
1200 : {
1201 532 : char *text_str = VARDATA(oldoptions[i]);
1202 532 : int text_len = VARSIZE(oldoptions[i]) - VARHDRSZ;
1203 :
1204 : /* Search for a match in defList */
1205 790 : foreach(cell, defList)
1206 : {
1207 580 : DefElem *def = (DefElem *) lfirst(cell);
1208 : int kw_len;
1209 :
1210 : /* ignore if not in the same namespace */
1211 580 : if (namspace == NULL)
1212 : {
1213 532 : if (def->defnamespace != NULL)
1214 0 : continue;
1215 : }
1216 48 : else if (def->defnamespace == NULL)
1217 30 : continue;
1218 18 : else if (strcmp(def->defnamespace, namspace) != 0)
1219 0 : continue;
1220 :
1221 550 : kw_len = strlen(def->defname);
1222 550 : if (text_len > kw_len && text_str[kw_len] == '=' &&
1223 344 : strncmp(text_str, def->defname, kw_len) == 0)
1224 322 : break;
1225 : }
1226 532 : if (!cell)
1227 : {
1228 : /* No match, so keep old option */
1229 210 : astate = accumArrayResult(astate, oldoptions[i],
1230 : false, TEXTOID,
1231 : CurrentMemoryContext);
1232 : }
1233 : }
1234 : }
1235 :
1236 : /*
1237 : * If CREATE/SET, add new options to array; if RESET, just check that the
1238 : * user didn't say RESET (option=val). (Must do this because the grammar
1239 : * doesn't enforce it.)
1240 : */
1241 7286 : foreach(cell, defList)
1242 : {
1243 3840 : DefElem *def = (DefElem *) lfirst(cell);
1244 :
1245 3840 : if (isReset)
1246 : {
1247 250 : if (def->arg != NULL)
1248 12 : ereport(ERROR,
1249 : (errcode(ERRCODE_SYNTAX_ERROR),
1250 : errmsg("RESET must not include values for parameters")));
1251 : }
1252 : else
1253 : {
1254 : text *t;
1255 : const char *value;
1256 : Size len;
1257 :
1258 : /*
1259 : * Error out if the namespace is not valid. A NULL namespace is
1260 : * always valid.
1261 : */
1262 3590 : if (def->defnamespace != NULL)
1263 : {
1264 112 : bool valid = false;
1265 : int i;
1266 :
1267 112 : if (validnsps)
1268 : {
1269 112 : for (i = 0; validnsps[i]; i++)
1270 : {
1271 106 : if (strcmp(def->defnamespace, validnsps[i]) == 0)
1272 : {
1273 100 : valid = true;
1274 100 : break;
1275 : }
1276 : }
1277 : }
1278 :
1279 112 : if (!valid)
1280 12 : ereport(ERROR,
1281 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1282 : errmsg("unrecognized parameter namespace \"%s\"",
1283 : def->defnamespace)));
1284 : }
1285 :
1286 : /* ignore if not in the same namespace */
1287 3578 : if (namspace == NULL)
1288 : {
1289 2708 : if (def->defnamespace != NULL)
1290 50 : continue;
1291 : }
1292 870 : else if (def->defnamespace == NULL)
1293 820 : continue;
1294 50 : else if (strcmp(def->defnamespace, namspace) != 0)
1295 0 : continue;
1296 :
1297 : /*
1298 : * Flatten the DefElem into a text string like "name=arg". If we
1299 : * have just "name", assume "name=true" is meant. Note: the
1300 : * namespace is not output.
1301 : */
1302 2708 : if (def->arg != NULL)
1303 2336 : value = defGetString(def);
1304 : else
1305 372 : value = "true";
1306 :
1307 : /*
1308 : * This is not a great place for this test, but there's no other
1309 : * convenient place to filter the option out. As WITH (oids =
1310 : * false) will be removed someday, this seems like an acceptable
1311 : * amount of ugly.
1312 : */
1313 2708 : if (acceptOidsOff && def->defnamespace == NULL &&
1314 1328 : strcmp(def->defname, "oids") == 0)
1315 : {
1316 18 : if (defGetBoolean(def))
1317 12 : ereport(ERROR,
1318 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1319 : errmsg("tables declared WITH OIDS are not supported")));
1320 : /* skip over option, reloptions machinery doesn't know it */
1321 6 : continue;
1322 : }
1323 :
1324 2690 : len = VARHDRSZ + strlen(def->defname) + 1 + strlen(value);
1325 : /* +1 leaves room for sprintf's trailing null */
1326 2690 : t = (text *) palloc(len + 1);
1327 2690 : SET_VARSIZE(t, len);
1328 2690 : sprintf(VARDATA(t), "%s=%s", def->defname, value);
1329 :
1330 2690 : astate = accumArrayResult(astate, PointerGetDatum(t),
1331 : false, TEXTOID,
1332 : CurrentMemoryContext);
1333 : }
1334 : }
1335 :
1336 3446 : if (astate)
1337 2542 : result = makeArrayResult(astate, CurrentMemoryContext);
1338 : else
1339 904 : result = (Datum) 0;
1340 :
1341 3446 : return result;
1342 : }
1343 :
1344 :
1345 : /*
1346 : * Convert the text-array format of reloptions into a List of DefElem.
1347 : * This is the inverse of transformRelOptions().
1348 : */
1349 : List *
1350 30292 : untransformRelOptions(Datum options)
1351 : {
1352 30292 : List *result = NIL;
1353 : ArrayType *array;
1354 : Datum *optiondatums;
1355 : int noptions;
1356 : int i;
1357 :
1358 : /* Nothing to do if no options */
1359 30292 : if (!PointerIsValid(DatumGetPointer(options)))
1360 4436 : return result;
1361 :
1362 25856 : array = DatumGetArrayTypeP(options);
1363 :
1364 25856 : deconstruct_array_builtin(array, TEXTOID, &optiondatums, NULL, &noptions);
1365 :
1366 77492 : for (i = 0; i < noptions; i++)
1367 : {
1368 : char *s;
1369 : char *p;
1370 51636 : Node *val = NULL;
1371 :
1372 51636 : s = TextDatumGetCString(optiondatums[i]);
1373 51636 : p = strchr(s, '=');
1374 51636 : if (p)
1375 : {
1376 51636 : *p++ = '\0';
1377 51636 : val = (Node *) makeString(p);
1378 : }
1379 51636 : result = lappend(result, makeDefElem(s, val, -1));
1380 : }
1381 :
1382 25856 : return result;
1383 : }
1384 :
1385 : /*
1386 : * Extract and parse reloptions from a pg_class tuple.
1387 : *
1388 : * This is a low-level routine, expected to be used by relcache code and
1389 : * callers that do not have a table's relcache entry (e.g. autovacuum). For
1390 : * other uses, consider grabbing the rd_options pointer from the relcache entry
1391 : * instead.
1392 : *
1393 : * tupdesc is pg_class' tuple descriptor. amoptions is a pointer to the index
1394 : * AM's options parser function in the case of a tuple corresponding to an
1395 : * index, or NULL otherwise.
1396 : */
1397 : bytea *
1398 1867186 : extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
1399 : amoptions_function amoptions)
1400 : {
1401 : bytea *options;
1402 : bool isnull;
1403 : Datum datum;
1404 : Form_pg_class classForm;
1405 :
1406 1867186 : datum = fastgetattr(tuple,
1407 : Anum_pg_class_reloptions,
1408 : tupdesc,
1409 : &isnull);
1410 1867186 : if (isnull)
1411 1847262 : return NULL;
1412 :
1413 19924 : classForm = (Form_pg_class) GETSTRUCT(tuple);
1414 :
1415 : /* Parse into appropriate format; don't error out here */
1416 19924 : switch (classForm->relkind)
1417 : {
1418 15276 : case RELKIND_RELATION:
1419 : case RELKIND_TOASTVALUE:
1420 : case RELKIND_MATVIEW:
1421 15276 : options = heap_reloptions(classForm->relkind, datum, false);
1422 15276 : break;
1423 0 : case RELKIND_PARTITIONED_TABLE:
1424 0 : options = partitioned_table_reloptions(datum, false);
1425 0 : break;
1426 2252 : case RELKIND_VIEW:
1427 2252 : options = view_reloptions(datum, false);
1428 2252 : break;
1429 2396 : case RELKIND_INDEX:
1430 : case RELKIND_PARTITIONED_INDEX:
1431 2396 : options = index_reloptions(amoptions, datum, false);
1432 2396 : break;
1433 0 : case RELKIND_FOREIGN_TABLE:
1434 0 : options = NULL;
1435 0 : break;
1436 0 : default:
1437 : Assert(false); /* can't get here */
1438 0 : options = NULL; /* keep compiler quiet */
1439 0 : break;
1440 : }
1441 :
1442 19924 : return options;
1443 : }
1444 :
1445 : static void
1446 22768 : parseRelOptionsInternal(Datum options, bool validate,
1447 : relopt_value *reloptions, int numoptions)
1448 : {
1449 22768 : ArrayType *array = DatumGetArrayTypeP(options);
1450 : Datum *optiondatums;
1451 : int noptions;
1452 : int i;
1453 :
1454 22768 : deconstruct_array_builtin(array, TEXTOID, &optiondatums, NULL, &noptions);
1455 :
1456 47242 : for (i = 0; i < noptions; i++)
1457 : {
1458 24796 : char *text_str = VARDATA(optiondatums[i]);
1459 24796 : int text_len = VARSIZE(optiondatums[i]) - VARHDRSZ;
1460 : int j;
1461 :
1462 : /* Search for a match in reloptions */
1463 93364 : for (j = 0; j < numoptions; j++)
1464 : {
1465 93280 : int kw_len = reloptions[j].gen->namelen;
1466 :
1467 93280 : if (text_len > kw_len && text_str[kw_len] == '=' &&
1468 25818 : strncmp(text_str, reloptions[j].gen->name, kw_len) == 0)
1469 : {
1470 24712 : parse_one_reloption(&reloptions[j], text_str, text_len,
1471 : validate);
1472 24456 : break;
1473 : }
1474 : }
1475 :
1476 24540 : if (j >= numoptions && validate)
1477 : {
1478 : char *s;
1479 : char *p;
1480 :
1481 66 : s = TextDatumGetCString(optiondatums[i]);
1482 66 : p = strchr(s, '=');
1483 66 : if (p)
1484 66 : *p = '\0';
1485 66 : ereport(ERROR,
1486 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1487 : errmsg("unrecognized parameter \"%s\"", s)));
1488 : }
1489 : }
1490 :
1491 : /* It's worth avoiding memory leaks in this function */
1492 22446 : pfree(optiondatums);
1493 :
1494 22446 : if (((void *) array) != DatumGetPointer(options))
1495 20212 : pfree(array);
1496 22446 : }
1497 :
1498 : /*
1499 : * Interpret reloptions that are given in text-array format.
1500 : *
1501 : * options is a reloption text array as constructed by transformRelOptions.
1502 : * kind specifies the family of options to be processed.
1503 : *
1504 : * The return value is a relopt_value * array on which the options actually
1505 : * set in the options array are marked with isset=true. The length of this
1506 : * array is returned in *numrelopts. Options not set are also present in the
1507 : * array; this is so that the caller can easily locate the default values.
1508 : *
1509 : * If there are no options of the given kind, numrelopts is set to 0 and NULL
1510 : * is returned (unless options are illegally supplied despite none being
1511 : * defined, in which case an error occurs).
1512 : *
1513 : * Note: values of type int, bool and real are allocated as part of the
1514 : * returned array. Values of type string are allocated separately and must
1515 : * be freed by the caller.
1516 : */
1517 : static relopt_value *
1518 106026 : parseRelOptions(Datum options, bool validate, relopt_kind kind,
1519 : int *numrelopts)
1520 : {
1521 106026 : relopt_value *reloptions = NULL;
1522 106026 : int numoptions = 0;
1523 : int i;
1524 : int j;
1525 :
1526 106026 : if (need_initialization)
1527 7720 : initialize_reloptions();
1528 :
1529 : /* Build a list of expected options, based on kind */
1530 :
1531 4781064 : for (i = 0; relOpts[i]; i++)
1532 4675038 : if (relOpts[i]->kinds & kind)
1533 1897282 : numoptions++;
1534 :
1535 106026 : if (numoptions > 0)
1536 : {
1537 106026 : reloptions = palloc(numoptions * sizeof(relopt_value));
1538 :
1539 4781064 : for (i = 0, j = 0; relOpts[i]; i++)
1540 : {
1541 4675038 : if (relOpts[i]->kinds & kind)
1542 : {
1543 1897282 : reloptions[j].gen = relOpts[i];
1544 1897282 : reloptions[j].isset = false;
1545 1897282 : j++;
1546 : }
1547 : }
1548 : }
1549 :
1550 : /* Done if no options */
1551 106026 : if (PointerIsValid(DatumGetPointer(options)))
1552 22322 : parseRelOptionsInternal(options, validate, reloptions, numoptions);
1553 :
1554 105790 : *numrelopts = numoptions;
1555 105790 : return reloptions;
1556 : }
1557 :
1558 : /* Parse local unregistered options. */
1559 : static relopt_value *
1560 2412 : parseLocalRelOptions(local_relopts *relopts, Datum options, bool validate)
1561 : {
1562 2412 : int nopts = list_length(relopts->options);
1563 2412 : relopt_value *values = palloc(sizeof(*values) * nopts);
1564 : ListCell *lc;
1565 2412 : int i = 0;
1566 :
1567 5528 : foreach(lc, relopts->options)
1568 : {
1569 3116 : local_relopt *opt = lfirst(lc);
1570 :
1571 3116 : values[i].gen = opt->option;
1572 3116 : values[i].isset = false;
1573 :
1574 3116 : i++;
1575 : }
1576 :
1577 2412 : if (options != (Datum) 0)
1578 446 : parseRelOptionsInternal(options, validate, values, nopts);
1579 :
1580 2326 : return values;
1581 : }
1582 :
1583 : /*
1584 : * Subroutine for parseRelOptions, to parse and validate a single option's
1585 : * value
1586 : */
1587 : static void
1588 24712 : parse_one_reloption(relopt_value *option, char *text_str, int text_len,
1589 : bool validate)
1590 : {
1591 : char *value;
1592 : int value_len;
1593 : bool parsed;
1594 24712 : bool nofree = false;
1595 :
1596 24712 : if (option->isset && validate)
1597 12 : ereport(ERROR,
1598 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1599 : errmsg("parameter \"%s\" specified more than once",
1600 : option->gen->name)));
1601 :
1602 24700 : value_len = text_len - option->gen->namelen - 1;
1603 24700 : value = (char *) palloc(value_len + 1);
1604 24700 : memcpy(value, text_str + option->gen->namelen + 1, value_len);
1605 24700 : value[value_len] = '\0';
1606 :
1607 24700 : switch (option->gen->type)
1608 : {
1609 14706 : case RELOPT_TYPE_BOOL:
1610 : {
1611 14706 : parsed = parse_bool(value, &option->values.bool_val);
1612 14706 : if (validate && !parsed)
1613 36 : ereport(ERROR,
1614 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1615 : errmsg("invalid value for boolean option \"%s\": %s",
1616 : option->gen->name, value)));
1617 : }
1618 14670 : break;
1619 8240 : case RELOPT_TYPE_INT:
1620 : {
1621 8240 : relopt_int *optint = (relopt_int *) option->gen;
1622 :
1623 8240 : parsed = parse_int(value, &option->values.int_val, 0, NULL);
1624 8240 : if (validate && !parsed)
1625 22 : ereport(ERROR,
1626 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1627 : errmsg("invalid value for integer option \"%s\": %s",
1628 : option->gen->name, value)));
1629 8218 : if (validate && (option->values.int_val < optint->min ||
1630 818 : option->values.int_val > optint->max))
1631 120 : ereport(ERROR,
1632 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1633 : errmsg("value %s out of bounds for option \"%s\"",
1634 : value, option->gen->name),
1635 : errdetail("Valid values are between \"%d\" and \"%d\".",
1636 : optint->min, optint->max)));
1637 : }
1638 8098 : break;
1639 466 : case RELOPT_TYPE_REAL:
1640 : {
1641 466 : relopt_real *optreal = (relopt_real *) option->gen;
1642 :
1643 466 : parsed = parse_real(value, &option->values.real_val, 0, NULL);
1644 466 : if (validate && !parsed)
1645 16 : ereport(ERROR,
1646 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1647 : errmsg("invalid value for floating point option \"%s\": %s",
1648 : option->gen->name, value)));
1649 450 : if (validate && (option->values.real_val < optreal->min ||
1650 160 : option->values.real_val > optreal->max))
1651 30 : ereport(ERROR,
1652 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1653 : errmsg("value %s out of bounds for option \"%s\"",
1654 : value, option->gen->name),
1655 : errdetail("Valid values are between \"%f\" and \"%f\".",
1656 : optreal->min, optreal->max)));
1657 : }
1658 420 : break;
1659 1124 : case RELOPT_TYPE_ENUM:
1660 : {
1661 1124 : relopt_enum *optenum = (relopt_enum *) option->gen;
1662 : relopt_enum_elt_def *elt;
1663 :
1664 1124 : parsed = false;
1665 2420 : for (elt = optenum->members; elt->string_val; elt++)
1666 : {
1667 2400 : if (pg_strcasecmp(value, elt->string_val) == 0)
1668 : {
1669 1104 : option->values.enum_val = elt->symbol_val;
1670 1104 : parsed = true;
1671 1104 : break;
1672 : }
1673 : }
1674 1124 : if (validate && !parsed)
1675 20 : ereport(ERROR,
1676 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1677 : errmsg("invalid value for enum option \"%s\": %s",
1678 : option->gen->name, value),
1679 : optenum->detailmsg ?
1680 : errdetail_internal("%s", _(optenum->detailmsg)) : 0));
1681 :
1682 : /*
1683 : * If value is not among the allowed string values, but we are
1684 : * not asked to validate, just use the default numeric value.
1685 : */
1686 1104 : if (!parsed)
1687 0 : option->values.enum_val = optenum->default_val;
1688 : }
1689 1104 : break;
1690 164 : case RELOPT_TYPE_STRING:
1691 : {
1692 164 : relopt_string *optstring = (relopt_string *) option->gen;
1693 :
1694 164 : option->values.string_val = value;
1695 164 : nofree = true;
1696 164 : if (validate && optstring->validate_cb)
1697 56 : (optstring->validate_cb) (value);
1698 164 : parsed = true;
1699 : }
1700 164 : break;
1701 0 : default:
1702 0 : elog(ERROR, "unsupported reloption type %d", option->gen->type);
1703 : parsed = true; /* quiet compiler */
1704 : break;
1705 : }
1706 :
1707 24456 : if (parsed)
1708 24456 : option->isset = true;
1709 24456 : if (!nofree)
1710 24292 : pfree(value);
1711 24456 : }
1712 :
1713 : /*
1714 : * Given the result from parseRelOptions, allocate a struct that's of the
1715 : * specified base size plus any extra space that's needed for string variables.
1716 : *
1717 : * "base" should be sizeof(struct) of the reloptions struct (StdRdOptions or
1718 : * equivalent).
1719 : */
1720 : static void *
1721 108116 : allocateReloptStruct(Size base, relopt_value *options, int numoptions)
1722 : {
1723 108116 : Size size = base;
1724 : int i;
1725 :
1726 2005464 : for (i = 0; i < numoptions; i++)
1727 : {
1728 1897348 : relopt_value *optval = &options[i];
1729 :
1730 1897348 : if (optval->gen->type == RELOPT_TYPE_STRING)
1731 : {
1732 232 : relopt_string *optstr = (relopt_string *) optval->gen;
1733 :
1734 232 : if (optstr->fill_cb)
1735 : {
1736 0 : const char *val = optval->isset ? optval->values.string_val :
1737 0 : optstr->default_isnull ? NULL : optstr->default_val;
1738 :
1739 0 : size += optstr->fill_cb(val, NULL);
1740 : }
1741 : else
1742 232 : size += GET_STRING_RELOPTION_LEN(*optval) + 1;
1743 : }
1744 : }
1745 :
1746 108116 : return palloc0(size);
1747 : }
1748 :
1749 : /*
1750 : * Given the result of parseRelOptions and a parsing table, fill in the
1751 : * struct (previously allocated with allocateReloptStruct) with the parsed
1752 : * values.
1753 : *
1754 : * rdopts is the pointer to the allocated struct to be filled.
1755 : * basesize is the sizeof(struct) that was passed to allocateReloptStruct.
1756 : * options, of length numoptions, is parseRelOptions' output.
1757 : * elems, of length numelems, is the table describing the allowed options.
1758 : * When validate is true, it is expected that all options appear in elems.
1759 : */
1760 : static void
1761 108116 : fillRelOptions(void *rdopts, Size basesize,
1762 : relopt_value *options, int numoptions,
1763 : bool validate,
1764 : const relopt_parse_elt *elems, int numelems)
1765 : {
1766 : int i;
1767 108116 : int offset = basesize;
1768 :
1769 2005464 : for (i = 0; i < numoptions; i++)
1770 : {
1771 : int j;
1772 1897348 : bool found = false;
1773 :
1774 22847814 : for (j = 0; j < numelems; j++)
1775 : {
1776 22847814 : if (strcmp(options[i].gen->name, elems[j].optname) == 0)
1777 : {
1778 : relopt_string *optstring;
1779 1897348 : char *itempos = ((char *) rdopts) + elems[j].offset;
1780 : char *string_val;
1781 :
1782 1897348 : switch (options[i].gen->type)
1783 : {
1784 256192 : case RELOPT_TYPE_BOOL:
1785 512384 : *(bool *) itempos = options[i].isset ?
1786 256192 : options[i].values.bool_val :
1787 241524 : ((relopt_bool *) options[i].gen)->default_val;
1788 256192 : break;
1789 1145832 : case RELOPT_TYPE_INT:
1790 2291664 : *(int *) itempos = options[i].isset ?
1791 1145832 : options[i].values.int_val :
1792 1137760 : ((relopt_int *) options[i].gen)->default_val;
1793 1145832 : break;
1794 392004 : case RELOPT_TYPE_REAL:
1795 784008 : *(double *) itempos = options[i].isset ?
1796 392004 : options[i].values.real_val :
1797 391598 : ((relopt_real *) options[i].gen)->default_val;
1798 392004 : break;
1799 103088 : case RELOPT_TYPE_ENUM:
1800 206176 : *(int *) itempos = options[i].isset ?
1801 103088 : options[i].values.enum_val :
1802 101984 : ((relopt_enum *) options[i].gen)->default_val;
1803 103088 : break;
1804 232 : case RELOPT_TYPE_STRING:
1805 232 : optstring = (relopt_string *) options[i].gen;
1806 232 : if (options[i].isset)
1807 160 : string_val = options[i].values.string_val;
1808 72 : else if (!optstring->default_isnull)
1809 30 : string_val = optstring->default_val;
1810 : else
1811 42 : string_val = NULL;
1812 :
1813 232 : if (optstring->fill_cb)
1814 : {
1815 : Size size =
1816 0 : optstring->fill_cb(string_val,
1817 : (char *) rdopts + offset);
1818 :
1819 0 : if (size)
1820 : {
1821 0 : *(int *) itempos = offset;
1822 0 : offset += size;
1823 : }
1824 : else
1825 0 : *(int *) itempos = 0;
1826 : }
1827 232 : else if (string_val == NULL)
1828 42 : *(int *) itempos = 0;
1829 : else
1830 : {
1831 190 : strcpy((char *) rdopts + offset, string_val);
1832 190 : *(int *) itempos = offset;
1833 190 : offset += strlen(string_val) + 1;
1834 : }
1835 232 : break;
1836 0 : default:
1837 0 : elog(ERROR, "unsupported reloption type %d",
1838 : options[i].gen->type);
1839 : break;
1840 : }
1841 1897348 : found = true;
1842 1897348 : break;
1843 : }
1844 : }
1845 1897348 : if (validate && !found)
1846 0 : elog(ERROR, "reloption \"%s\" not found in parse table",
1847 : options[i].gen->name);
1848 : }
1849 108116 : SET_VARSIZE(rdopts, offset);
1850 108116 : }
1851 :
1852 :
1853 : /*
1854 : * Option parser for anything that uses StdRdOptions.
1855 : */
1856 : bytea *
1857 85484 : default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
1858 : {
1859 : static const relopt_parse_elt tab[] = {
1860 : {"fillfactor", RELOPT_TYPE_INT, offsetof(StdRdOptions, fillfactor)},
1861 : {"autovacuum_enabled", RELOPT_TYPE_BOOL,
1862 : offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, enabled)},
1863 : {"autovacuum_vacuum_threshold", RELOPT_TYPE_INT,
1864 : offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_threshold)},
1865 : {"autovacuum_vacuum_max_threshold", RELOPT_TYPE_INT,
1866 : offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_max_threshold)},
1867 : {"autovacuum_vacuum_insert_threshold", RELOPT_TYPE_INT,
1868 : offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_ins_threshold)},
1869 : {"autovacuum_analyze_threshold", RELOPT_TYPE_INT,
1870 : offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, analyze_threshold)},
1871 : {"autovacuum_vacuum_cost_limit", RELOPT_TYPE_INT,
1872 : offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_cost_limit)},
1873 : {"autovacuum_freeze_min_age", RELOPT_TYPE_INT,
1874 : offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_min_age)},
1875 : {"autovacuum_freeze_max_age", RELOPT_TYPE_INT,
1876 : offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_max_age)},
1877 : {"autovacuum_freeze_table_age", RELOPT_TYPE_INT,
1878 : offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_table_age)},
1879 : {"autovacuum_multixact_freeze_min_age", RELOPT_TYPE_INT,
1880 : offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_min_age)},
1881 : {"autovacuum_multixact_freeze_max_age", RELOPT_TYPE_INT,
1882 : offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_max_age)},
1883 : {"autovacuum_multixact_freeze_table_age", RELOPT_TYPE_INT,
1884 : offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_table_age)},
1885 : {"log_autovacuum_min_duration", RELOPT_TYPE_INT,
1886 : offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, log_min_duration)},
1887 : {"toast_tuple_target", RELOPT_TYPE_INT,
1888 : offsetof(StdRdOptions, toast_tuple_target)},
1889 : {"autovacuum_vacuum_cost_delay", RELOPT_TYPE_REAL,
1890 : offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_cost_delay)},
1891 : {"autovacuum_vacuum_scale_factor", RELOPT_TYPE_REAL,
1892 : offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_scale_factor)},
1893 : {"autovacuum_vacuum_insert_scale_factor", RELOPT_TYPE_REAL,
1894 : offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_ins_scale_factor)},
1895 : {"autovacuum_analyze_scale_factor", RELOPT_TYPE_REAL,
1896 : offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, analyze_scale_factor)},
1897 : {"user_catalog_table", RELOPT_TYPE_BOOL,
1898 : offsetof(StdRdOptions, user_catalog_table)},
1899 : {"parallel_workers", RELOPT_TYPE_INT,
1900 : offsetof(StdRdOptions, parallel_workers)},
1901 : {"vacuum_index_cleanup", RELOPT_TYPE_ENUM,
1902 : offsetof(StdRdOptions, vacuum_index_cleanup)},
1903 : {"vacuum_truncate", RELOPT_TYPE_BOOL,
1904 : offsetof(StdRdOptions, vacuum_truncate)},
1905 : {"vacuum_max_eager_freeze_failure_rate", RELOPT_TYPE_REAL,
1906 : offsetof(StdRdOptions, vacuum_max_eager_freeze_failure_rate)}
1907 : };
1908 :
1909 85484 : return (bytea *) build_reloptions(reloptions, validate, kind,
1910 : sizeof(StdRdOptions),
1911 : tab, lengthof(tab));
1912 : }
1913 :
1914 : /*
1915 : * build_reloptions
1916 : *
1917 : * Parses "reloptions" provided by the caller, returning them in a
1918 : * structure containing the parsed options. The parsing is done with
1919 : * the help of a parsing table describing the allowed options, defined
1920 : * by "relopt_elems" of length "num_relopt_elems".
1921 : *
1922 : * "validate" must be true if reloptions value is freshly built by
1923 : * transformRelOptions(), as opposed to being read from the catalog, in which
1924 : * case the values contained in it must already be valid.
1925 : *
1926 : * NULL is returned if the passed-in options did not match any of the options
1927 : * in the parsing table, unless validate is true in which case an error would
1928 : * be reported.
1929 : */
1930 : void *
1931 106026 : build_reloptions(Datum reloptions, bool validate,
1932 : relopt_kind kind,
1933 : Size relopt_struct_size,
1934 : const relopt_parse_elt *relopt_elems,
1935 : int num_relopt_elems)
1936 : {
1937 : int numoptions;
1938 : relopt_value *options;
1939 : void *rdopts;
1940 :
1941 : /* parse options specific to given relation option kind */
1942 106026 : options = parseRelOptions(reloptions, validate, kind, &numoptions);
1943 : Assert(numoptions <= num_relopt_elems);
1944 :
1945 : /* if none set, we're done */
1946 105790 : if (numoptions == 0)
1947 : {
1948 : Assert(options == NULL);
1949 0 : return NULL;
1950 : }
1951 :
1952 : /* allocate and fill the structure */
1953 105790 : rdopts = allocateReloptStruct(relopt_struct_size, options, numoptions);
1954 105790 : fillRelOptions(rdopts, relopt_struct_size, options, numoptions,
1955 : validate, relopt_elems, num_relopt_elems);
1956 :
1957 105790 : pfree(options);
1958 :
1959 105790 : return rdopts;
1960 : }
1961 :
1962 : /*
1963 : * Parse local options, allocate a bytea struct that's of the specified
1964 : * 'base_size' plus any extra space that's needed for string variables,
1965 : * fill its option's fields located at the given offsets and return it.
1966 : */
1967 : void *
1968 2412 : build_local_reloptions(local_relopts *relopts, Datum options, bool validate)
1969 : {
1970 2412 : int noptions = list_length(relopts->options);
1971 2412 : relopt_parse_elt *elems = palloc(sizeof(*elems) * noptions);
1972 : relopt_value *vals;
1973 : void *opts;
1974 2412 : int i = 0;
1975 : ListCell *lc;
1976 :
1977 5528 : foreach(lc, relopts->options)
1978 : {
1979 3116 : local_relopt *opt = lfirst(lc);
1980 :
1981 3116 : elems[i].optname = opt->option->name;
1982 3116 : elems[i].opttype = opt->option->type;
1983 3116 : elems[i].offset = opt->offset;
1984 :
1985 3116 : i++;
1986 : }
1987 :
1988 2412 : vals = parseLocalRelOptions(relopts, options, validate);
1989 2326 : opts = allocateReloptStruct(relopts->relopt_struct_size, vals, noptions);
1990 2326 : fillRelOptions(opts, relopts->relopt_struct_size, vals, noptions, validate,
1991 : elems, noptions);
1992 :
1993 2326 : if (validate)
1994 652 : foreach(lc, relopts->validators)
1995 6 : ((relopts_validator) lfirst(lc)) (opts, vals, noptions);
1996 :
1997 2324 : if (elems)
1998 2324 : pfree(elems);
1999 :
2000 2324 : return opts;
2001 : }
2002 :
2003 : /*
2004 : * Option parser for partitioned tables
2005 : */
2006 : bytea *
2007 4868 : partitioned_table_reloptions(Datum reloptions, bool validate)
2008 : {
2009 4868 : if (validate && reloptions)
2010 12 : ereport(ERROR,
2011 : errcode(ERRCODE_WRONG_OBJECT_TYPE),
2012 : errmsg("cannot specify storage parameters for a partitioned table"),
2013 : errhint("Specify storage parameters for its leaf partitions instead."));
2014 4856 : return NULL;
2015 : }
2016 :
2017 : /*
2018 : * Option parser for views
2019 : */
2020 : bytea *
2021 17460 : view_reloptions(Datum reloptions, bool validate)
2022 : {
2023 : static const relopt_parse_elt tab[] = {
2024 : {"security_barrier", RELOPT_TYPE_BOOL,
2025 : offsetof(ViewOptions, security_barrier)},
2026 : {"security_invoker", RELOPT_TYPE_BOOL,
2027 : offsetof(ViewOptions, security_invoker)},
2028 : {"check_option", RELOPT_TYPE_ENUM,
2029 : offsetof(ViewOptions, check_option)}
2030 : };
2031 :
2032 17460 : return (bytea *) build_reloptions(reloptions, validate,
2033 : RELOPT_KIND_VIEW,
2034 : sizeof(ViewOptions),
2035 : tab, lengthof(tab));
2036 : }
2037 :
2038 : /*
2039 : * Parse options for heaps, views and toast tables.
2040 : */
2041 : bytea *
2042 92212 : heap_reloptions(char relkind, Datum reloptions, bool validate)
2043 : {
2044 : StdRdOptions *rdopts;
2045 :
2046 92212 : switch (relkind)
2047 : {
2048 37000 : case RELKIND_TOASTVALUE:
2049 : rdopts = (StdRdOptions *)
2050 37000 : default_reloptions(reloptions, validate, RELOPT_KIND_TOAST);
2051 36994 : if (rdopts != NULL)
2052 : {
2053 : /* adjust default-only parameters for TOAST relations */
2054 36994 : rdopts->fillfactor = 100;
2055 36994 : rdopts->autovacuum.analyze_threshold = -1;
2056 36994 : rdopts->autovacuum.analyze_scale_factor = -1;
2057 : }
2058 36994 : return (bytea *) rdopts;
2059 48484 : case RELKIND_RELATION:
2060 : case RELKIND_MATVIEW:
2061 48484 : return default_reloptions(reloptions, validate, RELOPT_KIND_HEAP);
2062 6728 : default:
2063 : /* other relkinds are not supported */
2064 6728 : return NULL;
2065 : }
2066 : }
2067 :
2068 :
2069 : /*
2070 : * Parse options for indexes.
2071 : *
2072 : * amoptions index AM's option parser function
2073 : * reloptions options as text[] datum
2074 : * validate error flag
2075 : */
2076 : bytea *
2077 31176 : index_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
2078 : {
2079 : Assert(amoptions != NULL);
2080 :
2081 : /* Assume function is strict */
2082 31176 : if (!PointerIsValid(DatumGetPointer(reloptions)))
2083 28252 : return NULL;
2084 :
2085 2924 : return amoptions(reloptions, validate);
2086 : }
2087 :
2088 : /*
2089 : * Option parser for attribute reloptions
2090 : */
2091 : bytea *
2092 38 : attribute_reloptions(Datum reloptions, bool validate)
2093 : {
2094 : static const relopt_parse_elt tab[] = {
2095 : {"n_distinct", RELOPT_TYPE_REAL, offsetof(AttributeOpts, n_distinct)},
2096 : {"n_distinct_inherited", RELOPT_TYPE_REAL, offsetof(AttributeOpts, n_distinct_inherited)}
2097 : };
2098 :
2099 38 : return (bytea *) build_reloptions(reloptions, validate,
2100 : RELOPT_KIND_ATTRIBUTE,
2101 : sizeof(AttributeOpts),
2102 : tab, lengthof(tab));
2103 : }
2104 :
2105 : /*
2106 : * Option parser for tablespace reloptions
2107 : */
2108 : bytea *
2109 120 : tablespace_reloptions(Datum reloptions, bool validate)
2110 : {
2111 : static const relopt_parse_elt tab[] = {
2112 : {"random_page_cost", RELOPT_TYPE_REAL, offsetof(TableSpaceOpts, random_page_cost)},
2113 : {"seq_page_cost", RELOPT_TYPE_REAL, offsetof(TableSpaceOpts, seq_page_cost)},
2114 : {"effective_io_concurrency", RELOPT_TYPE_INT, offsetof(TableSpaceOpts, effective_io_concurrency)},
2115 : {"maintenance_io_concurrency", RELOPT_TYPE_INT, offsetof(TableSpaceOpts, maintenance_io_concurrency)}
2116 : };
2117 :
2118 120 : return (bytea *) build_reloptions(reloptions, validate,
2119 : RELOPT_KIND_TABLESPACE,
2120 : sizeof(TableSpaceOpts),
2121 : tab, lengthof(tab));
2122 : }
2123 :
2124 : /*
2125 : * Determine the required LOCKMODE from an option list.
2126 : *
2127 : * Called from AlterTableGetLockLevel(), see that function
2128 : * for a longer explanation of how this works.
2129 : */
2130 : LOCKMODE
2131 752 : AlterTableGetRelOptionsLockLevel(List *defList)
2132 : {
2133 752 : LOCKMODE lockmode = NoLock;
2134 : ListCell *cell;
2135 :
2136 752 : if (defList == NIL)
2137 0 : return AccessExclusiveLock;
2138 :
2139 752 : if (need_initialization)
2140 8 : initialize_reloptions();
2141 :
2142 1540 : foreach(cell, defList)
2143 : {
2144 788 : DefElem *def = (DefElem *) lfirst(cell);
2145 : int i;
2146 :
2147 35946 : for (i = 0; relOpts[i]; i++)
2148 : {
2149 35158 : if (strncmp(relOpts[i]->name,
2150 35158 : def->defname,
2151 35158 : relOpts[i]->namelen + 1) == 0)
2152 : {
2153 1110 : if (lockmode < relOpts[i]->lockmode)
2154 746 : lockmode = relOpts[i]->lockmode;
2155 : }
2156 : }
2157 : }
2158 :
2159 752 : return lockmode;
2160 : }
|