LCOV - code coverage report
Current view: top level - src/backend/access/common - reloptions.c (source / functions) Hit Total Coverage
Test: PostgreSQL 19devel Lines: 525 580 90.5 %
Date: 2026-01-30 00:18:14 Functions: 40 44 90.9 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.16