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