Age Owner Branch data TLA 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
5578 alvherre@alvh.no-ip. 580 :CBC 3082 : initialize_reloptions(void)
581 : : {
582 : : int i;
583 : : int j;
584 : :
5148 tgl@sss.pgh.pa.us 585 : 3082 : j = 0;
5578 alvherre@alvh.no-ip. 586 [ + + ]: 27738 : for (i = 0; boolRelOpts[i].gen.name; i++)
587 : : {
3166 simon@2ndQuadrant.co 588 [ - + ]: 24656 : Assert(DoLockModesConflict(boolRelOpts[i].gen.lockmode,
589 : : boolRelOpts[i].gen.lockmode));
5578 alvherre@alvh.no-ip. 590 : 24656 : j++;
591 : : }
592 [ + + ]: 70886 : for (i = 0; intRelOpts[i].gen.name; i++)
593 : : {
3166 simon@2ndQuadrant.co 594 [ - + ]: 67804 : Assert(DoLockModesConflict(intRelOpts[i].gen.lockmode,
595 : : intRelOpts[i].gen.lockmode));
5578 alvherre@alvh.no-ip. 596 : 67804 : j++;
597 : : }
598 [ + + ]: 30820 : for (i = 0; realRelOpts[i].gen.name; i++)
599 : : {
3166 simon@2ndQuadrant.co 600 [ - + ]: 27738 : Assert(DoLockModesConflict(realRelOpts[i].gen.lockmode,
601 : : realRelOpts[i].gen.lockmode));
5578 alvherre@alvh.no-ip. 602 : 27738 : j++;
603 : : }
1663 604 [ + + ]: 12328 : for (i = 0; enumRelOpts[i].gen.name; i++)
605 : : {
606 [ - + ]: 9246 : Assert(DoLockModesConflict(enumRelOpts[i].gen.lockmode,
607 : : enumRelOpts[i].gen.lockmode));
608 : 9246 : j++;
609 : : }
5578 610 [ - + ]: 3082 : for (i = 0; stringRelOpts[i].gen.name; i++)
611 : : {
3166 simon@2ndQuadrant.co 612 [ # # ]:UBC 0 : Assert(DoLockModesConflict(stringRelOpts[i].gen.lockmode,
613 : : stringRelOpts[i].gen.lockmode));
5578 alvherre@alvh.no-ip. 614 : 0 : j++;
615 : : }
5578 alvherre@alvh.no-ip. 616 :CBC 3082 : j += num_custom_options;
617 : :
618 [ - + ]: 3082 : if (relOpts)
5578 alvherre@alvh.no-ip. 619 :UBC 0 : pfree(relOpts);
5578 alvherre@alvh.no-ip. 620 :CBC 6164 : relOpts = MemoryContextAlloc(TopMemoryContext,
621 : 3082 : (j + 1) * sizeof(relopt_gen *));
622 : :
623 : 3082 : j = 0;
624 [ + + ]: 27738 : for (i = 0; boolRelOpts[i].gen.name; i++)
625 : : {
626 : 24656 : relOpts[j] = &boolRelOpts[i].gen;
627 : 24656 : relOpts[j]->type = RELOPT_TYPE_BOOL;
628 : 24656 : relOpts[j]->namelen = strlen(relOpts[j]->name);
629 : 24656 : j++;
630 : : }
631 : :
632 [ + + ]: 70886 : for (i = 0; intRelOpts[i].gen.name; i++)
633 : : {
634 : 67804 : relOpts[j] = &intRelOpts[i].gen;
635 : 67804 : relOpts[j]->type = RELOPT_TYPE_INT;
636 : 67804 : relOpts[j]->namelen = strlen(relOpts[j]->name);
637 : 67804 : j++;
638 : : }
639 : :
640 [ + + ]: 30820 : for (i = 0; realRelOpts[i].gen.name; i++)
641 : : {
642 : 27738 : relOpts[j] = &realRelOpts[i].gen;
643 : 27738 : relOpts[j]->type = RELOPT_TYPE_REAL;
644 : 27738 : relOpts[j]->namelen = strlen(relOpts[j]->name);
645 : 27738 : j++;
646 : : }
647 : :
1663 648 [ + + ]: 12328 : for (i = 0; enumRelOpts[i].gen.name; i++)
649 : : {
650 : 9246 : relOpts[j] = &enumRelOpts[i].gen;
651 : 9246 : relOpts[j]->type = RELOPT_TYPE_ENUM;
652 : 9246 : relOpts[j]->namelen = strlen(relOpts[j]->name);
653 : 9246 : j++;
654 : : }
655 : :
5578 656 [ - + ]: 3082 : for (i = 0; stringRelOpts[i].gen.name; i++)
657 : : {
5578 alvherre@alvh.no-ip. 658 :UBC 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 : :
5578 alvherre@alvh.no-ip. 664 [ + + ]:CBC 6190 : for (i = 0; i < num_custom_options; i++)
665 : : {
666 : 3108 : relOpts[j] = custom_options[i];
667 : 3108 : j++;
668 : : }
669 : :
670 : : /* add a list terminator */
671 : 3082 : relOpts[j] = NULL;
672 : :
673 : : /* flag the work is complete */
5148 tgl@sss.pgh.pa.us 674 : 3082 : need_initialization = false;
5578 alvherre@alvh.no-ip. 675 : 3082 : }
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 : 96 : add_reloption_kind(void)
684 : : {
685 : : /* don't hand out the last bit so that the enum's behavior is portable */
686 [ - + ]: 96 : if (last_assigned_kind >= RELOPT_KIND_MAX)
5578 alvherre@alvh.no-ip. 687 [ # # ]:UBC 0 : ereport(ERROR,
688 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
689 : : errmsg("user-defined relation parameter types limit exceeded")));
5489 alvherre@alvh.no-ip. 690 :CBC 96 : last_assigned_kind <<= 1;
5439 tgl@sss.pgh.pa.us 691 : 96 : 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
5578 alvherre@alvh.no-ip. 700 : 3141 : add_reloption(relopt_gen *newoption)
701 : : {
702 : : static int max_custom_options = 0;
703 : :
704 [ + + ]: 3141 : if (num_custom_options >= max_custom_options)
705 : : {
706 : : MemoryContext oldcxt;
707 : :
708 : 381 : oldcxt = MemoryContextSwitchTo(TopMemoryContext);
709 : :
710 [ + + ]: 381 : if (max_custom_options == 0)
711 : : {
712 : 96 : max_custom_options = 8;
713 : 96 : custom_options = palloc(max_custom_options * sizeof(relopt_gen *));
714 : : }
715 : : else
716 : : {
717 : 285 : max_custom_options *= 2;
718 : 285 : custom_options = repalloc(custom_options,
719 : : max_custom_options * sizeof(relopt_gen *));
720 : : }
721 : 381 : MemoryContextSwitchTo(oldcxt);
722 : : }
723 : 3141 : custom_options[num_custom_options++] = newoption;
724 : :
725 : 3141 : need_initialization = true;
726 : 3141 : }
727 : :
728 : : /*
729 : : * init_local_reloptions
730 : : * Initialize local reloptions that will parsed into bytea structure of
731 : : * 'relopt_struct_size'.
732 : : */
733 : : void
573 pg@bowt.ie 734 : 2778 : init_local_reloptions(local_relopts *relopts, Size relopt_struct_size)
735 : : {
736 : 2778 : relopts->options = NIL;
737 : 2778 : relopts->validators = NIL;
738 : 2778 : relopts->relopt_struct_size = relopt_struct_size;
1476 akorotkov@postgresql 739 : 2778 : }
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
573 pg@bowt.ie 747 : 14 : register_reloptions_validator(local_relopts *relopts, relopts_validator validator)
748 : : {
749 : 14 : relopts->validators = lappend(relopts->validators, validator);
1476 akorotkov@postgresql 750 : 14 : }
751 : :
752 : : /*
753 : : * add_local_reloption
754 : : * Add an already-created custom reloption to the local list.
755 : : */
756 : : static void
757 : 1810 : add_local_reloption(local_relopts *relopts, relopt_gen *newoption, int offset)
758 : : {
759 : 1810 : local_relopt *opt = palloc(sizeof(*opt));
760 : :
761 [ - + ]: 1810 : Assert(offset < relopts->relopt_struct_size);
762 : :
763 : 1810 : opt->option = newoption;
764 : 1810 : opt->offset = offset;
765 : :
766 : 1810 : relopts->options = lappend(relopts->options, opt);
767 : 1810 : }
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 *
1663 michael@paquier.xyz 775 : 4951 : 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 : :
1476 akorotkov@postgresql 782 [ + + ]: 4951 : if (kinds != RELOPT_KIND_LOCAL)
783 : 3141 : oldcxt = MemoryContextSwitchTo(TopMemoryContext);
784 : : else
785 : 1810 : oldcxt = NULL;
786 : :
5578 alvherre@alvh.no-ip. 787 [ + + + + : 4951 : switch (type)
+ - ]
788 : : {
789 : 1 : case RELOPT_TYPE_BOOL:
790 : 1 : size = sizeof(relopt_bool);
791 : 1 : break;
792 : 4104 : case RELOPT_TYPE_INT:
793 : 4104 : size = sizeof(relopt_int);
794 : 4104 : break;
795 : 843 : case RELOPT_TYPE_REAL:
796 : 843 : size = sizeof(relopt_real);
797 : 843 : break;
1663 798 : 1 : case RELOPT_TYPE_ENUM:
799 : 1 : size = sizeof(relopt_enum);
800 : 1 : break;
4632 heikki.linnakangas@i 801 : 2 : case RELOPT_TYPE_STRING:
802 : 2 : size = sizeof(relopt_string);
803 : 2 : break;
5578 alvherre@alvh.no-ip. 804 :UBC 0 : default:
3178 tgl@sss.pgh.pa.us 805 [ # # ]: 0 : elog(ERROR, "unsupported reloption type %d", type);
806 : : return NULL; /* keep compiler quiet */
807 : : }
808 : :
5578 alvherre@alvh.no-ip. 809 :CBC 4951 : newoption = palloc(size);
810 : :
811 : 4951 : newoption->name = pstrdup(name);
812 [ + + ]: 4951 : if (desc)
813 : 4950 : newoption->desc = pstrdup(desc);
814 : : else
815 : 1 : newoption->desc = NULL;
5489 816 : 4951 : newoption->kinds = kinds;
5578 817 : 4951 : newoption->namelen = strlen(name);
818 : 4951 : newoption->type = type;
1663 michael@paquier.xyz 819 : 4951 : newoption->lockmode = lockmode;
820 : :
1476 akorotkov@postgresql 821 [ + + ]: 4951 : if (oldcxt != NULL)
822 : 3141 : MemoryContextSwitchTo(oldcxt);
823 : :
5578 alvherre@alvh.no-ip. 824 : 4951 : return newoption;
825 : : }
826 : :
827 : : /*
828 : : * init_bool_reloption
829 : : * Allocate and initialize a new boolean reloption
830 : : */
831 : : static relopt_bool *
1476 akorotkov@postgresql 832 : 1 : init_bool_reloption(bits32 kinds, const char *name, const char *desc,
833 : : bool default_val, LOCKMODE lockmode)
834 : : {
835 : : relopt_bool *newoption;
836 : :
5489 alvherre@alvh.no-ip. 837 : 1 : newoption = (relopt_bool *) allocate_reloption(kinds, RELOPT_TYPE_BOOL,
838 : : name, desc, lockmode);
5578 839 : 1 : newoption->default_val = default_val;
840 : :
1476 akorotkov@postgresql 841 : 1 : return newoption;
842 : : }
843 : :
844 : : /*
845 : : * add_bool_reloption
846 : : * Add a new boolean reloption
847 : : */
848 : : void
849 : 1 : add_bool_reloption(bits32 kinds, const char *name, const char *desc,
850 : : bool default_val, LOCKMODE lockmode)
851 : : {
852 : 1 : relopt_bool *newoption = init_bool_reloption(kinds, name, desc,
853 : : default_val, lockmode);
854 : :
5578 alvherre@alvh.no-ip. 855 : 1 : add_reloption((relopt_gen *) newoption);
856 : 1 : }
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
1476 akorotkov@postgresql 865 :UBC 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 *
1476 akorotkov@postgresql 881 :CBC 4104 : 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 : :
5489 alvherre@alvh.no-ip. 887 : 4104 : newoption = (relopt_int *) allocate_reloption(kinds, RELOPT_TYPE_INT,
888 : : name, desc, lockmode);
5578 889 : 4104 : newoption->default_val = default_val;
890 : 4104 : newoption->min = min_val;
891 : 4104 : newoption->max = max_val;
892 : :
1476 akorotkov@postgresql 893 : 4104 : return newoption;
894 : : }
895 : :
896 : : /*
897 : : * add_int_reloption
898 : : * Add a new integer reloption
899 : : */
900 : : void
901 : 3136 : 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 : 3136 : relopt_int *newoption = init_int_reloption(kinds, name, desc,
905 : : default_val, min_val,
906 : : max_val, lockmode);
907 : :
5578 alvherre@alvh.no-ip. 908 : 3136 : add_reloption((relopt_gen *) newoption);
909 : 3136 : }
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
1476 akorotkov@postgresql 918 : 968 : 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 : 968 : relopt_int *newoption = init_int_reloption(RELOPT_KIND_LOCAL,
923 : : name, desc, default_val,
924 : : min_val, max_val, 0);
925 : :
926 : 968 : add_local_reloption(relopts, (relopt_gen *) newoption, offset);
927 : 968 : }
928 : :
929 : : /*
930 : : * init_real_reloption
931 : : * Allocate and initialize a new real reloption
932 : : */
933 : : static relopt_real *
934 : 843 : 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 : :
5489 alvherre@alvh.no-ip. 940 : 843 : newoption = (relopt_real *) allocate_reloption(kinds, RELOPT_TYPE_REAL,
941 : : name, desc, lockmode);
5578 942 : 843 : newoption->default_val = default_val;
943 : 843 : newoption->min = min_val;
944 : 843 : newoption->max = max_val;
945 : :
1476 akorotkov@postgresql 946 : 843 : return newoption;
947 : : }
948 : :
949 : : /*
950 : : * add_real_reloption
951 : : * Add a new float reloption
952 : : */
953 : : void
954 : 1 : 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 : 1 : relopt_real *newoption = init_real_reloption(kinds, name, desc,
959 : : default_val, min_val,
960 : : max_val, lockmode);
961 : :
5578 alvherre@alvh.no-ip. 962 : 1 : add_reloption((relopt_gen *) newoption);
963 : 1 : }
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
1476 akorotkov@postgresql 972 : 842 : 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 : 842 : relopt_real *newoption = init_real_reloption(RELOPT_KIND_LOCAL,
977 : : name, desc,
978 : : default_val, min_val,
979 : : max_val, 0);
980 : :
981 : 842 : add_local_reloption(relopts, (relopt_gen *) newoption, offset);
982 : 842 : }
983 : :
984 : : /*
985 : : * init_enum_reloption
986 : : * Allocate and initialize a new enum reloption
987 : : */
988 : : static relopt_enum *
989 : 1 : 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 : 1 : newoption = (relopt_enum *) allocate_reloption(kinds, RELOPT_TYPE_ENUM,
996 : : name, desc, lockmode);
997 : 1 : newoption->members = members;
998 : 1 : newoption->default_val = default_val;
999 : 1 : newoption->detailmsg = detailmsg;
1000 : :
1001 : 1 : 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
1663 alvherre@alvh.no-ip. 1018 : 1 : 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 : : {
1476 akorotkov@postgresql 1022 : 1 : relopt_enum *newoption = init_enum_reloption(kinds, name, desc,
1023 : : members, default_val,
1024 : : detailmsg, lockmode);
1025 : :
1663 alvherre@alvh.no-ip. 1026 : 1 : add_reloption((relopt_gen *) newoption);
1027 : 1 : }
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
1476 akorotkov@postgresql 1036 :UBC 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 *
1476 akorotkov@postgresql 1053 :CBC 2 : 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 */
4632 heikki.linnakangas@i 1062 [ + - ]: 2 : if (validator)
1063 : 2 : (validator) (default_val);
1064 : :
1065 : 2 : newoption = (relopt_string *) allocate_reloption(kinds, RELOPT_TYPE_STRING,
1066 : : name, desc, lockmode);
5575 alvherre@alvh.no-ip. 1067 : 2 : newoption->validate_cb = validator;
1476 akorotkov@postgresql 1068 : 2 : newoption->fill_cb = filler;
5578 alvherre@alvh.no-ip. 1069 [ + + ]: 2 : if (default_val)
1070 : : {
1476 akorotkov@postgresql 1071 [ - + ]: 1 : if (kinds == RELOPT_KIND_LOCAL)
1476 akorotkov@postgresql 1072 :UBC 0 : newoption->default_val = strdup(default_val);
1073 : : else
1476 akorotkov@postgresql 1074 :CBC 1 : newoption->default_val = MemoryContextStrdup(TopMemoryContext, default_val);
4632 heikki.linnakangas@i 1075 : 1 : newoption->default_len = strlen(default_val);
5578 alvherre@alvh.no-ip. 1076 : 1 : newoption->default_isnull = false;
1077 : : }
1078 : : else
1079 : : {
4632 heikki.linnakangas@i 1080 : 1 : newoption->default_val = "";
5578 alvherre@alvh.no-ip. 1081 : 1 : newoption->default_len = 0;
1082 : 1 : newoption->default_isnull = true;
1083 : : }
1084 : :
1476 akorotkov@postgresql 1085 : 2 : 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 : 2 : 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 : 2 : relopt_string *newoption = init_string_reloption(kinds, name, desc,
1103 : : default_val,
1104 : : validator, NULL,
1105 : : lockmode);
1106 : :
5578 alvherre@alvh.no-ip. 1107 : 2 : add_reloption((relopt_gen *) newoption);
1108 : 2 : }
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
1476 akorotkov@postgresql 1118 :UBC 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
2357 peter_e@gmx.net 1156 :CBC 58442 : transformRelOptions(Datum oldOptions, List *defList, const char *namspace,
1157 : : char *validnsps[], bool acceptOidsOff, bool isReset)
1158 : : {
1159 : : Datum result;
1160 : : ArrayBuildState *astate;
1161 : : ListCell *cell;
1162 : :
1163 : : /* no change if empty list */
6495 tgl@sss.pgh.pa.us 1164 [ + + ]: 58442 : if (defList == NIL)
1165 : 56788 : return oldOptions;
1166 : :
1167 : : /* We build new array using accumArrayResult */
1168 : 1654 : astate = NULL;
1169 : :
1170 : : /* Copy any oldOptions that aren't to be replaced */
5841 alvherre@alvh.no-ip. 1171 [ + + ]: 1654 : if (PointerIsValid(DatumGetPointer(oldOptions)))
1172 : : {
6402 bruce@momjian.us 1173 : 168 : ArrayType *array = DatumGetArrayTypeP(oldOptions);
1174 : : Datum *oldoptions;
1175 : : int noldoptions;
1176 : : int i;
1177 : :
653 peter@eisentraut.org 1178 : 168 : deconstruct_array_builtin(array, TEXTOID, &oldoptions, NULL, &noldoptions);
1179 : :
6495 tgl@sss.pgh.pa.us 1180 [ + + ]: 431 : for (i = 0; i < noldoptions; i++)
1181 : : {
2590 noah@leadboat.com 1182 : 263 : char *text_str = VARDATA(oldoptions[i]);
1183 : 263 : int text_len = VARSIZE(oldoptions[i]) - VARHDRSZ;
1184 : :
1185 : : /* Search for a match in defList */
6495 tgl@sss.pgh.pa.us 1186 [ + - + + : 392 : foreach(cell, defList)
+ + ]
1187 : : {
5421 bruce@momjian.us 1188 : 287 : DefElem *def = (DefElem *) lfirst(cell);
1189 : : int kw_len;
1190 : :
1191 : : /* ignore if not in the same namespace */
5550 alvherre@alvh.no-ip. 1192 [ + + ]: 287 : if (namspace == NULL)
1193 : : {
5489 tgl@sss.pgh.pa.us 1194 [ - + ]: 263 : if (def->defnamespace != NULL)
5550 alvherre@alvh.no-ip. 1195 :UBC 0 : continue;
1196 : : }
5489 tgl@sss.pgh.pa.us 1197 [ + + ]:CBC 24 : else if (def->defnamespace == NULL)
5550 alvherre@alvh.no-ip. 1198 : 15 : continue;
2270 tgl@sss.pgh.pa.us 1199 [ - + ]: 9 : else if (strcmp(def->defnamespace, namspace) != 0)
5550 alvherre@alvh.no-ip. 1200 :UBC 0 : continue;
1201 : :
5489 tgl@sss.pgh.pa.us 1202 :CBC 272 : kw_len = strlen(def->defname);
6495 1203 [ + + + + ]: 272 : if (text_len > kw_len && text_str[kw_len] == '=' &&
2270 1204 [ + + ]: 169 : strncmp(text_str, def->defname, kw_len) == 0)
6495 1205 : 158 : break;
1206 : : }
1207 [ + + ]: 263 : if (!cell)
1208 : : {
1209 : : /* No match, so keep old option */
1210 : 105 : 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 [ + - + + : 3463 : foreach(cell, defList)
+ + ]
1223 : : {
5421 bruce@momjian.us 1224 : 1827 : DefElem *def = (DefElem *) lfirst(cell);
1225 : :
6495 tgl@sss.pgh.pa.us 1226 [ + + ]: 1827 : if (isReset)
1227 : : {
1228 [ + + ]: 122 : if (def->arg != NULL)
1229 [ + - ]: 6 : 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 : : */
5489 1243 [ + + ]: 1705 : if (def->defnamespace != NULL)
1244 : : {
5421 bruce@momjian.us 1245 : 56 : bool valid = false;
1246 : : int i;
1247 : :
5550 alvherre@alvh.no-ip. 1248 [ + + ]: 56 : if (validnsps)
1249 : : {
1250 [ + + ]: 56 : for (i = 0; validnsps[i]; i++)
1251 : : {
2270 tgl@sss.pgh.pa.us 1252 [ + + ]: 53 : if (strcmp(def->defnamespace, validnsps[i]) == 0)
1253 : : {
5550 alvherre@alvh.no-ip. 1254 : 50 : valid = true;
1255 : 50 : break;
1256 : : }
1257 : : }
1258 : : }
1259 : :
1260 [ + + ]: 56 : if (!valid)
1261 [ + - ]: 6 : 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 [ + + ]: 1699 : if (namspace == NULL)
1269 : : {
5489 tgl@sss.pgh.pa.us 1270 [ + + ]: 1294 : if (def->defnamespace != NULL)
5550 alvherre@alvh.no-ip. 1271 : 25 : continue;
1272 : : }
5489 tgl@sss.pgh.pa.us 1273 [ + + ]: 405 : else if (def->defnamespace == NULL)
5550 alvherre@alvh.no-ip. 1274 : 380 : continue;
2270 tgl@sss.pgh.pa.us 1275 [ - + ]: 25 : else if (strcmp(def->defnamespace, namspace) != 0)
6495 tgl@sss.pgh.pa.us 1276 :UBC 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 : : */
6495 tgl@sss.pgh.pa.us 1283 [ + + ]:CBC 1294 : if (def->arg != NULL)
5489 1284 : 1129 : value = defGetString(def);
1285 : : else
6495 1286 : 165 : 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 : : */
1972 andres@anarazel.de 1294 [ + + + + ]: 1294 : if (acceptOidsOff && def->defnamespace == NULL &&
1295 [ + + ]: 610 : strcmp(def->defname, "oids") == 0)
1296 : : {
1297 [ + + ]: 9 : if (defGetBoolean(def))
1298 [ + - ]: 6 : 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 : 3 : continue;
1303 : : }
1304 : :
5489 tgl@sss.pgh.pa.us 1305 : 1285 : len = VARHDRSZ + strlen(def->defname) + 1 + strlen(value);
1306 : : /* +1 leaves room for sprintf's trailing null */
6495 1307 : 1285 : t = (text *) palloc(len + 1);
6256 1308 : 1285 : SET_VARSIZE(t, len);
5489 1309 : 1285 : sprintf(VARDATA(t), "%s=%s", def->defname, value);
1310 : :
6495 1311 : 1285 : astate = accumArrayResult(astate, PointerGetDatum(t),
1312 : : false, TEXTOID,
1313 : : CurrentMemoryContext);
1314 : : }
1315 : : }
1316 : :
1317 [ + + ]: 1636 : if (astate)
1318 : 1214 : result = makeArrayResult(astate, CurrentMemoryContext);
1319 : : else
1320 : 422 : result = (Datum) 0;
1321 : :
1322 : 1636 : 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 *
5979 1331 : 14504 : untransformRelOptions(Datum options)
1332 : : {
1333 : 14504 : List *result = NIL;
1334 : : ArrayType *array;
1335 : : Datum *optiondatums;
1336 : : int noptions;
1337 : : int i;
1338 : :
1339 : : /* Nothing to do if no options */
5841 alvherre@alvh.no-ip. 1340 [ + + ]: 14504 : if (!PointerIsValid(DatumGetPointer(options)))
5979 tgl@sss.pgh.pa.us 1341 : 1782 : return result;
1342 : :
1343 : 12722 : array = DatumGetArrayTypeP(options);
1344 : :
653 peter@eisentraut.org 1345 : 12722 : deconstruct_array_builtin(array, TEXTOID, &optiondatums, NULL, &noptions);
1346 : :
5979 tgl@sss.pgh.pa.us 1347 [ + + ]: 37915 : for (i = 0; i < noptions; i++)
1348 : : {
1349 : : char *s;
1350 : : char *p;
1351 : 25193 : Node *val = NULL;
1352 : :
5864 1353 : 25193 : s = TextDatumGetCString(optiondatums[i]);
5979 1354 : 25193 : p = strchr(s, '=');
1355 [ + - ]: 25193 : if (p)
1356 : : {
1357 : 25193 : *p++ = '\0';
579 alvherre@alvh.no-ip. 1358 : 25193 : val = (Node *) makeString(p);
1359 : : }
1360 : 25193 : result = lappend(result, makeDefElem(s, val, -1));
1361 : : }
1362 : :
5979 tgl@sss.pgh.pa.us 1363 : 12722 : 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 *
3010 1379 : 862086 : 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 : :
5557 alvherre@alvh.no-ip. 1387 : 862086 : datum = fastgetattr(tuple,
1388 : : Anum_pg_class_reloptions,
1389 : : tupdesc,
1390 : : &isnull);
1391 [ + + ]: 862086 : if (isnull)
1392 : 855239 : return NULL;
1393 : :
1394 : 6847 : classForm = (Form_pg_class) GETSTRUCT(tuple);
1395 : :
1396 : : /* Parse into appropriate format; don't error out here */
1397 [ + - + + : 6847 : switch (classForm->relkind)
- - ]
1398 : : {
1399 : 4901 : case RELKIND_RELATION:
1400 : : case RELKIND_TOASTVALUE:
1401 : : case RELKIND_MATVIEW:
3 akorotkov@postgresql 1402 : 4901 : options = heap_reloptions(classForm->relkind, datum, false);
5557 alvherre@alvh.no-ip. 1403 : 4901 : break;
1613 michael@paquier.xyz 1404 :UBC 0 : case RELKIND_PARTITIONED_TABLE:
1405 : 0 : options = partitioned_table_reloptions(datum, false);
1406 : 0 : break;
3562 alvherre@alvh.no-ip. 1407 :CBC 968 : case RELKIND_VIEW:
1408 : 968 : options = view_reloptions(datum, false);
1409 : 968 : break;
5557 1410 : 978 : case RELKIND_INDEX:
1411 : : case RELKIND_PARTITIONED_INDEX:
1412 : 978 : options = index_reloptions(amoptions, datum, false);
1413 : 978 : break;
4852 rhaas@postgresql.org 1414 :UBC 0 : case RELKIND_FOREIGN_TABLE:
1415 : 0 : options = NULL;
1416 : 0 : break;
5557 alvherre@alvh.no-ip. 1417 : 0 : default:
1418 : 0 : Assert(false); /* can't get here */
1419 : : options = NULL; /* keep compiler quiet */
1420 : : break;
1421 : : }
1422 : :
5557 alvherre@alvh.no-ip. 1423 :CBC 6847 : return options;
1424 : : }
1425 : :
1426 : : static void
1476 akorotkov@postgresql 1427 : 8209 : parseRelOptionsInternal(Datum options, bool validate,
1428 : : relopt_value *reloptions, int numoptions)
1429 : : {
1430 : 8209 : ArrayType *array = DatumGetArrayTypeP(options);
1431 : : Datum *optiondatums;
1432 : : int noptions;
1433 : : int i;
1434 : :
653 peter@eisentraut.org 1435 : 8209 : deconstruct_array_builtin(array, TEXTOID, &optiondatums, NULL, &noptions);
1436 : :
1476 akorotkov@postgresql 1437 [ + + ]: 17201 : for (i = 0; i < noptions; i++)
1438 : : {
1439 : 9153 : char *text_str = VARDATA(optiondatums[i]);
1440 : 9153 : int text_len = VARSIZE(optiondatums[i]) - VARHDRSZ;
1441 : : int j;
1442 : :
1443 : : /* Search for a match in reloptions */
1444 [ + + ]: 40587 : for (j = 0; j < numoptions; j++)
1445 : : {
1446 : 40545 : int kw_len = reloptions[j].gen->namelen;
1447 : :
1448 [ + + + + ]: 40545 : if (text_len > kw_len && text_str[kw_len] == '=' &&
1449 [ + + ]: 9584 : strncmp(text_str, reloptions[j].gen->name, kw_len) == 0)
1450 : : {
1451 : 9111 : parse_one_reloption(&reloptions[j], text_str, text_len,
1452 : : validate);
1453 : 8983 : break;
1454 : : }
1455 : : }
1456 : :
1457 [ + + + + ]: 9025 : if (j >= numoptions && validate)
1458 : : {
1459 : : char *s;
1460 : : char *p;
1461 : :
1462 : 33 : s = TextDatumGetCString(optiondatums[i]);
1463 : 33 : p = strchr(s, '=');
1464 [ + - ]: 33 : if (p)
1465 : 33 : *p = '\0';
1466 [ + - ]: 33 : 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 : 8048 : pfree(optiondatums);
1474 : :
1475 [ + + ]: 8048 : if (((void *) array) != DatumGetPointer(options))
1476 : 6987 : pfree(array);
1477 : 8048 : }
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 *
5578 alvherre@alvh.no-ip. 1499 : 48143 : parseRelOptions(Datum options, bool validate, relopt_kind kind,
1500 : : int *numrelopts)
1501 : : {
2571 rhaas@postgresql.org 1502 : 48143 : relopt_value *reloptions = NULL;
5578 alvherre@alvh.no-ip. 1503 : 48143 : int numoptions = 0;
1504 : : int i;
1505 : : int j;
1506 : :
1507 [ + + ]: 48143 : if (need_initialization)
1508 : 3078 : initialize_reloptions();
1509 : :
1510 : : /* Build a list of expected options, based on kind */
1511 : :
1512 [ + + ]: 2074535 : for (i = 0; relOpts[i]; i++)
5489 1513 [ + + ]: 2026392 : if (relOpts[i]->kinds & kind)
5578 1514 : 782544 : numoptions++;
1515 : :
2571 rhaas@postgresql.org 1516 [ + - ]: 48143 : if (numoptions > 0)
1517 : : {
1518 : 48143 : reloptions = palloc(numoptions * sizeof(relopt_value));
1519 : :
1520 [ + + ]: 2074535 : for (i = 0, j = 0; relOpts[i]; i++)
1521 : : {
1522 [ + + ]: 2026392 : if (relOpts[i]->kinds & kind)
1523 : : {
1524 : 782544 : reloptions[j].gen = relOpts[i];
1525 : 782544 : reloptions[j].isset = false;
1526 : 782544 : j++;
1527 : : }
1528 : : }
1529 : : }
1530 : :
1531 : : /* Done if no options */
5578 alvherre@alvh.no-ip. 1532 [ + + ]: 48143 : if (PointerIsValid(DatumGetPointer(options)))
1476 akorotkov@postgresql 1533 : 7992 : parseRelOptionsInternal(options, validate, reloptions, numoptions);
1534 : :
1535 : 48025 : *numrelopts = numoptions;
1536 : 48025 : return reloptions;
1537 : : }
1538 : :
1539 : : /* Parse local unregistered options. */
1540 : : static relopt_value *
1541 : 1389 : parseLocalRelOptions(local_relopts *relopts, Datum options, bool validate)
1542 : : {
1543 : 1389 : int nopts = list_length(relopts->options);
1544 : 1389 : relopt_value *values = palloc(sizeof(*values) * nopts);
1545 : : ListCell *lc;
1546 : 1389 : int i = 0;
1547 : :
1548 [ + - + + : 3199 : foreach(lc, relopts->options)
+ + ]
1549 : : {
1550 : 1810 : local_relopt *opt = lfirst(lc);
1551 : :
1552 : 1810 : values[i].gen = opt->option;
1553 : 1810 : values[i].isset = false;
1554 : :
1555 : 1810 : i++;
1556 : : }
1557 : :
1558 [ + + ]: 1389 : if (options != (Datum) 0)
1559 : 217 : parseRelOptionsInternal(options, validate, values, nopts);
1560 : :
1561 : 1346 : return values;
1562 : : }
1563 : :
1564 : : /*
1565 : : * Subroutine for parseRelOptions, to parse and validate a single option's
1566 : : * value
1567 : : */
1568 : : static void
5578 alvherre@alvh.no-ip. 1569 : 9111 : 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 : 9111 : bool nofree = false;
1576 : :
1577 [ + + + + ]: 9111 : if (option->isset && validate)
1578 [ + - ]: 6 : ereport(ERROR,
1579 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1580 : : errmsg("parameter \"%s\" specified more than once",
1581 : : option->gen->name)));
1582 : :
1583 : 9105 : value_len = text_len - option->gen->namelen - 1;
1584 : 9105 : value = (char *) palloc(value_len + 1);
1585 : 9105 : memcpy(value, text_str + option->gen->namelen + 1, value_len);
1586 : 9105 : value[value_len] = '\0';
1587 : :
1588 [ + + + + : 9105 : switch (option->gen->type)
+ - ]
1589 : : {
1590 : 4480 : case RELOPT_TYPE_BOOL:
1591 : : {
1592 : 4480 : parsed = parse_bool(value, &option->values.bool_val);
1593 [ + + + + ]: 4480 : if (validate && !parsed)
1594 [ + - ]: 18 : ereport(ERROR,
1595 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1596 : : errmsg("invalid value for boolean option \"%s\": %s",
1597 : : option->gen->name, value)));
1598 : : }
1599 : 4462 : break;
1600 : 3793 : case RELOPT_TYPE_INT:
1601 : : {
5421 bruce@momjian.us 1602 : 3793 : relopt_int *optint = (relopt_int *) option->gen;
1603 : :
3516 fujii@postgresql.org 1604 : 3793 : parsed = parse_int(value, &option->values.int_val, 0, NULL);
5578 alvherre@alvh.no-ip. 1605 [ + + + + ]: 3793 : if (validate && !parsed)
1606 [ + - ]: 11 : ereport(ERROR,
1607 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1608 : : errmsg("invalid value for integer option \"%s\": %s",
1609 : : option->gen->name, value)));
1610 [ + + + + ]: 3782 : if (validate && (option->values.int_val < optint->min ||
1611 [ + + ]: 398 : option->values.int_val > optint->max))
1612 [ + - ]: 60 : 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 : 3722 : break;
1620 : 222 : case RELOPT_TYPE_REAL:
1621 : : {
5421 bruce@momjian.us 1622 : 222 : relopt_real *optreal = (relopt_real *) option->gen;
1623 : :
1862 tgl@sss.pgh.pa.us 1624 : 222 : parsed = parse_real(value, &option->values.real_val, 0, NULL);
5578 alvherre@alvh.no-ip. 1625 [ + + + + ]: 222 : if (validate && !parsed)
1626 [ + - ]: 8 : ereport(ERROR,
1627 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1628 : : errmsg("invalid value for floating point option \"%s\": %s",
1629 : : option->gen->name, value)));
1630 [ + + + + ]: 214 : if (validate && (option->values.real_val < optreal->min ||
1631 [ + + ]: 80 : option->values.real_val > optreal->max))
1632 [ + - ]: 15 : 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 : 199 : break;
1663 1640 : 528 : case RELOPT_TYPE_ENUM:
1641 : : {
1642 : 528 : relopt_enum *optenum = (relopt_enum *) option->gen;
1643 : : relopt_enum_elt_def *elt;
1644 : :
1645 : 528 : parsed = false;
1646 [ + + ]: 1113 : for (elt = optenum->members; elt->string_val; elt++)
1647 : : {
1648 [ + + ]: 1103 : if (pg_strcasecmp(value, elt->string_val) == 0)
1649 : : {
1650 : 518 : option->values.enum_val = elt->symbol_val;
1651 : 518 : parsed = true;
1652 : 518 : break;
1653 : : }
1654 : : }
1655 [ + + + + ]: 528 : if (validate && !parsed)
1656 [ + - + - ]: 10 : 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 [ - + ]: 518 : if (!parsed)
1663 alvherre@alvh.no-ip. 1668 :UBC 0 : option->values.enum_val = optenum->default_val;
1669 : : }
1663 alvherre@alvh.no-ip. 1670 :CBC 518 : break;
5578 1671 : 82 : case RELOPT_TYPE_STRING:
1672 : : {
5421 bruce@momjian.us 1673 : 82 : relopt_string *optstring = (relopt_string *) option->gen;
1674 : :
5575 alvherre@alvh.no-ip. 1675 : 82 : option->values.string_val = value;
1676 : 82 : nofree = true;
5571 1677 [ + + + - ]: 82 : if (validate && optstring->validate_cb)
1678 : 28 : (optstring->validate_cb) (value);
5575 1679 : 82 : parsed = true;
1680 : : }
5578 1681 : 82 : break;
5578 alvherre@alvh.no-ip. 1682 :UBC 0 : default:
1683 [ # # ]: 0 : elog(ERROR, "unsupported reloption type %d", option->gen->type);
1684 : : parsed = true; /* quiet compiler */
1685 : : break;
1686 : : }
1687 : :
5578 alvherre@alvh.no-ip. 1688 [ + - ]:CBC 8983 : if (parsed)
1689 : 8983 : option->isset = true;
1690 [ + + ]: 8983 : if (!nofree)
1691 : 8901 : pfree(value);
1692 : 8983 : }
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 *
5571 1702 : 49371 : allocateReloptStruct(Size base, relopt_value *options, int numoptions)
1703 : : {
5421 bruce@momjian.us 1704 : 49371 : Size size = base;
1705 : : int i;
1706 : :
5571 alvherre@alvh.no-ip. 1707 [ + + ]: 832302 : for (i = 0; i < numoptions; i++)
1708 : : {
1476 akorotkov@postgresql 1709 : 782931 : relopt_value *optval = &options[i];
1710 : :
1711 [ + + ]: 782931 : if (optval->gen->type == RELOPT_TYPE_STRING)
1712 : : {
1713 : 116 : relopt_string *optstr = (relopt_string *) optval->gen;
1714 : :
1715 [ - + ]: 116 : if (optstr->fill_cb)
1716 : : {
1476 akorotkov@postgresql 1717 [ # # ]:UBC 0 : const char *val = optval->isset ? optval->values.string_val :
331 tgl@sss.pgh.pa.us 1718 [ # # ]: 0 : optstr->default_isnull ? NULL : optstr->default_val;
1719 : :
1476 akorotkov@postgresql 1720 : 0 : size += optstr->fill_cb(val, NULL);
1721 : : }
1722 : : else
1476 akorotkov@postgresql 1723 [ + + ]:CBC 116 : size += GET_STRING_RELOPTION_LEN(*optval) + 1;
1724 : : }
1725 : : }
1726 : :
5571 alvherre@alvh.no-ip. 1727 : 49371 : 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
5501 tgl@sss.pgh.pa.us 1742 : 49371 : 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;
5421 bruce@momjian.us 1748 : 49371 : int offset = basesize;
1749 : :
5571 alvherre@alvh.no-ip. 1750 [ + + ]: 832302 : for (i = 0; i < numoptions; i++)
1751 : : {
1752 : : int j;
5421 bruce@momjian.us 1753 : 782931 : bool found = false;
1754 : :
5571 alvherre@alvh.no-ip. 1755 [ + - ]: 8617890 : for (j = 0; j < numelems; j++)
1756 : : {
2270 tgl@sss.pgh.pa.us 1757 [ + + ]: 8617890 : if (strcmp(options[i].gen->name, elems[j].optname) == 0)
1758 : : {
1759 : : relopt_string *optstring;
5421 bruce@momjian.us 1760 : 782931 : char *itempos = ((char *) rdopts) + elems[j].offset;
1761 : : char *string_val;
1762 : :
5571 alvherre@alvh.no-ip. 1763 [ + + + + : 782931 : switch (options[i].gen->type)
+ - ]
1764 : : {
1765 : 115422 : case RELOPT_TYPE_BOOL:
1766 : 230844 : *(bool *) itempos = options[i].isset ?
1767 [ + + ]: 115422 : options[i].values.bool_val :
1768 [ + + ]: 110961 : ((relopt_bool *) options[i].gen)->default_val;
1769 : 115422 : break;
1770 : 481015 : case RELOPT_TYPE_INT:
1771 : 962030 : *(int *) itempos = options[i].isset ?
1772 [ + + ]: 481015 : options[i].values.int_val :
1773 : 477306 : ((relopt_int *) options[i].gen)->default_val;
1774 : 481015 : break;
1775 : 139493 : case RELOPT_TYPE_REAL:
1776 : 278986 : *(double *) itempos = options[i].isset ?
1777 [ + + ]: 139493 : options[i].values.real_val :
1778 : 139301 : ((relopt_real *) options[i].gen)->default_val;
1779 : 139493 : break;
1663 1780 : 46885 : case RELOPT_TYPE_ENUM:
1781 : 93770 : *(int *) itempos = options[i].isset ?
1782 [ + + ]: 46885 : options[i].values.enum_val :
1783 : 46367 : ((relopt_enum *) options[i].gen)->default_val;
1784 : 46885 : break;
5571 1785 : 116 : case RELOPT_TYPE_STRING:
1786 : 116 : optstring = (relopt_string *) options[i].gen;
1787 [ + + ]: 116 : if (options[i].isset)
1788 : 80 : string_val = options[i].values.string_val;
1789 [ + + ]: 36 : else if (!optstring->default_isnull)
1790 : 15 : string_val = optstring->default_val;
1791 : : else
1792 : 21 : string_val = NULL;
1793 : :
1476 akorotkov@postgresql 1794 [ - + ]: 116 : if (optstring->fill_cb)
1795 : : {
1796 : : Size size =
331 tgl@sss.pgh.pa.us 1797 :UBC 0 : optstring->fill_cb(string_val,
1798 : : (char *) rdopts + offset);
1799 : :
1476 akorotkov@postgresql 1800 [ # # ]: 0 : if (size)
1801 : : {
1802 : 0 : *(int *) itempos = offset;
1803 : 0 : offset += size;
1804 : : }
1805 : : else
1806 : 0 : *(int *) itempos = 0;
1807 : : }
1476 akorotkov@postgresql 1808 [ + + ]:CBC 116 : else if (string_val == NULL)
5571 alvherre@alvh.no-ip. 1809 : 21 : *(int *) itempos = 0;
1810 : : else
1811 : : {
1812 : 95 : strcpy((char *) rdopts + offset, string_val);
1813 : 95 : *(int *) itempos = offset;
1814 : 95 : offset += strlen(string_val) + 1;
1815 : : }
1816 : 116 : break;
5571 alvherre@alvh.no-ip. 1817 :UBC 0 : default:
3178 tgl@sss.pgh.pa.us 1818 [ # # ]: 0 : elog(ERROR, "unsupported reloption type %d",
1819 : : options[i].gen->type);
1820 : : break;
1821 : : }
5571 alvherre@alvh.no-ip. 1822 :CBC 782931 : found = true;
1823 : 782931 : break;
1824 : : }
1825 : : }
1916 tgl@sss.pgh.pa.us 1826 [ + + - + ]: 782931 : if (validate && !found)
5501 tgl@sss.pgh.pa.us 1827 [ # # ]:UBC 0 : elog(ERROR, "reloption \"%s\" not found in parse table",
1828 : : options[i].gen->name);
1829 : : }
5571 alvherre@alvh.no-ip. 1830 :CBC 49371 : SET_VARSIZE(rdopts, offset);
1831 : 49371 : }
1832 : :
1833 : :
1834 : : /*
1835 : : * Option parser for anything that uses StdRdOptions.
1836 : : */
1837 : : bytea *
5578 1838 : 39166 : 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 : :
1622 michael@paquier.xyz 1886 : 39166 : 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 : 48143 : 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 */
5578 alvherre@alvh.no-ip. 1919 : 48143 : options = parseRelOptions(reloptions, validate, kind, &numoptions);
1622 michael@paquier.xyz 1920 [ - + ]: 48025 : Assert(numoptions <= num_relopt_elems);
1921 : :
1922 : : /* if none set, we're done */
5578 alvherre@alvh.no-ip. 1923 [ - + ]: 48025 : if (numoptions == 0)
1924 : : {
1622 michael@paquier.xyz 1925 [ # # ]:UBC 0 : Assert(options == NULL);
6495 tgl@sss.pgh.pa.us 1926 : 0 : return NULL;
1927 : : }
1928 : :
1929 : : /* allocate and fill the structure */
1622 michael@paquier.xyz 1930 :CBC 48025 : rdopts = allocateReloptStruct(relopt_struct_size, options, numoptions);
1931 : 48025 : fillRelOptions(rdopts, relopt_struct_size, options, numoptions,
1932 : : validate, relopt_elems, num_relopt_elems);
1933 : :
5578 alvherre@alvh.no-ip. 1934 : 48025 : pfree(options);
1935 : :
1622 michael@paquier.xyz 1936 : 48025 : 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 *
1476 akorotkov@postgresql 1945 : 1389 : build_local_reloptions(local_relopts *relopts, Datum options, bool validate)
1946 : : {
1947 : 1389 : int noptions = list_length(relopts->options);
1948 : 1389 : relopt_parse_elt *elems = palloc(sizeof(*elems) * noptions);
1949 : : relopt_value *vals;
1950 : : void *opts;
1951 : 1389 : int i = 0;
1952 : : ListCell *lc;
1953 : :
1954 [ + - + + : 3199 : foreach(lc, relopts->options)
+ + ]
1955 : : {
1956 : 1810 : local_relopt *opt = lfirst(lc);
1957 : :
1958 : 1810 : elems[i].optname = opt->option->name;
1959 : 1810 : elems[i].opttype = opt->option->type;
1960 : 1810 : elems[i].offset = opt->offset;
1961 : :
1962 : 1810 : i++;
1963 : : }
1964 : :
1965 : 1389 : vals = parseLocalRelOptions(relopts, options, validate);
1966 : 1346 : opts = allocateReloptStruct(relopts->relopt_struct_size, vals, noptions);
1967 : 1346 : fillRelOptions(opts, relopts->relopt_struct_size, vals, noptions, validate,
1968 : : elems, noptions);
1969 : :
357 1970 [ + + ]: 1346 : if (validate)
1971 [ + + + + : 324 : foreach(lc, relopts->validators)
+ + ]
1972 : 3 : ((relopts_validator) lfirst(lc)) (opts, vals, noptions);
1973 : :
1476 1974 [ + - ]: 1345 : if (elems)
1975 : 1345 : pfree(elems);
1976 : :
1977 : 1345 : return opts;
1978 : : }
1979 : :
1980 : : /*
1981 : : * Option parser for partitioned tables
1982 : : */
1983 : : bytea *
1613 michael@paquier.xyz 1984 : 2429 : partitioned_table_reloptions(Datum reloptions, bool validate)
1985 : : {
522 tgl@sss.pgh.pa.us 1986 [ + - + + ]: 2429 : if (validate && reloptions)
1987 [ + - ]: 6 : 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 : 2423 : return NULL;
1992 : : }
1993 : :
1994 : : /*
1995 : : * Option parser for views
1996 : : */
1997 : : bytea *
3562 alvherre@alvh.no-ip. 1998 : 7658 : 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 : :
1622 michael@paquier.xyz 2009 : 7658 : 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 *
3 akorotkov@postgresql 2019 : 40562 : heap_reloptions(char relkind, Datum reloptions, bool validate)
2020 : : {
2021 : : StdRdOptions *rdopts;
2022 : :
5489 alvherre@alvh.no-ip. 2023 [ + + + ]: 40562 : switch (relkind)
2024 : : {
2025 : 18123 : case RELKIND_TOASTVALUE:
2026 : : rdopts = (StdRdOptions *)
5060 itagaki.takahiro@gma 2027 : 18123 : default_reloptions(reloptions, validate, RELOPT_KIND_TOAST);
2028 [ + - ]: 18120 : if (rdopts != NULL)
2029 : : {
2030 : : /* adjust default-only parameters for TOAST relations */
2031 : 18120 : rdopts->fillfactor = 100;
3 akorotkov@postgresql 2032 : 18120 : rdopts->autovacuum.analyze_threshold = -1;
2033 : 18120 : rdopts->autovacuum.analyze_scale_factor = -1;
2034 : : }
5060 itagaki.takahiro@gma 2035 : 18120 : return (bytea *) rdopts;
5489 alvherre@alvh.no-ip. 2036 : 21043 : case RELKIND_RELATION:
2037 : : case RELKIND_MATVIEW:
3 akorotkov@postgresql 2038 : 21043 : return default_reloptions(reloptions, validate, RELOPT_KIND_HEAP);
5489 alvherre@alvh.no-ip. 2039 : 1396 : default:
2040 : : /* other relkinds are not supported */
2041 : 1396 : 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 *
3010 tgl@sss.pgh.pa.us 2054 : 14482 : index_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
2055 : : {
2056 [ - + ]: 14482 : Assert(amoptions != NULL);
2057 : :
2058 : : /* Assume function is strict */
5841 alvherre@alvh.no-ip. 2059 [ + + ]: 14482 : if (!PointerIsValid(DatumGetPointer(reloptions)))
6495 tgl@sss.pgh.pa.us 2060 : 13240 : return NULL;
2061 : :
3010 2062 : 1242 : return amoptions(reloptions, validate);
2063 : : }
2064 : :
2065 : : /*
2066 : : * Option parser for attribute reloptions
2067 : : */
2068 : : bytea *
5196 rhaas@postgresql.org 2069 : 19 : 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 : :
1622 michael@paquier.xyz 2076 : 19 : 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 *
5213 rhaas@postgresql.org 2086 : 58 : 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 : :
1622 michael@paquier.xyz 2095 : 58 : 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
3166 simon@2ndQuadrant.co 2108 : 370 : AlterTableGetRelOptionsLockLevel(List *defList)
2109 : : {
2866 rhaas@postgresql.org 2110 : 370 : LOCKMODE lockmode = NoLock;
2111 : : ListCell *cell;
2112 : :
3166 simon@2ndQuadrant.co 2113 [ - + ]: 370 : if (defList == NIL)
3166 simon@2ndQuadrant.co 2114 :UBC 0 : return AccessExclusiveLock;
2115 : :
3166 simon@2ndQuadrant.co 2116 [ + + ]:CBC 370 : if (need_initialization)
2117 : 4 : initialize_reloptions();
2118 : :
2119 [ + - + + : 758 : foreach(cell, defList)
+ + ]
2120 : : {
2866 rhaas@postgresql.org 2121 : 388 : DefElem *def = (DefElem *) lfirst(cell);
2122 : : int i;
2123 : :
3166 simon@2ndQuadrant.co 2124 [ + + ]: 16927 : for (i = 0; relOpts[i]; i++)
2125 : : {
2270 tgl@sss.pgh.pa.us 2126 : 16539 : if (strncmp(relOpts[i]->name,
2127 : 16539 : def->defname,
2128 [ + + ]: 16539 : relOpts[i]->namelen + 1) == 0)
2129 : : {
3166 simon@2ndQuadrant.co 2130 [ + + ]: 549 : if (lockmode < relOpts[i]->lockmode)
2131 : 367 : lockmode = relOpts[i]->lockmode;
2132 : : }
2133 : : }
2134 : : }
2135 : :
2136 : 370 : return lockmode;
2137 : : }
|