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