LCOV - code coverage report
Current view: top level - src/backend/access/common - reloptions.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 500 550 90.9 %
Date: 2025-04-01 14:15:22 Functions: 38 41 92.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * reloptions.c
       4             :  *    Core support for relation options (pg_class.reloptions)
       5             :  *
       6             :  * Portions Copyright (c) 1996-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 autovacuum actions will be logged",
     326             :             RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
     327             :             ShareUpdateExclusiveLock
     328             :         },
     329             :         -1, -1, INT_MAX
     330             :     },
     331             :     {
     332             :         {
     333             :             "toast_tuple_target",
     334             :             "Sets the target tuple length at which external columns will be toasted",
     335             :             RELOPT_KIND_HEAP,
     336             :             ShareUpdateExclusiveLock
     337             :         },
     338             :         TOAST_TUPLE_TARGET, 128, TOAST_TUPLE_TARGET_MAIN
     339             :     },
     340             :     {
     341             :         {
     342             :             "pages_per_range",
     343             :             "Number of pages that each page range covers in a BRIN index",
     344             :             RELOPT_KIND_BRIN,
     345             :             AccessExclusiveLock
     346             :         }, 128, 1, 131072
     347             :     },
     348             :     {
     349             :         {
     350             :             "gin_pending_list_limit",
     351             :             "Maximum size of the pending list for this GIN index, in kilobytes.",
     352             :             RELOPT_KIND_GIN,
     353             :             AccessExclusiveLock
     354             :         },
     355             :         -1, 64, MAX_KILOBYTES
     356             :     },
     357             :     {
     358             :         {
     359             :             "effective_io_concurrency",
     360             :             "Number of simultaneous requests that can be handled efficiently by the disk subsystem.",
     361             :             RELOPT_KIND_TABLESPACE,
     362             :             ShareUpdateExclusiveLock
     363             :         },
     364             :         -1, 0, MAX_IO_CONCURRENCY
     365             :     },
     366             :     {
     367             :         {
     368             :             "maintenance_io_concurrency",
     369             :             "Number of simultaneous requests that can be handled efficiently by the disk subsystem for maintenance work.",
     370             :             RELOPT_KIND_TABLESPACE,
     371             :             ShareUpdateExclusiveLock
     372             :         },
     373             :         -1, 0, MAX_IO_CONCURRENCY
     374             :     },
     375             :     {
     376             :         {
     377             :             "parallel_workers",
     378             :             "Number of parallel processes that can be used per executor node for this relation.",
     379             :             RELOPT_KIND_HEAP,
     380             :             ShareUpdateExclusiveLock
     381             :         },
     382             :         -1, 0, 1024
     383             :     },
     384             : 
     385             :     /* list terminator */
     386             :     {{NULL}}
     387             : };
     388             : 
     389             : static relopt_real realRelOpts[] =
     390             : {
     391             :     {
     392             :         {
     393             :             "autovacuum_vacuum_cost_delay",
     394             :             "Vacuum cost delay in milliseconds, for autovacuum",
     395             :             RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
     396             :             ShareUpdateExclusiveLock
     397             :         },
     398             :         -1, 0.0, 100.0
     399             :     },
     400             :     {
     401             :         {
     402             :             "autovacuum_vacuum_scale_factor",
     403             :             "Number of tuple updates or deletes prior to vacuum as a fraction of reltuples",
     404             :             RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
     405             :             ShareUpdateExclusiveLock
     406             :         },
     407             :         -1, 0.0, 100.0
     408             :     },
     409             :     {
     410             :         {
     411             :             "autovacuum_vacuum_insert_scale_factor",
     412             :             "Number of tuple inserts 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_analyze_scale_factor",
     421             :             "Number of tuple inserts, updates or deletes prior to analyze as a fraction of reltuples",
     422             :             RELOPT_KIND_HEAP,
     423             :             ShareUpdateExclusiveLock
     424             :         },
     425             :         -1, 0.0, 100.0
     426             :     },
     427             :     {
     428             :         {
     429             :             "vacuum_max_eager_freeze_failure_rate",
     430             :             "Fraction of pages in a relation vacuum can scan and fail to freeze before disabling eager scanning.",
     431             :             RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
     432             :             ShareUpdateExclusiveLock
     433             :         },
     434             :         -1, 0.0, 1.0
     435             :     },
     436             : 
     437             :     {
     438             :         {
     439             :             "seq_page_cost",
     440             :             "Sets the planner's estimate of the cost of a sequentially fetched disk page.",
     441             :             RELOPT_KIND_TABLESPACE,
     442             :             ShareUpdateExclusiveLock
     443             :         },
     444             :         -1, 0.0, DBL_MAX
     445             :     },
     446             :     {
     447             :         {
     448             :             "random_page_cost",
     449             :             "Sets the planner's estimate of the cost of a nonsequentially fetched disk page.",
     450             :             RELOPT_KIND_TABLESPACE,
     451             :             ShareUpdateExclusiveLock
     452             :         },
     453             :         -1, 0.0, DBL_MAX
     454             :     },
     455             :     {
     456             :         {
     457             :             "n_distinct",
     458             :             "Sets the planner's estimate of the number of distinct values appearing in a column (excluding child relations).",
     459             :             RELOPT_KIND_ATTRIBUTE,
     460             :             ShareUpdateExclusiveLock
     461             :         },
     462             :         0, -1.0, DBL_MAX
     463             :     },
     464             :     {
     465             :         {
     466             :             "n_distinct_inherited",
     467             :             "Sets the planner's estimate of the number of distinct values appearing in a column (including child relations).",
     468             :             RELOPT_KIND_ATTRIBUTE,
     469             :             ShareUpdateExclusiveLock
     470             :         },
     471             :         0, -1.0, DBL_MAX
     472             :     },
     473             :     {
     474             :         {
     475             :             "vacuum_cleanup_index_scale_factor",
     476             :             "Deprecated B-Tree parameter.",
     477             :             RELOPT_KIND_BTREE,
     478             :             ShareUpdateExclusiveLock
     479             :         },
     480             :         -1, 0.0, 1e10
     481             :     },
     482             :     /* list terminator */
     483             :     {{NULL}}
     484             : };
     485             : 
     486             : /* values from StdRdOptIndexCleanup */
     487             : static relopt_enum_elt_def StdRdOptIndexCleanupValues[] =
     488             : {
     489             :     {"auto", STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO},
     490             :     {"on", STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON},
     491             :     {"off", STDRD_OPTION_VACUUM_INDEX_CLEANUP_OFF},
     492             :     {"true", STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON},
     493             :     {"false", STDRD_OPTION_VACUUM_INDEX_CLEANUP_OFF},
     494             :     {"yes", STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON},
     495             :     {"no", STDRD_OPTION_VACUUM_INDEX_CLEANUP_OFF},
     496             :     {"1", STDRD_OPTION_VACUUM_INDEX_CLEANUP_ON},
     497             :     {"0", STDRD_OPTION_VACUUM_INDEX_CLEANUP_OFF},
     498             :     {(const char *) NULL}       /* list terminator */
     499             : };
     500             : 
     501             : /* values from GistOptBufferingMode */
     502             : static relopt_enum_elt_def gistBufferingOptValues[] =
     503             : {
     504             :     {"auto", GIST_OPTION_BUFFERING_AUTO},
     505             :     {"on", GIST_OPTION_BUFFERING_ON},
     506             :     {"off", GIST_OPTION_BUFFERING_OFF},
     507             :     {(const char *) NULL}       /* list terminator */
     508             : };
     509             : 
     510             : /* values from ViewOptCheckOption */
     511             : static relopt_enum_elt_def viewCheckOptValues[] =
     512             : {
     513             :     /* no value for NOT_SET */
     514             :     {"local", VIEW_OPTION_CHECK_OPTION_LOCAL},
     515             :     {"cascaded", VIEW_OPTION_CHECK_OPTION_CASCADED},
     516             :     {(const char *) NULL}       /* list terminator */
     517             : };
     518             : 
     519             : static relopt_enum enumRelOpts[] =
     520             : {
     521             :     {
     522             :         {
     523             :             "vacuum_index_cleanup",
     524             :             "Controls index vacuuming and index cleanup",
     525             :             RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
     526             :             ShareUpdateExclusiveLock
     527             :         },
     528             :         StdRdOptIndexCleanupValues,
     529             :         STDRD_OPTION_VACUUM_INDEX_CLEANUP_AUTO,
     530             :         gettext_noop("Valid values are \"on\", \"off\", and \"auto\".")
     531             :     },
     532             :     {
     533             :         {
     534             :             "buffering",
     535             :             "Enables buffering build for this GiST index",
     536             :             RELOPT_KIND_GIST,
     537             :             AccessExclusiveLock
     538             :         },
     539             :         gistBufferingOptValues,
     540             :         GIST_OPTION_BUFFERING_AUTO,
     541             :         gettext_noop("Valid values are \"on\", \"off\", and \"auto\".")
     542             :     },
     543             :     {
     544             :         {
     545             :             "check_option",
     546             :             "View has WITH CHECK OPTION defined (local or cascaded).",
     547             :             RELOPT_KIND_VIEW,
     548             :             AccessExclusiveLock
     549             :         },
     550             :         viewCheckOptValues,
     551             :         VIEW_OPTION_CHECK_OPTION_NOT_SET,
     552             :         gettext_noop("Valid values are \"local\" and \"cascaded\".")
     553             :     },
     554             :     /* list terminator */
     555             :     {{NULL}}
     556             : };
     557             : 
     558             : static relopt_string stringRelOpts[] =
     559             : {
     560             :     /* list terminator */
     561             :     {{NULL}}
     562             : };
     563             : 
     564             : static relopt_gen **relOpts = NULL;
     565             : static bits32 last_assigned_kind = RELOPT_KIND_LAST_DEFAULT;
     566             : 
     567             : static int  num_custom_options = 0;
     568             : static relopt_gen **custom_options = NULL;
     569             : static bool need_initialization = true;
     570             : 
     571             : static void initialize_reloptions(void);
     572             : static void parse_one_reloption(relopt_value *option, char *text_str,
     573             :                                 int text_len, bool validate);
     574             : 
     575             : /*
     576             :  * Get the length of a string reloption (either default or the user-defined
     577             :  * value).  This is used for allocation purposes when building a set of
     578             :  * relation options.
     579             :  */
     580             : #define GET_STRING_RELOPTION_LEN(option) \
     581             :     ((option).isset ? strlen((option).values.string_val) : \
     582             :      ((relopt_string *) (option).gen)->default_len)
     583             : 
     584             : /*
     585             :  * initialize_reloptions
     586             :  *      initialization routine, must be called before parsing
     587             :  *
     588             :  * Initialize the relOpts array and fill each variable's type and name length.
     589             :  */
     590             : static void
     591        7860 : initialize_reloptions(void)
     592             : {
     593             :     int         i;
     594             :     int         j;
     595             : 
     596        7860 :     j = 0;
     597       70740 :     for (i = 0; boolRelOpts[i].gen.name; i++)
     598             :     {
     599             :         Assert(DoLockModesConflict(boolRelOpts[i].gen.lockmode,
     600             :                                    boolRelOpts[i].gen.lockmode));
     601       62880 :         j++;
     602             :     }
     603      188640 :     for (i = 0; intRelOpts[i].gen.name; i++)
     604             :     {
     605             :         Assert(DoLockModesConflict(intRelOpts[i].gen.lockmode,
     606             :                                    intRelOpts[i].gen.lockmode));
     607      180780 :         j++;
     608             :     }
     609       86460 :     for (i = 0; realRelOpts[i].gen.name; i++)
     610             :     {
     611             :         Assert(DoLockModesConflict(realRelOpts[i].gen.lockmode,
     612             :                                    realRelOpts[i].gen.lockmode));
     613       78600 :         j++;
     614             :     }
     615       31440 :     for (i = 0; enumRelOpts[i].gen.name; i++)
     616             :     {
     617             :         Assert(DoLockModesConflict(enumRelOpts[i].gen.lockmode,
     618             :                                    enumRelOpts[i].gen.lockmode));
     619       23580 :         j++;
     620             :     }
     621        7860 :     for (i = 0; stringRelOpts[i].gen.name; i++)
     622             :     {
     623             :         Assert(DoLockModesConflict(stringRelOpts[i].gen.lockmode,
     624             :                                    stringRelOpts[i].gen.lockmode));
     625           0 :         j++;
     626             :     }
     627        7860 :     j += num_custom_options;
     628             : 
     629        7860 :     if (relOpts)
     630           0 :         pfree(relOpts);
     631       15720 :     relOpts = MemoryContextAlloc(TopMemoryContext,
     632        7860 :                                  (j + 1) * sizeof(relopt_gen *));
     633             : 
     634        7860 :     j = 0;
     635       70740 :     for (i = 0; boolRelOpts[i].gen.name; i++)
     636             :     {
     637       62880 :         relOpts[j] = &boolRelOpts[i].gen;
     638       62880 :         relOpts[j]->type = RELOPT_TYPE_BOOL;
     639       62880 :         relOpts[j]->namelen = strlen(relOpts[j]->name);
     640       62880 :         j++;
     641             :     }
     642             : 
     643      188640 :     for (i = 0; intRelOpts[i].gen.name; i++)
     644             :     {
     645      180780 :         relOpts[j] = &intRelOpts[i].gen;
     646      180780 :         relOpts[j]->type = RELOPT_TYPE_INT;
     647      180780 :         relOpts[j]->namelen = strlen(relOpts[j]->name);
     648      180780 :         j++;
     649             :     }
     650             : 
     651       86460 :     for (i = 0; realRelOpts[i].gen.name; i++)
     652             :     {
     653       78600 :         relOpts[j] = &realRelOpts[i].gen;
     654       78600 :         relOpts[j]->type = RELOPT_TYPE_REAL;
     655       78600 :         relOpts[j]->namelen = strlen(relOpts[j]->name);
     656       78600 :         j++;
     657             :     }
     658             : 
     659       31440 :     for (i = 0; enumRelOpts[i].gen.name; i++)
     660             :     {
     661       23580 :         relOpts[j] = &enumRelOpts[i].gen;
     662       23580 :         relOpts[j]->type = RELOPT_TYPE_ENUM;
     663       23580 :         relOpts[j]->namelen = strlen(relOpts[j]->name);
     664       23580 :         j++;
     665             :     }
     666             : 
     667        7860 :     for (i = 0; stringRelOpts[i].gen.name; i++)
     668             :     {
     669           0 :         relOpts[j] = &stringRelOpts[i].gen;
     670           0 :         relOpts[j]->type = RELOPT_TYPE_STRING;
     671           0 :         relOpts[j]->namelen = strlen(relOpts[j]->name);
     672           0 :         j++;
     673             :     }
     674             : 
     675       14076 :     for (i = 0; i < num_custom_options; i++)
     676             :     {
     677        6216 :         relOpts[j] = custom_options[i];
     678        6216 :         j++;
     679             :     }
     680             : 
     681             :     /* add a list terminator */
     682        7860 :     relOpts[j] = NULL;
     683             : 
     684             :     /* flag the work is complete */
     685        7860 :     need_initialization = false;
     686        7860 : }
     687             : 
     688             : /*
     689             :  * add_reloption_kind
     690             :  *      Create a new relopt_kind value, to be used in custom reloptions by
     691             :  *      user-defined AMs.
     692             :  */
     693             : relopt_kind
     694         192 : add_reloption_kind(void)
     695             : {
     696             :     /* don't hand out the last bit so that the enum's behavior is portable */
     697         192 :     if (last_assigned_kind >= RELOPT_KIND_MAX)
     698           0 :         ereport(ERROR,
     699             :                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     700             :                  errmsg("user-defined relation parameter types limit exceeded")));
     701         192 :     last_assigned_kind <<= 1;
     702         192 :     return (relopt_kind) last_assigned_kind;
     703             : }
     704             : 
     705             : /*
     706             :  * add_reloption
     707             :  *      Add an already-created custom reloption to the list, and recompute the
     708             :  *      main parser table.
     709             :  */
     710             : static void
     711        6282 : add_reloption(relopt_gen *newoption)
     712             : {
     713             :     static int  max_custom_options = 0;
     714             : 
     715        6282 :     if (num_custom_options >= max_custom_options)
     716             :     {
     717             :         MemoryContext oldcxt;
     718             : 
     719         762 :         oldcxt = MemoryContextSwitchTo(TopMemoryContext);
     720             : 
     721         762 :         if (max_custom_options == 0)
     722             :         {
     723         192 :             max_custom_options = 8;
     724         192 :             custom_options = palloc(max_custom_options * sizeof(relopt_gen *));
     725             :         }
     726             :         else
     727             :         {
     728         570 :             max_custom_options *= 2;
     729         570 :             custom_options = repalloc(custom_options,
     730             :                                       max_custom_options * sizeof(relopt_gen *));
     731             :         }
     732         762 :         MemoryContextSwitchTo(oldcxt);
     733             :     }
     734        6282 :     custom_options[num_custom_options++] = newoption;
     735             : 
     736        6282 :     need_initialization = true;
     737        6282 : }
     738             : 
     739             : /*
     740             :  * init_local_reloptions
     741             :  *      Initialize local reloptions that will parsed into bytea structure of
     742             :  *      'relopt_struct_size'.
     743             :  */
     744             : void
     745        5076 : init_local_reloptions(local_relopts *relopts, Size relopt_struct_size)
     746             : {
     747        5076 :     relopts->options = NIL;
     748        5076 :     relopts->validators = NIL;
     749        5076 :     relopts->relopt_struct_size = relopt_struct_size;
     750        5076 : }
     751             : 
     752             : /*
     753             :  * register_reloptions_validator
     754             :  *      Register custom validation callback that will be called at the end of
     755             :  *      build_local_reloptions().
     756             :  */
     757             : void
     758          28 : register_reloptions_validator(local_relopts *relopts, relopts_validator validator)
     759             : {
     760          28 :     relopts->validators = lappend(relopts->validators, validator);
     761          28 : }
     762             : 
     763             : /*
     764             :  * add_local_reloption
     765             :  *      Add an already-created custom reloption to the local list.
     766             :  */
     767             : static void
     768        3288 : add_local_reloption(local_relopts *relopts, relopt_gen *newoption, int offset)
     769             : {
     770        3288 :     local_relopt *opt = palloc(sizeof(*opt));
     771             : 
     772             :     Assert(offset < relopts->relopt_struct_size);
     773             : 
     774        3288 :     opt->option = newoption;
     775        3288 :     opt->offset = offset;
     776             : 
     777        3288 :     relopts->options = lappend(relopts->options, opt);
     778        3288 : }
     779             : 
     780             : /*
     781             :  * allocate_reloption
     782             :  *      Allocate a new reloption and initialize the type-agnostic fields
     783             :  *      (for types other than string)
     784             :  */
     785             : static relopt_gen *
     786        9570 : allocate_reloption(bits32 kinds, int type, const char *name, const char *desc,
     787             :                    LOCKMODE lockmode)
     788             : {
     789             :     MemoryContext oldcxt;
     790             :     size_t      size;
     791             :     relopt_gen *newoption;
     792             : 
     793        9570 :     if (kinds != RELOPT_KIND_LOCAL)
     794        6282 :         oldcxt = MemoryContextSwitchTo(TopMemoryContext);
     795             :     else
     796        3288 :         oldcxt = NULL;
     797             : 
     798        9570 :     switch (type)
     799             :     {
     800           2 :         case RELOPT_TYPE_BOOL:
     801           2 :             size = sizeof(relopt_bool);
     802           2 :             break;
     803        8060 :         case RELOPT_TYPE_INT:
     804        8060 :             size = sizeof(relopt_int);
     805        8060 :             break;
     806        1502 :         case RELOPT_TYPE_REAL:
     807        1502 :             size = sizeof(relopt_real);
     808        1502 :             break;
     809           2 :         case RELOPT_TYPE_ENUM:
     810           2 :             size = sizeof(relopt_enum);
     811           2 :             break;
     812           4 :         case RELOPT_TYPE_STRING:
     813           4 :             size = sizeof(relopt_string);
     814           4 :             break;
     815           0 :         default:
     816           0 :             elog(ERROR, "unsupported reloption type %d", type);
     817             :             return NULL;        /* keep compiler quiet */
     818             :     }
     819             : 
     820        9570 :     newoption = palloc(size);
     821             : 
     822        9570 :     newoption->name = pstrdup(name);
     823        9570 :     if (desc)
     824        9568 :         newoption->desc = pstrdup(desc);
     825             :     else
     826           2 :         newoption->desc = NULL;
     827        9570 :     newoption->kinds = kinds;
     828        9570 :     newoption->namelen = strlen(name);
     829        9570 :     newoption->type = type;
     830        9570 :     newoption->lockmode = lockmode;
     831             : 
     832        9570 :     if (oldcxt != NULL)
     833        6282 :         MemoryContextSwitchTo(oldcxt);
     834             : 
     835        9570 :     return newoption;
     836             : }
     837             : 
     838             : /*
     839             :  * init_bool_reloption
     840             :  *      Allocate and initialize a new boolean reloption
     841             :  */
     842             : static relopt_bool *
     843           2 : init_bool_reloption(bits32 kinds, const char *name, const char *desc,
     844             :                     bool default_val, LOCKMODE lockmode)
     845             : {
     846             :     relopt_bool *newoption;
     847             : 
     848           2 :     newoption = (relopt_bool *) allocate_reloption(kinds, RELOPT_TYPE_BOOL,
     849             :                                                    name, desc, lockmode);
     850           2 :     newoption->default_val = default_val;
     851             : 
     852           2 :     return newoption;
     853             : }
     854             : 
     855             : /*
     856             :  * add_bool_reloption
     857             :  *      Add a new boolean reloption
     858             :  */
     859             : void
     860           2 : add_bool_reloption(bits32 kinds, const char *name, const char *desc,
     861             :                    bool default_val, LOCKMODE lockmode)
     862             : {
     863           2 :     relopt_bool *newoption = init_bool_reloption(kinds, name, desc,
     864             :                                                  default_val, lockmode);
     865             : 
     866           2 :     add_reloption((relopt_gen *) newoption);
     867           2 : }
     868             : 
     869             : /*
     870             :  * add_local_bool_reloption
     871             :  *      Add a new boolean local reloption
     872             :  *
     873             :  * 'offset' is offset of bool-typed field.
     874             :  */
     875             : void
     876           0 : add_local_bool_reloption(local_relopts *relopts, const char *name,
     877             :                          const char *desc, bool default_val, int offset)
     878             : {
     879           0 :     relopt_bool *newoption = init_bool_reloption(RELOPT_KIND_LOCAL,
     880             :                                                  name, desc,
     881             :                                                  default_val, 0);
     882             : 
     883           0 :     add_local_reloption(relopts, (relopt_gen *) newoption, offset);
     884           0 : }
     885             : 
     886             : 
     887             : /*
     888             :  * init_real_reloption
     889             :  *      Allocate and initialize a new integer reloption
     890             :  */
     891             : static relopt_int *
     892        8060 : init_int_reloption(bits32 kinds, const char *name, const char *desc,
     893             :                    int default_val, int min_val, int max_val,
     894             :                    LOCKMODE lockmode)
     895             : {
     896             :     relopt_int *newoption;
     897             : 
     898        8060 :     newoption = (relopt_int *) allocate_reloption(kinds, RELOPT_TYPE_INT,
     899             :                                                   name, desc, lockmode);
     900        8060 :     newoption->default_val = default_val;
     901        8060 :     newoption->min = min_val;
     902        8060 :     newoption->max = max_val;
     903             : 
     904        8060 :     return newoption;
     905             : }
     906             : 
     907             : /*
     908             :  * add_int_reloption
     909             :  *      Add a new integer reloption
     910             :  */
     911             : void
     912        6272 : add_int_reloption(bits32 kinds, const char *name, const char *desc, int default_val,
     913             :                   int min_val, int max_val, LOCKMODE lockmode)
     914             : {
     915        6272 :     relopt_int *newoption = init_int_reloption(kinds, name, desc,
     916             :                                                default_val, min_val,
     917             :                                                max_val, lockmode);
     918             : 
     919        6272 :     add_reloption((relopt_gen *) newoption);
     920        6272 : }
     921             : 
     922             : /*
     923             :  * add_local_int_reloption
     924             :  *      Add a new local integer reloption
     925             :  *
     926             :  * 'offset' is offset of int-typed field.
     927             :  */
     928             : void
     929        1788 : add_local_int_reloption(local_relopts *relopts, const char *name,
     930             :                         const char *desc, int default_val, int min_val,
     931             :                         int max_val, int offset)
     932             : {
     933        1788 :     relopt_int *newoption = init_int_reloption(RELOPT_KIND_LOCAL,
     934             :                                                name, desc, default_val,
     935             :                                                min_val, max_val, 0);
     936             : 
     937        1788 :     add_local_reloption(relopts, (relopt_gen *) newoption, offset);
     938        1788 : }
     939             : 
     940             : /*
     941             :  * init_real_reloption
     942             :  *      Allocate and initialize a new real reloption
     943             :  */
     944             : static relopt_real *
     945        1502 : init_real_reloption(bits32 kinds, const char *name, const char *desc,
     946             :                     double default_val, double min_val, double max_val,
     947             :                     LOCKMODE lockmode)
     948             : {
     949             :     relopt_real *newoption;
     950             : 
     951        1502 :     newoption = (relopt_real *) allocate_reloption(kinds, RELOPT_TYPE_REAL,
     952             :                                                    name, desc, lockmode);
     953        1502 :     newoption->default_val = default_val;
     954        1502 :     newoption->min = min_val;
     955        1502 :     newoption->max = max_val;
     956             : 
     957        1502 :     return newoption;
     958             : }
     959             : 
     960             : /*
     961             :  * add_real_reloption
     962             :  *      Add a new float reloption
     963             :  */
     964             : void
     965           2 : add_real_reloption(bits32 kinds, const char *name, const char *desc,
     966             :                    double default_val, double min_val, double max_val,
     967             :                    LOCKMODE lockmode)
     968             : {
     969           2 :     relopt_real *newoption = init_real_reloption(kinds, name, desc,
     970             :                                                  default_val, min_val,
     971             :                                                  max_val, lockmode);
     972             : 
     973           2 :     add_reloption((relopt_gen *) newoption);
     974           2 : }
     975             : 
     976             : /*
     977             :  * add_local_real_reloption
     978             :  *      Add a new local float reloption
     979             :  *
     980             :  * 'offset' is offset of double-typed field.
     981             :  */
     982             : void
     983        1500 : add_local_real_reloption(local_relopts *relopts, const char *name,
     984             :                          const char *desc, double default_val,
     985             :                          double min_val, double max_val, int offset)
     986             : {
     987        1500 :     relopt_real *newoption = init_real_reloption(RELOPT_KIND_LOCAL,
     988             :                                                  name, desc,
     989             :                                                  default_val, min_val,
     990             :                                                  max_val, 0);
     991             : 
     992        1500 :     add_local_reloption(relopts, (relopt_gen *) newoption, offset);
     993        1500 : }
     994             : 
     995             : /*
     996             :  * init_enum_reloption
     997             :  *      Allocate and initialize a new enum reloption
     998             :  */
     999             : static relopt_enum *
    1000           2 : init_enum_reloption(bits32 kinds, const char *name, const char *desc,
    1001             :                     relopt_enum_elt_def *members, int default_val,
    1002             :                     const char *detailmsg, LOCKMODE lockmode)
    1003             : {
    1004             :     relopt_enum *newoption;
    1005             : 
    1006           2 :     newoption = (relopt_enum *) allocate_reloption(kinds, RELOPT_TYPE_ENUM,
    1007             :                                                    name, desc, lockmode);
    1008           2 :     newoption->members = members;
    1009           2 :     newoption->default_val = default_val;
    1010           2 :     newoption->detailmsg = detailmsg;
    1011             : 
    1012           2 :     return newoption;
    1013             : }
    1014             : 
    1015             : 
    1016             : /*
    1017             :  * add_enum_reloption
    1018             :  *      Add a new enum reloption
    1019             :  *
    1020             :  * The members array must have a terminating NULL entry.
    1021             :  *
    1022             :  * The detailmsg is shown when unsupported values are passed, and has this
    1023             :  * form:   "Valid values are \"foo\", \"bar\", and \"bar\"."
    1024             :  *
    1025             :  * The members array and detailmsg are not copied -- caller must ensure that
    1026             :  * they are valid throughout the life of the process.
    1027             :  */
    1028             : void
    1029           2 : add_enum_reloption(bits32 kinds, const char *name, const char *desc,
    1030             :                    relopt_enum_elt_def *members, int default_val,
    1031             :                    const char *detailmsg, LOCKMODE lockmode)
    1032             : {
    1033           2 :     relopt_enum *newoption = init_enum_reloption(kinds, name, desc,
    1034             :                                                  members, default_val,
    1035             :                                                  detailmsg, lockmode);
    1036             : 
    1037           2 :     add_reloption((relopt_gen *) newoption);
    1038           2 : }
    1039             : 
    1040             : /*
    1041             :  * add_local_enum_reloption
    1042             :  *      Add a new local enum reloption
    1043             :  *
    1044             :  * 'offset' is offset of int-typed field.
    1045             :  */
    1046             : void
    1047           0 : add_local_enum_reloption(local_relopts *relopts, const char *name,
    1048             :                          const char *desc, relopt_enum_elt_def *members,
    1049             :                          int default_val, const char *detailmsg, int offset)
    1050             : {
    1051           0 :     relopt_enum *newoption = init_enum_reloption(RELOPT_KIND_LOCAL,
    1052             :                                                  name, desc,
    1053             :                                                  members, default_val,
    1054             :                                                  detailmsg, 0);
    1055             : 
    1056           0 :     add_local_reloption(relopts, (relopt_gen *) newoption, offset);
    1057           0 : }
    1058             : 
    1059             : /*
    1060             :  * init_string_reloption
    1061             :  *      Allocate and initialize a new string reloption
    1062             :  */
    1063             : static relopt_string *
    1064           4 : init_string_reloption(bits32 kinds, const char *name, const char *desc,
    1065             :                       const char *default_val,
    1066             :                       validate_string_relopt validator,
    1067             :                       fill_string_relopt filler,
    1068             :                       LOCKMODE lockmode)
    1069             : {
    1070             :     relopt_string *newoption;
    1071             : 
    1072             :     /* make sure the validator/default combination is sane */
    1073           4 :     if (validator)
    1074           4 :         (validator) (default_val);
    1075             : 
    1076           4 :     newoption = (relopt_string *) allocate_reloption(kinds, RELOPT_TYPE_STRING,
    1077             :                                                      name, desc, lockmode);
    1078           4 :     newoption->validate_cb = validator;
    1079           4 :     newoption->fill_cb = filler;
    1080           4 :     if (default_val)
    1081             :     {
    1082           2 :         if (kinds == RELOPT_KIND_LOCAL)
    1083           0 :             newoption->default_val = strdup(default_val);
    1084             :         else
    1085           2 :             newoption->default_val = MemoryContextStrdup(TopMemoryContext, default_val);
    1086           2 :         newoption->default_len = strlen(default_val);
    1087           2 :         newoption->default_isnull = false;
    1088             :     }
    1089             :     else
    1090             :     {
    1091           2 :         newoption->default_val = "";
    1092           2 :         newoption->default_len = 0;
    1093           2 :         newoption->default_isnull = true;
    1094             :     }
    1095             : 
    1096           4 :     return newoption;
    1097             : }
    1098             : 
    1099             : /*
    1100             :  * add_string_reloption
    1101             :  *      Add a new string reloption
    1102             :  *
    1103             :  * "validator" is an optional function pointer that can be used to test the
    1104             :  * validity of the values.  It must elog(ERROR) when the argument string is
    1105             :  * not acceptable for the variable.  Note that the default value must pass
    1106             :  * the validation.
    1107             :  */
    1108             : void
    1109           4 : add_string_reloption(bits32 kinds, const char *name, const char *desc,
    1110             :                      const char *default_val, validate_string_relopt validator,
    1111             :                      LOCKMODE lockmode)
    1112             : {
    1113           4 :     relopt_string *newoption = init_string_reloption(kinds, name, desc,
    1114             :                                                      default_val,
    1115             :                                                      validator, NULL,
    1116             :                                                      lockmode);
    1117             : 
    1118           4 :     add_reloption((relopt_gen *) newoption);
    1119           4 : }
    1120             : 
    1121             : /*
    1122             :  * add_local_string_reloption
    1123             :  *      Add a new local string reloption
    1124             :  *
    1125             :  * 'offset' is offset of int-typed field that will store offset of string value
    1126             :  * in the resulting bytea structure.
    1127             :  */
    1128             : void
    1129           0 : add_local_string_reloption(local_relopts *relopts, const char *name,
    1130             :                            const char *desc, const char *default_val,
    1131             :                            validate_string_relopt validator,
    1132             :                            fill_string_relopt filler, int offset)
    1133             : {
    1134           0 :     relopt_string *newoption = init_string_reloption(RELOPT_KIND_LOCAL,
    1135             :                                                      name, desc,
    1136             :                                                      default_val,
    1137             :                                                      validator, filler,
    1138             :                                                      0);
    1139             : 
    1140           0 :     add_local_reloption(relopts, (relopt_gen *) newoption, offset);
    1141           0 : }
    1142             : 
    1143             : /*
    1144             :  * Transform a relation options list (list of DefElem) into the text array
    1145             :  * format that is kept in pg_class.reloptions, including only those options
    1146             :  * that are in the passed namespace.  The output values do not include the
    1147             :  * namespace.
    1148             :  *
    1149             :  * This is used for three cases: CREATE TABLE/INDEX, ALTER TABLE SET, and
    1150             :  * ALTER TABLE RESET.  In the ALTER cases, oldOptions is the existing
    1151             :  * reloptions value (possibly NULL), and we replace or remove entries
    1152             :  * as needed.
    1153             :  *
    1154             :  * If acceptOidsOff is true, then we allow oids = false, but throw error when
    1155             :  * on. This is solely needed for backwards compatibility.
    1156             :  *
    1157             :  * Note that this is not responsible for determining whether the options
    1158             :  * are valid, but it does check that namespaces for all the options given are
    1159             :  * listed in validnsps.  The NULL namespace is always valid and need not be
    1160             :  * explicitly listed.  Passing a NULL pointer means that only the NULL
    1161             :  * namespace is valid.
    1162             :  *
    1163             :  * Both oldOptions and the result are text arrays (or NULL for "default"),
    1164             :  * but we declare them as Datums to avoid including array.h in reloptions.h.
    1165             :  */
    1166             : Datum
    1167      127056 : transformRelOptions(Datum oldOptions, List *defList, const char *namspace,
    1168             :                     const char *const validnsps[], bool acceptOidsOff, bool isReset)
    1169             : {
    1170             :     Datum       result;
    1171             :     ArrayBuildState *astate;
    1172             :     ListCell   *cell;
    1173             : 
    1174             :     /* no change if empty list */
    1175      127056 :     if (defList == NIL)
    1176      123536 :         return oldOptions;
    1177             : 
    1178             :     /* We build new array using accumArrayResult */
    1179        3520 :     astate = NULL;
    1180             : 
    1181             :     /* Copy any oldOptions that aren't to be replaced */
    1182        3520 :     if (PointerIsValid(DatumGetPointer(oldOptions)))
    1183             :     {
    1184         348 :         ArrayType  *array = DatumGetArrayTypeP(oldOptions);
    1185             :         Datum      *oldoptions;
    1186             :         int         noldoptions;
    1187             :         int         i;
    1188             : 
    1189         348 :         deconstruct_array_builtin(array, TEXTOID, &oldoptions, NULL, &noldoptions);
    1190             : 
    1191         892 :         for (i = 0; i < noldoptions; i++)
    1192             :         {
    1193         544 :             char       *text_str = VARDATA(oldoptions[i]);
    1194         544 :             int         text_len = VARSIZE(oldoptions[i]) - VARHDRSZ;
    1195             : 
    1196             :             /* Search for a match in defList */
    1197         808 :             foreach(cell, defList)
    1198             :             {
    1199         592 :                 DefElem    *def = (DefElem *) lfirst(cell);
    1200             :                 int         kw_len;
    1201             : 
    1202             :                 /* ignore if not in the same namespace */
    1203         592 :                 if (namspace == NULL)
    1204             :                 {
    1205         544 :                     if (def->defnamespace != NULL)
    1206           0 :                         continue;
    1207             :                 }
    1208          48 :                 else if (def->defnamespace == NULL)
    1209          30 :                     continue;
    1210          18 :                 else if (strcmp(def->defnamespace, namspace) != 0)
    1211           0 :                     continue;
    1212             : 
    1213         562 :                 kw_len = strlen(def->defname);
    1214         562 :                 if (text_len > kw_len && text_str[kw_len] == '=' &&
    1215         350 :                     strncmp(text_str, def->defname, kw_len) == 0)
    1216         328 :                     break;
    1217             :             }
    1218         544 :             if (!cell)
    1219             :             {
    1220             :                 /* No match, so keep old option */
    1221         216 :                 astate = accumArrayResult(astate, oldoptions[i],
    1222             :                                           false, TEXTOID,
    1223             :                                           CurrentMemoryContext);
    1224             :             }
    1225             :         }
    1226             :     }
    1227             : 
    1228             :     /*
    1229             :      * If CREATE/SET, add new options to array; if RESET, just check that the
    1230             :      * user didn't say RESET (option=val).  (Must do this because the grammar
    1231             :      * doesn't enforce it.)
    1232             :      */
    1233        7362 :     foreach(cell, defList)
    1234             :     {
    1235        3878 :         DefElem    *def = (DefElem *) lfirst(cell);
    1236             : 
    1237        3878 :         if (isReset)
    1238             :         {
    1239         262 :             if (def->arg != NULL)
    1240          12 :                 ereport(ERROR,
    1241             :                         (errcode(ERRCODE_SYNTAX_ERROR),
    1242             :                          errmsg("RESET must not include values for parameters")));
    1243             :         }
    1244             :         else
    1245             :         {
    1246             :             text       *t;
    1247             :             const char *value;
    1248             :             Size        len;
    1249             : 
    1250             :             /*
    1251             :              * Error out if the namespace is not valid.  A NULL namespace is
    1252             :              * always valid.
    1253             :              */
    1254        3616 :             if (def->defnamespace != NULL)
    1255             :             {
    1256         112 :                 bool        valid = false;
    1257             :                 int         i;
    1258             : 
    1259         112 :                 if (validnsps)
    1260             :                 {
    1261         112 :                     for (i = 0; validnsps[i]; i++)
    1262             :                     {
    1263         106 :                         if (strcmp(def->defnamespace, validnsps[i]) == 0)
    1264             :                         {
    1265         100 :                             valid = true;
    1266         100 :                             break;
    1267             :                         }
    1268             :                     }
    1269             :                 }
    1270             : 
    1271         112 :                 if (!valid)
    1272          12 :                     ereport(ERROR,
    1273             :                             (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1274             :                              errmsg("unrecognized parameter namespace \"%s\"",
    1275             :                                     def->defnamespace)));
    1276             :             }
    1277             : 
    1278             :             /* ignore if not in the same namespace */
    1279        3604 :             if (namspace == NULL)
    1280             :             {
    1281        2722 :                 if (def->defnamespace != NULL)
    1282          50 :                     continue;
    1283             :             }
    1284         882 :             else if (def->defnamespace == NULL)
    1285         832 :                 continue;
    1286          50 :             else if (strcmp(def->defnamespace, namspace) != 0)
    1287           0 :                 continue;
    1288             : 
    1289             :             /*
    1290             :              * Flatten the DefElem into a text string like "name=arg". If we
    1291             :              * have just "name", assume "name=true" is meant.  Note: the
    1292             :              * namespace is not output.
    1293             :              */
    1294        2722 :             if (def->arg != NULL)
    1295        2350 :                 value = defGetString(def);
    1296             :             else
    1297         372 :                 value = "true";
    1298             : 
    1299             :             /*
    1300             :              * This is not a great place for this test, but there's no other
    1301             :              * convenient place to filter the option out. As WITH (oids =
    1302             :              * false) will be removed someday, this seems like an acceptable
    1303             :              * amount of ugly.
    1304             :              */
    1305        2722 :             if (acceptOidsOff && def->defnamespace == NULL &&
    1306        1340 :                 strcmp(def->defname, "oids") == 0)
    1307             :             {
    1308          18 :                 if (defGetBoolean(def))
    1309          12 :                     ereport(ERROR,
    1310             :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1311             :                              errmsg("tables declared WITH OIDS are not supported")));
    1312             :                 /* skip over option, reloptions machinery doesn't know it */
    1313           6 :                 continue;
    1314             :             }
    1315             : 
    1316        2704 :             len = VARHDRSZ + strlen(def->defname) + 1 + strlen(value);
    1317             :             /* +1 leaves room for sprintf's trailing null */
    1318        2704 :             t = (text *) palloc(len + 1);
    1319        2704 :             SET_VARSIZE(t, len);
    1320        2704 :             sprintf(VARDATA(t), "%s=%s", def->defname, value);
    1321             : 
    1322        2704 :             astate = accumArrayResult(astate, PointerGetDatum(t),
    1323             :                                       false, TEXTOID,
    1324             :                                       CurrentMemoryContext);
    1325             :         }
    1326             :     }
    1327             : 
    1328        3484 :     if (astate)
    1329        2562 :         result = makeArrayResult(astate, CurrentMemoryContext);
    1330             :     else
    1331         922 :         result = (Datum) 0;
    1332             : 
    1333        3484 :     return result;
    1334             : }
    1335             : 
    1336             : 
    1337             : /*
    1338             :  * Convert the text-array format of reloptions into a List of DefElem.
    1339             :  * This is the inverse of transformRelOptions().
    1340             :  */
    1341             : List *
    1342       30834 : untransformRelOptions(Datum options)
    1343             : {
    1344       30834 :     List       *result = NIL;
    1345             :     ArrayType  *array;
    1346             :     Datum      *optiondatums;
    1347             :     int         noptions;
    1348             :     int         i;
    1349             : 
    1350             :     /* Nothing to do if no options */
    1351       30834 :     if (!PointerIsValid(DatumGetPointer(options)))
    1352        4514 :         return result;
    1353             : 
    1354       26320 :     array = DatumGetArrayTypeP(options);
    1355             : 
    1356       26320 :     deconstruct_array_builtin(array, TEXTOID, &optiondatums, NULL, &noptions);
    1357             : 
    1358       78912 :     for (i = 0; i < noptions; i++)
    1359             :     {
    1360             :         char       *s;
    1361             :         char       *p;
    1362       52592 :         Node       *val = NULL;
    1363             : 
    1364       52592 :         s = TextDatumGetCString(optiondatums[i]);
    1365       52592 :         p = strchr(s, '=');
    1366       52592 :         if (p)
    1367             :         {
    1368       52592 :             *p++ = '\0';
    1369       52592 :             val = (Node *) makeString(p);
    1370             :         }
    1371       52592 :         result = lappend(result, makeDefElem(s, val, -1));
    1372             :     }
    1373             : 
    1374       26320 :     return result;
    1375             : }
    1376             : 
    1377             : /*
    1378             :  * Extract and parse reloptions from a pg_class tuple.
    1379             :  *
    1380             :  * This is a low-level routine, expected to be used by relcache code and
    1381             :  * callers that do not have a table's relcache entry (e.g. autovacuum).  For
    1382             :  * other uses, consider grabbing the rd_options pointer from the relcache entry
    1383             :  * instead.
    1384             :  *
    1385             :  * tupdesc is pg_class' tuple descriptor.  amoptions is a pointer to the index
    1386             :  * AM's options parser function in the case of a tuple corresponding to an
    1387             :  * index, or NULL otherwise.
    1388             :  */
    1389             : bytea *
    1390     1936558 : extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
    1391             :                   amoptions_function amoptions)
    1392             : {
    1393             :     bytea      *options;
    1394             :     bool        isnull;
    1395             :     Datum       datum;
    1396             :     Form_pg_class classForm;
    1397             : 
    1398     1936558 :     datum = fastgetattr(tuple,
    1399             :                         Anum_pg_class_reloptions,
    1400             :                         tupdesc,
    1401             :                         &isnull);
    1402     1936558 :     if (isnull)
    1403     1916152 :         return NULL;
    1404             : 
    1405       20406 :     classForm = (Form_pg_class) GETSTRUCT(tuple);
    1406             : 
    1407             :     /* Parse into appropriate format; don't error out here */
    1408       20406 :     switch (classForm->relkind)
    1409             :     {
    1410       15698 :         case RELKIND_RELATION:
    1411             :         case RELKIND_TOASTVALUE:
    1412             :         case RELKIND_MATVIEW:
    1413       15698 :             options = heap_reloptions(classForm->relkind, datum, false);
    1414       15698 :             break;
    1415           0 :         case RELKIND_PARTITIONED_TABLE:
    1416           0 :             options = partitioned_table_reloptions(datum, false);
    1417           0 :             break;
    1418        2280 :         case RELKIND_VIEW:
    1419        2280 :             options = view_reloptions(datum, false);
    1420        2280 :             break;
    1421        2428 :         case RELKIND_INDEX:
    1422             :         case RELKIND_PARTITIONED_INDEX:
    1423        2428 :             options = index_reloptions(amoptions, datum, false);
    1424        2428 :             break;
    1425           0 :         case RELKIND_FOREIGN_TABLE:
    1426           0 :             options = NULL;
    1427           0 :             break;
    1428           0 :         default:
    1429             :             Assert(false);      /* can't get here */
    1430           0 :             options = NULL;     /* keep compiler quiet */
    1431           0 :             break;
    1432             :     }
    1433             : 
    1434       20406 :     return options;
    1435             : }
    1436             : 
    1437             : static void
    1438       23270 : parseRelOptionsInternal(Datum options, bool validate,
    1439             :                         relopt_value *reloptions, int numoptions)
    1440             : {
    1441       23270 :     ArrayType  *array = DatumGetArrayTypeP(options);
    1442             :     Datum      *optiondatums;
    1443             :     int         noptions;
    1444             :     int         i;
    1445             : 
    1446       23270 :     deconstruct_array_builtin(array, TEXTOID, &optiondatums, NULL, &noptions);
    1447             : 
    1448       48246 :     for (i = 0; i < noptions; i++)
    1449             :     {
    1450       25298 :         char       *text_str = VARDATA(optiondatums[i]);
    1451       25298 :         int         text_len = VARSIZE(optiondatums[i]) - VARHDRSZ;
    1452             :         int         j;
    1453             : 
    1454             :         /* Search for a match in reloptions */
    1455       94526 :         for (j = 0; j < numoptions; j++)
    1456             :         {
    1457       94442 :             int         kw_len = reloptions[j].gen->namelen;
    1458             : 
    1459       94442 :             if (text_len > kw_len && text_str[kw_len] == '=' &&
    1460       26320 :                 strncmp(text_str, reloptions[j].gen->name, kw_len) == 0)
    1461             :             {
    1462       25214 :                 parse_one_reloption(&reloptions[j], text_str, text_len,
    1463             :                                     validate);
    1464       24958 :                 break;
    1465             :             }
    1466             :         }
    1467             : 
    1468       25042 :         if (j >= numoptions && validate)
    1469             :         {
    1470             :             char       *s;
    1471             :             char       *p;
    1472             : 
    1473          66 :             s = TextDatumGetCString(optiondatums[i]);
    1474          66 :             p = strchr(s, '=');
    1475          66 :             if (p)
    1476          66 :                 *p = '\0';
    1477          66 :             ereport(ERROR,
    1478             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1479             :                      errmsg("unrecognized parameter \"%s\"", s)));
    1480             :         }
    1481             :     }
    1482             : 
    1483             :     /* It's worth avoiding memory leaks in this function */
    1484       22948 :     pfree(optiondatums);
    1485             : 
    1486       22948 :     if (((void *) array) != DatumGetPointer(options))
    1487       20694 :         pfree(array);
    1488       22948 : }
    1489             : 
    1490             : /*
    1491             :  * Interpret reloptions that are given in text-array format.
    1492             :  *
    1493             :  * options is a reloption text array as constructed by transformRelOptions.
    1494             :  * kind specifies the family of options to be processed.
    1495             :  *
    1496             :  * The return value is a relopt_value * array on which the options actually
    1497             :  * set in the options array are marked with isset=true.  The length of this
    1498             :  * array is returned in *numrelopts.  Options not set are also present in the
    1499             :  * array; this is so that the caller can easily locate the default values.
    1500             :  *
    1501             :  * If there are no options of the given kind, numrelopts is set to 0 and NULL
    1502             :  * is returned (unless options are illegally supplied despite none being
    1503             :  * defined, in which case an error occurs).
    1504             :  *
    1505             :  * Note: values of type int, bool and real are allocated as part of the
    1506             :  * returned array.  Values of type string are allocated separately and must
    1507             :  * be freed by the caller.
    1508             :  */
    1509             : static relopt_value *
    1510      107178 : parseRelOptions(Datum options, bool validate, relopt_kind kind,
    1511             :                 int *numrelopts)
    1512             : {
    1513      107178 :     relopt_value *reloptions = NULL;
    1514      107178 :     int         numoptions = 0;
    1515             :     int         i;
    1516             :     int         j;
    1517             : 
    1518      107178 :     if (need_initialization)
    1519        7852 :         initialize_reloptions();
    1520             : 
    1521             :     /* Build a list of expected options, based on kind */
    1522             : 
    1523     4832904 :     for (i = 0; relOpts[i]; i++)
    1524     4725726 :         if (relOpts[i]->kinds & kind)
    1525     1921108 :             numoptions++;
    1526             : 
    1527      107178 :     if (numoptions > 0)
    1528             :     {
    1529      107178 :         reloptions = palloc(numoptions * sizeof(relopt_value));
    1530             : 
    1531     4832904 :         for (i = 0, j = 0; relOpts[i]; i++)
    1532             :         {
    1533     4725726 :             if (relOpts[i]->kinds & kind)
    1534             :             {
    1535     1921108 :                 reloptions[j].gen = relOpts[i];
    1536     1921108 :                 reloptions[j].isset = false;
    1537     1921108 :                 j++;
    1538             :             }
    1539             :         }
    1540             :     }
    1541             : 
    1542             :     /* Done if no options */
    1543      107178 :     if (PointerIsValid(DatumGetPointer(options)))
    1544       22824 :         parseRelOptionsInternal(options, validate, reloptions, numoptions);
    1545             : 
    1546      106942 :     *numrelopts = numoptions;
    1547      106942 :     return reloptions;
    1548             : }
    1549             : 
    1550             : /* Parse local unregistered options. */
    1551             : static relopt_value *
    1552        2538 : parseLocalRelOptions(local_relopts *relopts, Datum options, bool validate)
    1553             : {
    1554        2538 :     int         nopts = list_length(relopts->options);
    1555        2538 :     relopt_value *values = palloc(sizeof(*values) * nopts);
    1556             :     ListCell   *lc;
    1557        2538 :     int         i = 0;
    1558             : 
    1559        5826 :     foreach(lc, relopts->options)
    1560             :     {
    1561        3288 :         local_relopt *opt = lfirst(lc);
    1562             : 
    1563        3288 :         values[i].gen = opt->option;
    1564        3288 :         values[i].isset = false;
    1565             : 
    1566        3288 :         i++;
    1567             :     }
    1568             : 
    1569        2538 :     if (options != (Datum) 0)
    1570         446 :         parseRelOptionsInternal(options, validate, values, nopts);
    1571             : 
    1572        2452 :     return values;
    1573             : }
    1574             : 
    1575             : /*
    1576             :  * Subroutine for parseRelOptions, to parse and validate a single option's
    1577             :  * value
    1578             :  */
    1579             : static void
    1580       25214 : parse_one_reloption(relopt_value *option, char *text_str, int text_len,
    1581             :                     bool validate)
    1582             : {
    1583             :     char       *value;
    1584             :     int         value_len;
    1585             :     bool        parsed;
    1586       25214 :     bool        nofree = false;
    1587             : 
    1588       25214 :     if (option->isset && validate)
    1589          12 :         ereport(ERROR,
    1590             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1591             :                  errmsg("parameter \"%s\" specified more than once",
    1592             :                         option->gen->name)));
    1593             : 
    1594       25202 :     value_len = text_len - option->gen->namelen - 1;
    1595       25202 :     value = (char *) palloc(value_len + 1);
    1596       25202 :     memcpy(value, text_str + option->gen->namelen + 1, value_len);
    1597       25202 :     value[value_len] = '\0';
    1598             : 
    1599       25202 :     switch (option->gen->type)
    1600             :     {
    1601       15122 :         case RELOPT_TYPE_BOOL:
    1602             :             {
    1603       15122 :                 parsed = parse_bool(value, &option->values.bool_val);
    1604       15122 :                 if (validate && !parsed)
    1605          36 :                     ereport(ERROR,
    1606             :                             (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1607             :                              errmsg("invalid value for boolean option \"%s\": %s",
    1608             :                                     option->gen->name, value)));
    1609             :             }
    1610       15086 :             break;
    1611        8298 :         case RELOPT_TYPE_INT:
    1612             :             {
    1613        8298 :                 relopt_int *optint = (relopt_int *) option->gen;
    1614             : 
    1615        8298 :                 parsed = parse_int(value, &option->values.int_val, 0, NULL);
    1616        8298 :                 if (validate && !parsed)
    1617          22 :                     ereport(ERROR,
    1618             :                             (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1619             :                              errmsg("invalid value for integer option \"%s\": %s",
    1620             :                                     option->gen->name, value)));
    1621        8276 :                 if (validate && (option->values.int_val < optint->min ||
    1622         818 :                                  option->values.int_val > optint->max))
    1623         120 :                     ereport(ERROR,
    1624             :                             (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1625             :                              errmsg("value %s out of bounds for option \"%s\"",
    1626             :                                     value, option->gen->name),
    1627             :                              errdetail("Valid values are between \"%d\" and \"%d\".",
    1628             :                                        optint->min, optint->max)));
    1629             :             }
    1630        8156 :             break;
    1631         466 :         case RELOPT_TYPE_REAL:
    1632             :             {
    1633         466 :                 relopt_real *optreal = (relopt_real *) option->gen;
    1634             : 
    1635         466 :                 parsed = parse_real(value, &option->values.real_val, 0, NULL);
    1636         466 :                 if (validate && !parsed)
    1637          16 :                     ereport(ERROR,
    1638             :                             (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1639             :                              errmsg("invalid value for floating point option \"%s\": %s",
    1640             :                                     option->gen->name, value)));
    1641         450 :                 if (validate && (option->values.real_val < optreal->min ||
    1642         160 :                                  option->values.real_val > optreal->max))
    1643          30 :                     ereport(ERROR,
    1644             :                             (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1645             :                              errmsg("value %s out of bounds for option \"%s\"",
    1646             :                                     value, option->gen->name),
    1647             :                              errdetail("Valid values are between \"%f\" and \"%f\".",
    1648             :                                        optreal->min, optreal->max)));
    1649             :             }
    1650         420 :             break;
    1651        1152 :         case RELOPT_TYPE_ENUM:
    1652             :             {
    1653        1152 :                 relopt_enum *optenum = (relopt_enum *) option->gen;
    1654             :                 relopt_enum_elt_def *elt;
    1655             : 
    1656        1152 :                 parsed = false;
    1657        2492 :                 for (elt = optenum->members; elt->string_val; elt++)
    1658             :                 {
    1659        2472 :                     if (pg_strcasecmp(value, elt->string_val) == 0)
    1660             :                     {
    1661        1132 :                         option->values.enum_val = elt->symbol_val;
    1662        1132 :                         parsed = true;
    1663        1132 :                         break;
    1664             :                     }
    1665             :                 }
    1666        1152 :                 if (validate && !parsed)
    1667          20 :                     ereport(ERROR,
    1668             :                             (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1669             :                              errmsg("invalid value for enum option \"%s\": %s",
    1670             :                                     option->gen->name, value),
    1671             :                              optenum->detailmsg ?
    1672             :                              errdetail_internal("%s", _(optenum->detailmsg)) : 0));
    1673             : 
    1674             :                 /*
    1675             :                  * If value is not among the allowed string values, but we are
    1676             :                  * not asked to validate, just use the default numeric value.
    1677             :                  */
    1678        1132 :                 if (!parsed)
    1679           0 :                     option->values.enum_val = optenum->default_val;
    1680             :             }
    1681        1132 :             break;
    1682         164 :         case RELOPT_TYPE_STRING:
    1683             :             {
    1684         164 :                 relopt_string *optstring = (relopt_string *) option->gen;
    1685             : 
    1686         164 :                 option->values.string_val = value;
    1687         164 :                 nofree = true;
    1688         164 :                 if (validate && optstring->validate_cb)
    1689          56 :                     (optstring->validate_cb) (value);
    1690         164 :                 parsed = true;
    1691             :             }
    1692         164 :             break;
    1693           0 :         default:
    1694           0 :             elog(ERROR, "unsupported reloption type %d", option->gen->type);
    1695             :             parsed = true;      /* quiet compiler */
    1696             :             break;
    1697             :     }
    1698             : 
    1699       24958 :     if (parsed)
    1700       24958 :         option->isset = true;
    1701       24958 :     if (!nofree)
    1702       24794 :         pfree(value);
    1703       24958 : }
    1704             : 
    1705             : /*
    1706             :  * Given the result from parseRelOptions, allocate a struct that's of the
    1707             :  * specified base size plus any extra space that's needed for string variables.
    1708             :  *
    1709             :  * "base" should be sizeof(struct) of the reloptions struct (StdRdOptions or
    1710             :  * equivalent).
    1711             :  */
    1712             : static void *
    1713      109394 : allocateReloptStruct(Size base, relopt_value *options, int numoptions)
    1714             : {
    1715      109394 :     Size        size = base;
    1716             :     int         i;
    1717             : 
    1718     2030740 :     for (i = 0; i < numoptions; i++)
    1719             :     {
    1720     1921346 :         relopt_value *optval = &options[i];
    1721             : 
    1722     1921346 :         if (optval->gen->type == RELOPT_TYPE_STRING)
    1723             :         {
    1724         232 :             relopt_string *optstr = (relopt_string *) optval->gen;
    1725             : 
    1726         232 :             if (optstr->fill_cb)
    1727             :             {
    1728           0 :                 const char *val = optval->isset ? optval->values.string_val :
    1729           0 :                     optstr->default_isnull ? NULL : optstr->default_val;
    1730             : 
    1731           0 :                 size += optstr->fill_cb(val, NULL);
    1732             :             }
    1733             :             else
    1734         232 :                 size += GET_STRING_RELOPTION_LEN(*optval) + 1;
    1735             :         }
    1736             :     }
    1737             : 
    1738      109394 :     return palloc0(size);
    1739             : }
    1740             : 
    1741             : /*
    1742             :  * Given the result of parseRelOptions and a parsing table, fill in the
    1743             :  * struct (previously allocated with allocateReloptStruct) with the parsed
    1744             :  * values.
    1745             :  *
    1746             :  * rdopts is the pointer to the allocated struct to be filled.
    1747             :  * basesize is the sizeof(struct) that was passed to allocateReloptStruct.
    1748             :  * options, of length numoptions, is parseRelOptions' output.
    1749             :  * elems, of length numelems, is the table describing the allowed options.
    1750             :  * When validate is true, it is expected that all options appear in elems.
    1751             :  */
    1752             : static void
    1753      109394 : fillRelOptions(void *rdopts, Size basesize,
    1754             :                relopt_value *options, int numoptions,
    1755             :                bool validate,
    1756             :                const relopt_parse_elt *elems, int numelems)
    1757             : {
    1758             :     int         i;
    1759      109394 :     int         offset = basesize;
    1760             : 
    1761     2030740 :     for (i = 0; i < numoptions; i++)
    1762             :     {
    1763             :         int         j;
    1764     1921346 :         bool        found = false;
    1765             : 
    1766    23141346 :         for (j = 0; j < numelems; j++)
    1767             :         {
    1768    23141346 :             if (strcmp(options[i].gen->name, elems[j].optname) == 0)
    1769             :             {
    1770             :                 relopt_string *optstring;
    1771     1921346 :                 char       *itempos = ((char *) rdopts) + elems[j].offset;
    1772             :                 char       *string_val;
    1773             : 
    1774             :                 /*
    1775             :                  * If isset_offset is provided, store whether the reloption is
    1776             :                  * set there.
    1777             :                  */
    1778     1921346 :                 if (elems[j].isset_offset > 0)
    1779             :                 {
    1780       86452 :                     char       *setpos = ((char *) rdopts) + elems[j].isset_offset;
    1781             : 
    1782       86452 :                     *(bool *) setpos = options[i].isset;
    1783             :                 }
    1784             : 
    1785     1921346 :                 switch (options[i].gen->type)
    1786             :                 {
    1787      259176 :                     case RELOPT_TYPE_BOOL:
    1788      518352 :                         *(bool *) itempos = options[i].isset ?
    1789      259176 :                             options[i].values.bool_val :
    1790      244092 :                             ((relopt_bool *) options[i].gen)->default_val;
    1791      259176 :                         break;
    1792     1160628 :                     case RELOPT_TYPE_INT:
    1793     2321256 :                         *(int *) itempos = options[i].isset ?
    1794     1160628 :                             options[i].values.int_val :
    1795     1152498 :                             ((relopt_int *) options[i].gen)->default_val;
    1796     1160628 :                         break;
    1797      397100 :                     case RELOPT_TYPE_REAL:
    1798      794200 :                         *(double *) itempos = options[i].isset ?
    1799      397100 :                             options[i].values.real_val :
    1800      396694 :                             ((relopt_real *) options[i].gen)->default_val;
    1801      397100 :                         break;
    1802      104210 :                     case RELOPT_TYPE_ENUM:
    1803      208420 :                         *(int *) itempos = options[i].isset ?
    1804      104210 :                             options[i].values.enum_val :
    1805      103078 :                             ((relopt_enum *) options[i].gen)->default_val;
    1806      104210 :                         break;
    1807         232 :                     case RELOPT_TYPE_STRING:
    1808         232 :                         optstring = (relopt_string *) options[i].gen;
    1809         232 :                         if (options[i].isset)
    1810         160 :                             string_val = options[i].values.string_val;
    1811          72 :                         else if (!optstring->default_isnull)
    1812          30 :                             string_val = optstring->default_val;
    1813             :                         else
    1814          42 :                             string_val = NULL;
    1815             : 
    1816         232 :                         if (optstring->fill_cb)
    1817             :                         {
    1818             :                             Size        size =
    1819           0 :                                 optstring->fill_cb(string_val,
    1820             :                                                    (char *) rdopts + offset);
    1821             : 
    1822           0 :                             if (size)
    1823             :                             {
    1824           0 :                                 *(int *) itempos = offset;
    1825           0 :                                 offset += size;
    1826             :                             }
    1827             :                             else
    1828           0 :                                 *(int *) itempos = 0;
    1829             :                         }
    1830         232 :                         else if (string_val == NULL)
    1831          42 :                             *(int *) itempos = 0;
    1832             :                         else
    1833             :                         {
    1834         190 :                             strcpy((char *) rdopts + offset, string_val);
    1835         190 :                             *(int *) itempos = offset;
    1836         190 :                             offset += strlen(string_val) + 1;
    1837             :                         }
    1838         232 :                         break;
    1839           0 :                     default:
    1840           0 :                         elog(ERROR, "unsupported reloption type %d",
    1841             :                              options[i].gen->type);
    1842             :                         break;
    1843             :                 }
    1844     1921346 :                 found = true;
    1845     1921346 :                 break;
    1846             :             }
    1847             :         }
    1848     1921346 :         if (validate && !found)
    1849           0 :             elog(ERROR, "reloption \"%s\" not found in parse table",
    1850             :                  options[i].gen->name);
    1851             :     }
    1852      109394 :     SET_VARSIZE(rdopts, offset);
    1853      109394 : }
    1854             : 
    1855             : 
    1856             : /*
    1857             :  * Option parser for anything that uses StdRdOptions.
    1858             :  */
    1859             : bytea *
    1860       86554 : default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
    1861             : {
    1862             :     static const relopt_parse_elt tab[] = {
    1863             :         {"fillfactor", RELOPT_TYPE_INT, offsetof(StdRdOptions, fillfactor)},
    1864             :         {"autovacuum_enabled", RELOPT_TYPE_BOOL,
    1865             :         offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, enabled)},
    1866             :         {"autovacuum_vacuum_threshold", RELOPT_TYPE_INT,
    1867             :         offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_threshold)},
    1868             :         {"autovacuum_vacuum_max_threshold", RELOPT_TYPE_INT,
    1869             :         offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_max_threshold)},
    1870             :         {"autovacuum_vacuum_insert_threshold", RELOPT_TYPE_INT,
    1871             :         offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_ins_threshold)},
    1872             :         {"autovacuum_analyze_threshold", RELOPT_TYPE_INT,
    1873             :         offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, analyze_threshold)},
    1874             :         {"autovacuum_vacuum_cost_limit", RELOPT_TYPE_INT,
    1875             :         offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_cost_limit)},
    1876             :         {"autovacuum_freeze_min_age", RELOPT_TYPE_INT,
    1877             :         offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_min_age)},
    1878             :         {"autovacuum_freeze_max_age", RELOPT_TYPE_INT,
    1879             :         offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_max_age)},
    1880             :         {"autovacuum_freeze_table_age", RELOPT_TYPE_INT,
    1881             :         offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_table_age)},
    1882             :         {"autovacuum_multixact_freeze_min_age", RELOPT_TYPE_INT,
    1883             :         offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_min_age)},
    1884             :         {"autovacuum_multixact_freeze_max_age", RELOPT_TYPE_INT,
    1885             :         offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_max_age)},
    1886             :         {"autovacuum_multixact_freeze_table_age", RELOPT_TYPE_INT,
    1887             :         offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_table_age)},
    1888             :         {"log_autovacuum_min_duration", RELOPT_TYPE_INT,
    1889             :         offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, log_min_duration)},
    1890             :         {"toast_tuple_target", RELOPT_TYPE_INT,
    1891             :         offsetof(StdRdOptions, toast_tuple_target)},
    1892             :         {"autovacuum_vacuum_cost_delay", RELOPT_TYPE_REAL,
    1893             :         offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_cost_delay)},
    1894             :         {"autovacuum_vacuum_scale_factor", RELOPT_TYPE_REAL,
    1895             :         offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_scale_factor)},
    1896             :         {"autovacuum_vacuum_insert_scale_factor", RELOPT_TYPE_REAL,
    1897             :         offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_ins_scale_factor)},
    1898             :         {"autovacuum_analyze_scale_factor", RELOPT_TYPE_REAL,
    1899             :         offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, analyze_scale_factor)},
    1900             :         {"user_catalog_table", RELOPT_TYPE_BOOL,
    1901             :         offsetof(StdRdOptions, user_catalog_table)},
    1902             :         {"parallel_workers", RELOPT_TYPE_INT,
    1903             :         offsetof(StdRdOptions, parallel_workers)},
    1904             :         {"vacuum_index_cleanup", RELOPT_TYPE_ENUM,
    1905             :         offsetof(StdRdOptions, vacuum_index_cleanup)},
    1906             :         {"vacuum_truncate", RELOPT_TYPE_BOOL,
    1907             :         offsetof(StdRdOptions, vacuum_truncate), offsetof(StdRdOptions, vacuum_truncate_set)},
    1908             :         {"vacuum_max_eager_freeze_failure_rate", RELOPT_TYPE_REAL,
    1909             :         offsetof(StdRdOptions, vacuum_max_eager_freeze_failure_rate)}
    1910             :     };
    1911             : 
    1912       86554 :     return (bytea *) build_reloptions(reloptions, validate, kind,
    1913             :                                       sizeof(StdRdOptions),
    1914             :                                       tab, lengthof(tab));
    1915             : }
    1916             : 
    1917             : /*
    1918             :  * build_reloptions
    1919             :  *
    1920             :  * Parses "reloptions" provided by the caller, returning them in a
    1921             :  * structure containing the parsed options.  The parsing is done with
    1922             :  * the help of a parsing table describing the allowed options, defined
    1923             :  * by "relopt_elems" of length "num_relopt_elems".
    1924             :  *
    1925             :  * "validate" must be true if reloptions value is freshly built by
    1926             :  * transformRelOptions(), as opposed to being read from the catalog, in which
    1927             :  * case the values contained in it must already be valid.
    1928             :  *
    1929             :  * NULL is returned if the passed-in options did not match any of the options
    1930             :  * in the parsing table, unless validate is true in which case an error would
    1931             :  * be reported.
    1932             :  */
    1933             : void *
    1934      107178 : build_reloptions(Datum reloptions, bool validate,
    1935             :                  relopt_kind kind,
    1936             :                  Size relopt_struct_size,
    1937             :                  const relopt_parse_elt *relopt_elems,
    1938             :                  int num_relopt_elems)
    1939             : {
    1940             :     int         numoptions;
    1941             :     relopt_value *options;
    1942             :     void       *rdopts;
    1943             : 
    1944             :     /* parse options specific to given relation option kind */
    1945      107178 :     options = parseRelOptions(reloptions, validate, kind, &numoptions);
    1946             :     Assert(numoptions <= num_relopt_elems);
    1947             : 
    1948             :     /* if none set, we're done */
    1949      106942 :     if (numoptions == 0)
    1950             :     {
    1951             :         Assert(options == NULL);
    1952           0 :         return NULL;
    1953             :     }
    1954             : 
    1955             :     /* allocate and fill the structure */
    1956      106942 :     rdopts = allocateReloptStruct(relopt_struct_size, options, numoptions);
    1957      106942 :     fillRelOptions(rdopts, relopt_struct_size, options, numoptions,
    1958             :                    validate, relopt_elems, num_relopt_elems);
    1959             : 
    1960      106942 :     pfree(options);
    1961             : 
    1962      106942 :     return rdopts;
    1963             : }
    1964             : 
    1965             : /*
    1966             :  * Parse local options, allocate a bytea struct that's of the specified
    1967             :  * 'base_size' plus any extra space that's needed for string variables,
    1968             :  * fill its option's fields located at the given offsets and return it.
    1969             :  */
    1970             : void *
    1971        2538 : build_local_reloptions(local_relopts *relopts, Datum options, bool validate)
    1972             : {
    1973        2538 :     int         noptions = list_length(relopts->options);
    1974        2538 :     relopt_parse_elt *elems = palloc(sizeof(*elems) * noptions);
    1975             :     relopt_value *vals;
    1976             :     void       *opts;
    1977        2538 :     int         i = 0;
    1978             :     ListCell   *lc;
    1979             : 
    1980        5826 :     foreach(lc, relopts->options)
    1981             :     {
    1982        3288 :         local_relopt *opt = lfirst(lc);
    1983             : 
    1984        3288 :         elems[i].optname = opt->option->name;
    1985        3288 :         elems[i].opttype = opt->option->type;
    1986        3288 :         elems[i].offset = opt->offset;
    1987        3288 :         elems[i].isset_offset = 0;  /* not supported for local relopts yet */
    1988             : 
    1989        3288 :         i++;
    1990             :     }
    1991             : 
    1992        2538 :     vals = parseLocalRelOptions(relopts, options, validate);
    1993        2452 :     opts = allocateReloptStruct(relopts->relopt_struct_size, vals, noptions);
    1994        2452 :     fillRelOptions(opts, relopts->relopt_struct_size, vals, noptions, validate,
    1995             :                    elems, noptions);
    1996             : 
    1997        2452 :     if (validate)
    1998         652 :         foreach(lc, relopts->validators)
    1999           6 :             ((relopts_validator) lfirst(lc)) (opts, vals, noptions);
    2000             : 
    2001        2450 :     if (elems)
    2002        2450 :         pfree(elems);
    2003             : 
    2004        2450 :     return opts;
    2005             : }
    2006             : 
    2007             : /*
    2008             :  * Option parser for partitioned tables
    2009             :  */
    2010             : bytea *
    2011        4886 : partitioned_table_reloptions(Datum reloptions, bool validate)
    2012             : {
    2013        4886 :     if (validate && reloptions)
    2014          12 :         ereport(ERROR,
    2015             :                 errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2016             :                 errmsg("cannot specify storage parameters for a partitioned table"),
    2017             :                 errhint("Specify storage parameters for its leaf partitions instead."));
    2018        4874 :     return NULL;
    2019             : }
    2020             : 
    2021             : /*
    2022             :  * Option parser for views
    2023             :  */
    2024             : bytea *
    2025       17508 : view_reloptions(Datum reloptions, bool validate)
    2026             : {
    2027             :     static const relopt_parse_elt tab[] = {
    2028             :         {"security_barrier", RELOPT_TYPE_BOOL,
    2029             :         offsetof(ViewOptions, security_barrier)},
    2030             :         {"security_invoker", RELOPT_TYPE_BOOL,
    2031             :         offsetof(ViewOptions, security_invoker)},
    2032             :         {"check_option", RELOPT_TYPE_ENUM,
    2033             :         offsetof(ViewOptions, check_option)}
    2034             :     };
    2035             : 
    2036       17508 :     return (bytea *) build_reloptions(reloptions, validate,
    2037             :                                       RELOPT_KIND_VIEW,
    2038             :                                       sizeof(ViewOptions),
    2039             :                                       tab, lengthof(tab));
    2040             : }
    2041             : 
    2042             : /*
    2043             :  * Parse options for heaps, views and toast tables.
    2044             :  */
    2045             : bytea *
    2046       93308 : heap_reloptions(char relkind, Datum reloptions, bool validate)
    2047             : {
    2048             :     StdRdOptions *rdopts;
    2049             : 
    2050       93308 :     switch (relkind)
    2051             :     {
    2052       37342 :         case RELKIND_TOASTVALUE:
    2053             :             rdopts = (StdRdOptions *)
    2054       37342 :                 default_reloptions(reloptions, validate, RELOPT_KIND_TOAST);
    2055       37336 :             if (rdopts != NULL)
    2056             :             {
    2057             :                 /* adjust default-only parameters for TOAST relations */
    2058       37336 :                 rdopts->fillfactor = 100;
    2059       37336 :                 rdopts->autovacuum.analyze_threshold = -1;
    2060       37336 :                 rdopts->autovacuum.analyze_scale_factor = -1;
    2061             :             }
    2062       37336 :             return (bytea *) rdopts;
    2063       49212 :         case RELKIND_RELATION:
    2064             :         case RELKIND_MATVIEW:
    2065       49212 :             return default_reloptions(reloptions, validate, RELOPT_KIND_HEAP);
    2066        6754 :         default:
    2067             :             /* other relkinds are not supported */
    2068        6754 :             return NULL;
    2069             :     }
    2070             : }
    2071             : 
    2072             : 
    2073             : /*
    2074             :  * Parse options for indexes.
    2075             :  *
    2076             :  *  amoptions   index AM's option parser function
    2077             :  *  reloptions  options as text[] datum
    2078             :  *  validate    error flag
    2079             :  */
    2080             : bytea *
    2081       31434 : index_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
    2082             : {
    2083             :     Assert(amoptions != NULL);
    2084             : 
    2085             :     /* Assume function is strict */
    2086       31434 :     if (!PointerIsValid(DatumGetPointer(reloptions)))
    2087       28476 :         return NULL;
    2088             : 
    2089        2958 :     return amoptions(reloptions, validate);
    2090             : }
    2091             : 
    2092             : /*
    2093             :  * Option parser for attribute reloptions
    2094             :  */
    2095             : bytea *
    2096          38 : attribute_reloptions(Datum reloptions, bool validate)
    2097             : {
    2098             :     static const relopt_parse_elt tab[] = {
    2099             :         {"n_distinct", RELOPT_TYPE_REAL, offsetof(AttributeOpts, n_distinct)},
    2100             :         {"n_distinct_inherited", RELOPT_TYPE_REAL, offsetof(AttributeOpts, n_distinct_inherited)}
    2101             :     };
    2102             : 
    2103          38 :     return (bytea *) build_reloptions(reloptions, validate,
    2104             :                                       RELOPT_KIND_ATTRIBUTE,
    2105             :                                       sizeof(AttributeOpts),
    2106             :                                       tab, lengthof(tab));
    2107             : }
    2108             : 
    2109             : /*
    2110             :  * Option parser for tablespace reloptions
    2111             :  */
    2112             : bytea *
    2113         120 : tablespace_reloptions(Datum reloptions, bool validate)
    2114             : {
    2115             :     static const relopt_parse_elt tab[] = {
    2116             :         {"random_page_cost", RELOPT_TYPE_REAL, offsetof(TableSpaceOpts, random_page_cost)},
    2117             :         {"seq_page_cost", RELOPT_TYPE_REAL, offsetof(TableSpaceOpts, seq_page_cost)},
    2118             :         {"effective_io_concurrency", RELOPT_TYPE_INT, offsetof(TableSpaceOpts, effective_io_concurrency)},
    2119             :         {"maintenance_io_concurrency", RELOPT_TYPE_INT, offsetof(TableSpaceOpts, maintenance_io_concurrency)}
    2120             :     };
    2121             : 
    2122         120 :     return (bytea *) build_reloptions(reloptions, validate,
    2123             :                                       RELOPT_KIND_TABLESPACE,
    2124             :                                       sizeof(TableSpaceOpts),
    2125             :                                       tab, lengthof(tab));
    2126             : }
    2127             : 
    2128             : /*
    2129             :  * Determine the required LOCKMODE from an option list.
    2130             :  *
    2131             :  * Called from AlterTableGetLockLevel(), see that function
    2132             :  * for a longer explanation of how this works.
    2133             :  */
    2134             : LOCKMODE
    2135         764 : AlterTableGetRelOptionsLockLevel(List *defList)
    2136             : {
    2137         764 :     LOCKMODE    lockmode = NoLock;
    2138             :     ListCell   *cell;
    2139             : 
    2140         764 :     if (defList == NIL)
    2141           0 :         return AccessExclusiveLock;
    2142             : 
    2143         764 :     if (need_initialization)
    2144           8 :         initialize_reloptions();
    2145             : 
    2146        1564 :     foreach(cell, defList)
    2147             :     {
    2148         800 :         DefElem    *def = (DefElem *) lfirst(cell);
    2149             :         int         i;
    2150             : 
    2151       36486 :         for (i = 0; relOpts[i]; i++)
    2152             :         {
    2153       35686 :             if (strncmp(relOpts[i]->name,
    2154       35686 :                         def->defname,
    2155       35686 :                         relOpts[i]->namelen + 1) == 0)
    2156             :             {
    2157        1138 :                 if (lockmode < relOpts[i]->lockmode)
    2158         758 :                     lockmode = relOpts[i]->lockmode;
    2159             :             }
    2160             :         }
    2161             :     }
    2162             : 
    2163         764 :     return lockmode;
    2164             : }

Generated by: LCOV version 1.14