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

Generated by: LCOV version 2.0-1