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