LCOV - code coverage report
Current view: top level - src/backend/access/common - reloptions.c (source / functions) Hit Total Coverage
Test: PostgreSQL 12devel Lines: 347 405 85.7 %
Date: 2018-12-15 21:21:55 Functions: 19 23 82.6 %
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-2018, 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/htup_details.h"
      23             : #include "access/nbtree.h"
      24             : #include "access/reloptions.h"
      25             : #include "access/spgist.h"
      26             : #include "access/tuptoaster.h"
      27             : #include "catalog/pg_type.h"
      28             : #include "commands/defrem.h"
      29             : #include "commands/tablespace.h"
      30             : #include "commands/view.h"
      31             : #include "nodes/makefuncs.h"
      32             : #include "postmaster/postmaster.h"
      33             : #include "utils/array.h"
      34             : #include "utils/attoptcache.h"
      35             : #include "utils/builtins.h"
      36             : #include "utils/guc.h"
      37             : #include "utils/memutils.h"
      38             : #include "utils/rel.h"
      39             : 
      40             : /*
      41             :  * Contents of pg_class.reloptions
      42             :  *
      43             :  * To add an option:
      44             :  *
      45             :  * (i) decide on a type (integer, real, bool, string), name, default value,
      46             :  * upper and lower bounds (if applicable); for strings, consider a validation
      47             :  * routine.
      48             :  * (ii) add a record below (or use add_<type>_reloption).
      49             :  * (iii) add it to the appropriate options struct (perhaps StdRdOptions)
      50             :  * (iv) add it to the appropriate handling routine (perhaps
      51             :  * default_reloptions)
      52             :  * (v) make sure the lock level is set correctly for that operation
      53             :  * (vi) don't forget to document the option
      54             :  *
      55             :  * Note that we don't handle "oids" in relOpts because it is handled by
      56             :  * interpretOidsOption().
      57             :  *
      58             :  * The default choice for any new option should be AccessExclusiveLock.
      59             :  * In some cases the lock level can be reduced from there, but the lock
      60             :  * level chosen should always conflict with itself to ensure that multiple
      61             :  * changes aren't lost when we attempt concurrent changes.
      62             :  * The choice of lock level depends completely upon how that parameter
      63             :  * is used within the server, not upon how and when you'd like to change it.
      64             :  * Safety first. Existing choices are documented here, and elsewhere in
      65             :  * backend code where the parameters are used.
      66             :  *
      67             :  * In general, anything that affects the results obtained from a SELECT must be
      68             :  * protected by AccessExclusiveLock.
      69             :  *
      70             :  * Autovacuum related parameters can be set at ShareUpdateExclusiveLock
      71             :  * since they are only used by the AV procs and don't change anything
      72             :  * currently executing.
      73             :  *
      74             :  * Fillfactor can be set because it applies only to subsequent changes made to
      75             :  * data blocks, as documented in heapio.c
      76             :  *
      77             :  * n_distinct options can be set at ShareUpdateExclusiveLock because they
      78             :  * are only used during ANALYZE, which uses a ShareUpdateExclusiveLock,
      79             :  * so the ANALYZE will not be affected by in-flight changes. Changing those
      80             :  * values has no affect until the next ANALYZE, so no need for stronger lock.
      81             :  *
      82             :  * Planner-related parameters can be set with ShareUpdateExclusiveLock because
      83             :  * they only affect planning and not the correctness of the execution. Plans
      84             :  * cannot be changed in mid-flight, so changes here could not easily result in
      85             :  * new improved plans in any case. So we allow existing queries to continue
      86             :  * and existing plans to survive, a small price to pay for allowing better
      87             :  * plans to be introduced concurrently without interfering with users.
      88             :  *
      89             :  * Setting parallel_workers is safe, since it acts the same as
      90             :  * max_parallel_workers_per_gather which is a USERSET parameter that doesn't
      91             :  * affect existing plans or queries.
      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             :             "recheck_on_update",
     135             :             "Recheck functional index expression for changed value after update",
     136             :             RELOPT_KIND_INDEX,
     137             :             ShareUpdateExclusiveLock    /* since only applies to later UPDATEs */
     138             :         },
     139             :         true
     140             :     },
     141             :     {
     142             :         {
     143             :             "security_barrier",
     144             :             "View acts as a row security barrier",
     145             :             RELOPT_KIND_VIEW,
     146             :             AccessExclusiveLock
     147             :         },
     148             :         false
     149             :     },
     150             :     /* list terminator */
     151             :     {{NULL}}
     152             : };
     153             : 
     154             : static relopt_int intRelOpts[] =
     155             : {
     156             :     {
     157             :         {
     158             :             "fillfactor",
     159             :             "Packs table pages only to this percentage",
     160             :             RELOPT_KIND_HEAP,
     161             :             ShareUpdateExclusiveLock    /* since it applies only to later
     162             :                                          * inserts */
     163             :         },
     164             :         HEAP_DEFAULT_FILLFACTOR, HEAP_MIN_FILLFACTOR, 100
     165             :     },
     166             :     {
     167             :         {
     168             :             "fillfactor",
     169             :             "Packs btree index pages only to this percentage",
     170             :             RELOPT_KIND_BTREE,
     171             :             ShareUpdateExclusiveLock    /* since it applies only to later
     172             :                                          * inserts */
     173             :         },
     174             :         BTREE_DEFAULT_FILLFACTOR, BTREE_MIN_FILLFACTOR, 100
     175             :     },
     176             :     {
     177             :         {
     178             :             "fillfactor",
     179             :             "Packs hash index pages only to this percentage",
     180             :             RELOPT_KIND_HASH,
     181             :             ShareUpdateExclusiveLock    /* since it applies only to later
     182             :                                          * inserts */
     183             :         },
     184             :         HASH_DEFAULT_FILLFACTOR, HASH_MIN_FILLFACTOR, 100
     185             :     },
     186             :     {
     187             :         {
     188             :             "fillfactor",
     189             :             "Packs gist index pages only to this percentage",
     190             :             RELOPT_KIND_GIST,
     191             :             ShareUpdateExclusiveLock    /* since it applies only to later
     192             :                                          * inserts */
     193             :         },
     194             :         GIST_DEFAULT_FILLFACTOR, GIST_MIN_FILLFACTOR, 100
     195             :     },
     196             :     {
     197             :         {
     198             :             "fillfactor",
     199             :             "Packs spgist index pages only to this percentage",
     200             :             RELOPT_KIND_SPGIST,
     201             :             ShareUpdateExclusiveLock    /* since it applies only to later
     202             :                                          * inserts */
     203             :         },
     204             :         SPGIST_DEFAULT_FILLFACTOR, SPGIST_MIN_FILLFACTOR, 100
     205             :     },
     206             :     {
     207             :         {
     208             :             "autovacuum_vacuum_threshold",
     209             :             "Minimum number of tuple updates or deletes prior to vacuum",
     210             :             RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
     211             :             ShareUpdateExclusiveLock
     212             :         },
     213             :         -1, 0, INT_MAX
     214             :     },
     215             :     {
     216             :         {
     217             :             "autovacuum_analyze_threshold",
     218             :             "Minimum number of tuple inserts, updates or deletes prior to analyze",
     219             :             RELOPT_KIND_HEAP,
     220             :             ShareUpdateExclusiveLock
     221             :         },
     222             :         -1, 0, INT_MAX
     223             :     },
     224             :     {
     225             :         {
     226             :             "autovacuum_vacuum_cost_delay",
     227             :             "Vacuum cost delay in milliseconds, for autovacuum",
     228             :             RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
     229             :             ShareUpdateExclusiveLock
     230             :         },
     231             :         -1, 0, 100
     232             :     },
     233             :     {
     234             :         {
     235             :             "autovacuum_vacuum_cost_limit",
     236             :             "Vacuum cost amount available before napping, for autovacuum",
     237             :             RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
     238             :             ShareUpdateExclusiveLock
     239             :         },
     240             :         -1, 1, 10000
     241             :     },
     242             :     {
     243             :         {
     244             :             "autovacuum_freeze_min_age",
     245             :             "Minimum age at which VACUUM should freeze a table row, for autovacuum",
     246             :             RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
     247             :             ShareUpdateExclusiveLock
     248             :         },
     249             :         -1, 0, 1000000000
     250             :     },
     251             :     {
     252             :         {
     253             :             "autovacuum_multixact_freeze_min_age",
     254             :             "Minimum multixact age at which VACUUM should freeze a row multixact's, for autovacuum",
     255             :             RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
     256             :             ShareUpdateExclusiveLock
     257             :         },
     258             :         -1, 0, 1000000000
     259             :     },
     260             :     {
     261             :         {
     262             :             "autovacuum_freeze_max_age",
     263             :             "Age at which to autovacuum a table to prevent transaction ID wraparound",
     264             :             RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
     265             :             ShareUpdateExclusiveLock
     266             :         },
     267             :         -1, 100000, 2000000000
     268             :     },
     269             :     {
     270             :         {
     271             :             "autovacuum_multixact_freeze_max_age",
     272             :             "Multixact age at which to autovacuum a table to prevent multixact wraparound",
     273             :             RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
     274             :             ShareUpdateExclusiveLock
     275             :         },
     276             :         -1, 10000, 2000000000
     277             :     },
     278             :     {
     279             :         {
     280             :             "autovacuum_freeze_table_age",
     281             :             "Age at which VACUUM should perform a full table sweep to freeze row versions",
     282             :             RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
     283             :             ShareUpdateExclusiveLock
     284             :         }, -1, 0, 2000000000
     285             :     },
     286             :     {
     287             :         {
     288             :             "autovacuum_multixact_freeze_table_age",
     289             :             "Age of multixact at which VACUUM should perform a full table sweep to freeze row versions",
     290             :             RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
     291             :             ShareUpdateExclusiveLock
     292             :         }, -1, 0, 2000000000
     293             :     },
     294             :     {
     295             :         {
     296             :             "log_autovacuum_min_duration",
     297             :             "Sets the minimum execution time above which autovacuum actions will be logged",
     298             :             RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
     299             :             ShareUpdateExclusiveLock
     300             :         },
     301             :         -1, -1, INT_MAX
     302             :     },
     303             :     {
     304             :         {
     305             :             "toast_tuple_target",
     306             :             "Sets the target tuple length at which external columns will be toasted",
     307             :             RELOPT_KIND_HEAP,
     308             :             ShareUpdateExclusiveLock
     309             :         },
     310             :         TOAST_TUPLE_TARGET, 128, TOAST_TUPLE_TARGET_MAIN
     311             :     },
     312             :     {
     313             :         {
     314             :             "pages_per_range",
     315             :             "Number of pages that each page range covers in a BRIN index",
     316             :             RELOPT_KIND_BRIN,
     317             :             AccessExclusiveLock
     318             :         }, 128, 1, 131072
     319             :     },
     320             :     {
     321             :         {
     322             :             "gin_pending_list_limit",
     323             :             "Maximum size of the pending list for this GIN index, in kilobytes.",
     324             :             RELOPT_KIND_GIN,
     325             :             AccessExclusiveLock
     326             :         },
     327             :         -1, 64, MAX_KILOBYTES
     328             :     },
     329             :     {
     330             :         {
     331             :             "effective_io_concurrency",
     332             :             "Number of simultaneous requests that can be handled efficiently by the disk subsystem.",
     333             :             RELOPT_KIND_TABLESPACE,
     334             :             ShareUpdateExclusiveLock
     335             :         },
     336             : #ifdef USE_PREFETCH
     337             :         -1, 0, MAX_IO_CONCURRENCY
     338             : #else
     339             :         0, 0, 0
     340             : #endif
     341             :     },
     342             :     {
     343             :         {
     344             :             "parallel_workers",
     345             :             "Number of parallel processes that can be used per executor node for this relation.",
     346             :             RELOPT_KIND_HEAP,
     347             :             ShareUpdateExclusiveLock
     348             :         },
     349             :         -1, 0, 1024
     350             :     },
     351             : 
     352             :     /* list terminator */
     353             :     {{NULL}}
     354             : };
     355             : 
     356             : static relopt_real realRelOpts[] =
     357             : {
     358             :     {
     359             :         {
     360             :             "autovacuum_vacuum_scale_factor",
     361             :             "Number of tuple updates or deletes prior to vacuum as a fraction of reltuples",
     362             :             RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
     363             :             ShareUpdateExclusiveLock
     364             :         },
     365             :         -1, 0.0, 100.0
     366             :     },
     367             :     {
     368             :         {
     369             :             "autovacuum_analyze_scale_factor",
     370             :             "Number of tuple inserts, updates or deletes prior to analyze as a fraction of reltuples",
     371             :             RELOPT_KIND_HEAP,
     372             :             ShareUpdateExclusiveLock
     373             :         },
     374             :         -1, 0.0, 100.0
     375             :     },
     376             :     {
     377             :         {
     378             :             "seq_page_cost",
     379             :             "Sets the planner's estimate of the cost of a sequentially fetched disk page.",
     380             :             RELOPT_KIND_TABLESPACE,
     381             :             ShareUpdateExclusiveLock
     382             :         },
     383             :         -1, 0.0, DBL_MAX
     384             :     },
     385             :     {
     386             :         {
     387             :             "random_page_cost",
     388             :             "Sets the planner's estimate of the cost of a nonsequentially fetched disk page.",
     389             :             RELOPT_KIND_TABLESPACE,
     390             :             ShareUpdateExclusiveLock
     391             :         },
     392             :         -1, 0.0, DBL_MAX
     393             :     },
     394             :     {
     395             :         {
     396             :             "n_distinct",
     397             :             "Sets the planner's estimate of the number of distinct values appearing in a column (excluding child relations).",
     398             :             RELOPT_KIND_ATTRIBUTE,
     399             :             ShareUpdateExclusiveLock
     400             :         },
     401             :         0, -1.0, DBL_MAX
     402             :     },
     403             :     {
     404             :         {
     405             :             "n_distinct_inherited",
     406             :             "Sets the planner's estimate of the number of distinct values appearing in a column (including child relations).",
     407             :             RELOPT_KIND_ATTRIBUTE,
     408             :             ShareUpdateExclusiveLock
     409             :         },
     410             :         0, -1.0, DBL_MAX
     411             :     },
     412             :     {
     413             :         {
     414             :             "vacuum_cleanup_index_scale_factor",
     415             :             "Number of tuple inserts prior to index cleanup as a fraction of reltuples.",
     416             :             RELOPT_KIND_BTREE,
     417             :             ShareUpdateExclusiveLock
     418             :         },
     419             :         -1, 0.0, 1e10
     420             :     },
     421             :     /* list terminator */
     422             :     {{NULL}}
     423             : };
     424             : 
     425             : static relopt_string stringRelOpts[] =
     426             : {
     427             :     {
     428             :         {
     429             :             "buffering",
     430             :             "Enables buffering build for this GiST index",
     431             :             RELOPT_KIND_GIST,
     432             :             AccessExclusiveLock
     433             :         },
     434             :         4,
     435             :         false,
     436             :         gistValidateBufferingOption,
     437             :         "auto"
     438             :     },
     439             :     {
     440             :         {
     441             :             "check_option",
     442             :             "View has WITH CHECK OPTION defined (local or cascaded).",
     443             :             RELOPT_KIND_VIEW,
     444             :             AccessExclusiveLock
     445             :         },
     446             :         0,
     447             :         true,
     448             :         validateWithCheckOption,
     449             :         NULL
     450             :     },
     451             :     /* list terminator */
     452             :     {{NULL}}
     453             : };
     454             : 
     455             : static relopt_gen **relOpts = NULL;
     456             : static bits32 last_assigned_kind = RELOPT_KIND_LAST_DEFAULT;
     457             : 
     458             : static int  num_custom_options = 0;
     459             : static relopt_gen **custom_options = NULL;
     460             : static bool need_initialization = true;
     461             : 
     462             : static void initialize_reloptions(void);
     463             : static void parse_one_reloption(relopt_value *option, char *text_str,
     464             :                     int text_len, bool validate);
     465             : 
     466             : /*
     467             :  * initialize_reloptions
     468             :  *      initialization routine, must be called before parsing
     469             :  *
     470             :  * Initialize the relOpts array and fill each variable's type and name length.
     471             :  */
     472             : static void
     473        3296 : initialize_reloptions(void)
     474             : {
     475             :     int         i;
     476             :     int         j;
     477             : 
     478        3296 :     j = 0;
     479       23072 :     for (i = 0; boolRelOpts[i].gen.name; i++)
     480             :     {
     481             :         Assert(DoLockModesConflict(boolRelOpts[i].gen.lockmode,
     482             :                                    boolRelOpts[i].gen.lockmode));
     483       19776 :         j++;
     484             :     }
     485       72512 :     for (i = 0; intRelOpts[i].gen.name; i++)
     486             :     {
     487             :         Assert(DoLockModesConflict(intRelOpts[i].gen.lockmode,
     488             :                                    intRelOpts[i].gen.lockmode));
     489       69216 :         j++;
     490             :     }
     491       26368 :     for (i = 0; realRelOpts[i].gen.name; i++)
     492             :     {
     493             :         Assert(DoLockModesConflict(realRelOpts[i].gen.lockmode,
     494             :                                    realRelOpts[i].gen.lockmode));
     495       23072 :         j++;
     496             :     }
     497        9888 :     for (i = 0; stringRelOpts[i].gen.name; i++)
     498             :     {
     499             :         Assert(DoLockModesConflict(stringRelOpts[i].gen.lockmode,
     500             :                                    stringRelOpts[i].gen.lockmode));
     501        6592 :         j++;
     502             :     }
     503        3296 :     j += num_custom_options;
     504             : 
     505        3296 :     if (relOpts)
     506           0 :         pfree(relOpts);
     507        3296 :     relOpts = MemoryContextAlloc(TopMemoryContext,
     508        3296 :                                  (j + 1) * sizeof(relopt_gen *));
     509             : 
     510        3296 :     j = 0;
     511       23072 :     for (i = 0; boolRelOpts[i].gen.name; i++)
     512             :     {
     513       19776 :         relOpts[j] = &boolRelOpts[i].gen;
     514       19776 :         relOpts[j]->type = RELOPT_TYPE_BOOL;
     515       19776 :         relOpts[j]->namelen = strlen(relOpts[j]->name);
     516       19776 :         j++;
     517             :     }
     518             : 
     519       72512 :     for (i = 0; intRelOpts[i].gen.name; i++)
     520             :     {
     521       69216 :         relOpts[j] = &intRelOpts[i].gen;
     522       69216 :         relOpts[j]->type = RELOPT_TYPE_INT;
     523       69216 :         relOpts[j]->namelen = strlen(relOpts[j]->name);
     524       69216 :         j++;
     525             :     }
     526             : 
     527       26368 :     for (i = 0; realRelOpts[i].gen.name; i++)
     528             :     {
     529       23072 :         relOpts[j] = &realRelOpts[i].gen;
     530       23072 :         relOpts[j]->type = RELOPT_TYPE_REAL;
     531       23072 :         relOpts[j]->namelen = strlen(relOpts[j]->name);
     532       23072 :         j++;
     533             :     }
     534             : 
     535        9888 :     for (i = 0; stringRelOpts[i].gen.name; i++)
     536             :     {
     537        6592 :         relOpts[j] = &stringRelOpts[i].gen;
     538        6592 :         relOpts[j]->type = RELOPT_TYPE_STRING;
     539        6592 :         relOpts[j]->namelen = strlen(relOpts[j]->name);
     540        6592 :         j++;
     541             :     }
     542             : 
     543        3362 :     for (i = 0; i < num_custom_options; i++)
     544             :     {
     545          66 :         relOpts[j] = custom_options[i];
     546          66 :         j++;
     547             :     }
     548             : 
     549             :     /* add a list terminator */
     550        3296 :     relOpts[j] = NULL;
     551             : 
     552             :     /* flag the work is complete */
     553        3296 :     need_initialization = false;
     554        3296 : }
     555             : 
     556             : /*
     557             :  * add_reloption_kind
     558             :  *      Create a new relopt_kind value, to be used in custom reloptions by
     559             :  *      user-defined AMs.
     560             :  */
     561             : relopt_kind
     562           2 : add_reloption_kind(void)
     563             : {
     564             :     /* don't hand out the last bit so that the enum's behavior is portable */
     565           2 :     if (last_assigned_kind >= RELOPT_KIND_MAX)
     566           0 :         ereport(ERROR,
     567             :                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     568             :                  errmsg("user-defined relation parameter types limit exceeded")));
     569           2 :     last_assigned_kind <<= 1;
     570           2 :     return (relopt_kind) last_assigned_kind;
     571             : }
     572             : 
     573             : /*
     574             :  * add_reloption
     575             :  *      Add an already-created custom reloption to the list, and recompute the
     576             :  *      main parser table.
     577             :  */
     578             : static void
     579          66 : add_reloption(relopt_gen *newoption)
     580             : {
     581             :     static int  max_custom_options = 0;
     582             : 
     583          66 :     if (num_custom_options >= max_custom_options)
     584             :     {
     585             :         MemoryContext oldcxt;
     586             : 
     587           8 :         oldcxt = MemoryContextSwitchTo(TopMemoryContext);
     588             : 
     589           8 :         if (max_custom_options == 0)
     590             :         {
     591           2 :             max_custom_options = 8;
     592           2 :             custom_options = palloc(max_custom_options * sizeof(relopt_gen *));
     593             :         }
     594             :         else
     595             :         {
     596           6 :             max_custom_options *= 2;
     597           6 :             custom_options = repalloc(custom_options,
     598             :                                       max_custom_options * sizeof(relopt_gen *));
     599             :         }
     600           8 :         MemoryContextSwitchTo(oldcxt);
     601             :     }
     602          66 :     custom_options[num_custom_options++] = newoption;
     603             : 
     604          66 :     need_initialization = true;
     605          66 : }
     606             : 
     607             : /*
     608             :  * allocate_reloption
     609             :  *      Allocate a new reloption and initialize the type-agnostic fields
     610             :  *      (for types other than string)
     611             :  */
     612             : static relopt_gen *
     613          66 : allocate_reloption(bits32 kinds, int type, const char *name, const char *desc)
     614             : {
     615             :     MemoryContext oldcxt;
     616             :     size_t      size;
     617             :     relopt_gen *newoption;
     618             : 
     619          66 :     oldcxt = MemoryContextSwitchTo(TopMemoryContext);
     620             : 
     621          66 :     switch (type)
     622             :     {
     623             :         case RELOPT_TYPE_BOOL:
     624           0 :             size = sizeof(relopt_bool);
     625           0 :             break;
     626             :         case RELOPT_TYPE_INT:
     627          66 :             size = sizeof(relopt_int);
     628          66 :             break;
     629             :         case RELOPT_TYPE_REAL:
     630           0 :             size = sizeof(relopt_real);
     631           0 :             break;
     632             :         case RELOPT_TYPE_STRING:
     633           0 :             size = sizeof(relopt_string);
     634           0 :             break;
     635             :         default:
     636           0 :             elog(ERROR, "unsupported reloption type %d", type);
     637             :             return NULL;        /* keep compiler quiet */
     638             :     }
     639             : 
     640          66 :     newoption = palloc(size);
     641             : 
     642          66 :     newoption->name = pstrdup(name);
     643          66 :     if (desc)
     644          66 :         newoption->desc = pstrdup(desc);
     645             :     else
     646           0 :         newoption->desc = NULL;
     647          66 :     newoption->kinds = kinds;
     648          66 :     newoption->namelen = strlen(name);
     649          66 :     newoption->type = type;
     650             : 
     651          66 :     MemoryContextSwitchTo(oldcxt);
     652             : 
     653          66 :     return newoption;
     654             : }
     655             : 
     656             : /*
     657             :  * add_bool_reloption
     658             :  *      Add a new boolean reloption
     659             :  */
     660             : void
     661           0 : add_bool_reloption(bits32 kinds, const char *name, const char *desc, bool default_val)
     662             : {
     663             :     relopt_bool *newoption;
     664             : 
     665           0 :     newoption = (relopt_bool *) allocate_reloption(kinds, RELOPT_TYPE_BOOL,
     666             :                                                    name, desc);
     667           0 :     newoption->default_val = default_val;
     668             : 
     669           0 :     add_reloption((relopt_gen *) newoption);
     670           0 : }
     671             : 
     672             : /*
     673             :  * add_int_reloption
     674             :  *      Add a new integer reloption
     675             :  */
     676             : void
     677          66 : add_int_reloption(bits32 kinds, const char *name, const char *desc, int default_val,
     678             :                   int min_val, int max_val)
     679             : {
     680             :     relopt_int *newoption;
     681             : 
     682          66 :     newoption = (relopt_int *) allocate_reloption(kinds, RELOPT_TYPE_INT,
     683             :                                                   name, desc);
     684          66 :     newoption->default_val = default_val;
     685          66 :     newoption->min = min_val;
     686          66 :     newoption->max = max_val;
     687             : 
     688          66 :     add_reloption((relopt_gen *) newoption);
     689          66 : }
     690             : 
     691             : /*
     692             :  * add_real_reloption
     693             :  *      Add a new float reloption
     694             :  */
     695             : void
     696           0 : add_real_reloption(bits32 kinds, const char *name, const char *desc, double default_val,
     697             :                    double min_val, double max_val)
     698             : {
     699             :     relopt_real *newoption;
     700             : 
     701           0 :     newoption = (relopt_real *) allocate_reloption(kinds, RELOPT_TYPE_REAL,
     702             :                                                    name, desc);
     703           0 :     newoption->default_val = default_val;
     704           0 :     newoption->min = min_val;
     705           0 :     newoption->max = max_val;
     706             : 
     707           0 :     add_reloption((relopt_gen *) newoption);
     708           0 : }
     709             : 
     710             : /*
     711             :  * add_string_reloption
     712             :  *      Add a new string reloption
     713             :  *
     714             :  * "validator" is an optional function pointer that can be used to test the
     715             :  * validity of the values.  It must elog(ERROR) when the argument string is
     716             :  * not acceptable for the variable.  Note that the default value must pass
     717             :  * the validation.
     718             :  */
     719             : void
     720           0 : add_string_reloption(bits32 kinds, const char *name, const char *desc, const char *default_val,
     721             :                      validate_string_relopt validator)
     722             : {
     723             :     relopt_string *newoption;
     724             : 
     725             :     /* make sure the validator/default combination is sane */
     726           0 :     if (validator)
     727           0 :         (validator) (default_val);
     728             : 
     729           0 :     newoption = (relopt_string *) allocate_reloption(kinds, RELOPT_TYPE_STRING,
     730             :                                                      name, desc);
     731           0 :     newoption->validate_cb = validator;
     732           0 :     if (default_val)
     733             :     {
     734           0 :         newoption->default_val = MemoryContextStrdup(TopMemoryContext,
     735             :                                                      default_val);
     736           0 :         newoption->default_len = strlen(default_val);
     737           0 :         newoption->default_isnull = false;
     738             :     }
     739             :     else
     740             :     {
     741           0 :         newoption->default_val = "";
     742           0 :         newoption->default_len = 0;
     743           0 :         newoption->default_isnull = true;
     744             :     }
     745             : 
     746           0 :     add_reloption((relopt_gen *) newoption);
     747           0 : }
     748             : 
     749             : /*
     750             :  * Transform a relation options list (list of DefElem) into the text array
     751             :  * format that is kept in pg_class.reloptions, including only those options
     752             :  * that are in the passed namespace.  The output values do not include the
     753             :  * namespace.
     754             :  *
     755             :  * This is used for three cases: CREATE TABLE/INDEX, ALTER TABLE SET, and
     756             :  * ALTER TABLE RESET.  In the ALTER cases, oldOptions is the existing
     757             :  * reloptions value (possibly NULL), and we replace or remove entries
     758             :  * as needed.
     759             :  *
     760             :  * If acceptOidsOff is true, then we allow oids = false, but throw error when
     761             :  * on. This is solely needed for backwards compatibility.
     762             :  *
     763             :  * Note that this is not responsible for determining whether the options
     764             :  * are valid, but it does check that namespaces for all the options given are
     765             :  * listed in validnsps.  The NULL namespace is always valid and need not be
     766             :  * explicitly listed.  Passing a NULL pointer means that only the NULL
     767             :  * namespace is valid.
     768             :  *
     769             :  * Both oldOptions and the result are text arrays (or NULL for "default"),
     770             :  * but we declare them as Datums to avoid including array.h in reloptions.h.
     771             :  */
     772             : Datum
     773      116362 : transformRelOptions(Datum oldOptions, List *defList, const char *namspace,
     774             :                     char *validnsps[], bool acceptOidsOff, bool isReset)
     775             : {
     776             :     Datum       result;
     777             :     ArrayBuildState *astate;
     778             :     ListCell   *cell;
     779             : 
     780             :     /* no change if empty list */
     781      116362 :     if (defList == NIL)
     782      114886 :         return oldOptions;
     783             : 
     784             :     /* We build new array using accumArrayResult */
     785        1476 :     astate = NULL;
     786             : 
     787             :     /* Copy any oldOptions that aren't to be replaced */
     788        1476 :     if (PointerIsValid(DatumGetPointer(oldOptions)))
     789             :     {
     790         144 :         ArrayType  *array = DatumGetArrayTypeP(oldOptions);
     791             :         Datum      *oldoptions;
     792             :         int         noldoptions;
     793             :         int         i;
     794             : 
     795         144 :         deconstruct_array(array, TEXTOID, -1, false, 'i',
     796             :                           &oldoptions, NULL, &noldoptions);
     797             : 
     798         336 :         for (i = 0; i < noldoptions; i++)
     799             :         {
     800         192 :             char       *text_str = VARDATA(oldoptions[i]);
     801         192 :             int         text_len = VARSIZE(oldoptions[i]) - VARHDRSZ;
     802             : 
     803             :             /* Search for a match in defList */
     804         276 :             foreach(cell, defList)
     805             :             {
     806         220 :                 DefElem    *def = (DefElem *) lfirst(cell);
     807             :                 int         kw_len;
     808             : 
     809             :                 /* ignore if not in the same namespace */
     810         220 :                 if (namspace == NULL)
     811             :                 {
     812         200 :                     if (def->defnamespace != NULL)
     813           0 :                         continue;
     814             :                 }
     815          20 :                 else if (def->defnamespace == NULL)
     816          12 :                     continue;
     817           8 :                 else if (strcmp(def->defnamespace, namspace) != 0)
     818           0 :                     continue;
     819             : 
     820         208 :                 kw_len = strlen(def->defname);
     821         344 :                 if (text_len > kw_len && text_str[kw_len] == '=' &&
     822         136 :                     strncmp(text_str, def->defname, kw_len) == 0)
     823         136 :                     break;
     824             :             }
     825         192 :             if (!cell)
     826             :             {
     827             :                 /* No match, so keep old option */
     828          56 :                 astate = accumArrayResult(astate, oldoptions[i],
     829             :                                           false, TEXTOID,
     830             :                                           CurrentMemoryContext);
     831             :             }
     832             :         }
     833             :     }
     834             : 
     835             :     /*
     836             :      * If CREATE/SET, add new options to array; if RESET, just check that the
     837             :      * user didn't say RESET (option=val).  (Must do this because the grammar
     838             :      * doesn't enforce it.)
     839             :      */
     840        3058 :     foreach(cell, defList)
     841             :     {
     842        1606 :         DefElem    *def = (DefElem *) lfirst(cell);
     843             : 
     844        1606 :         if (isReset)
     845             :         {
     846         132 :             if (def->arg != NULL)
     847           8 :                 ereport(ERROR,
     848             :                         (errcode(ERRCODE_SYNTAX_ERROR),
     849             :                          errmsg("RESET must not include values for parameters")));
     850             :         }
     851             :         else
     852             :         {
     853             :             text       *t;
     854             :             const char *value;
     855             :             Size        len;
     856             : 
     857             :             /*
     858             :              * Error out if the namespace is not valid.  A NULL namespace is
     859             :              * always valid.
     860             :              */
     861        1474 :             if (def->defnamespace != NULL)
     862             :             {
     863          52 :                 bool        valid = false;
     864             :                 int         i;
     865             : 
     866          52 :                 if (validnsps)
     867             :                 {
     868          52 :                     for (i = 0; validnsps[i]; i++)
     869             :                     {
     870          48 :                         if (strcmp(def->defnamespace, validnsps[i]) == 0)
     871             :                         {
     872          44 :                             valid = true;
     873          44 :                             break;
     874             :                         }
     875             :                     }
     876             :                 }
     877             : 
     878          52 :                 if (!valid)
     879           8 :                     ereport(ERROR,
     880             :                             (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     881             :                              errmsg("unrecognized parameter namespace \"%s\"",
     882             :                                     def->defnamespace)));
     883             :             }
     884             : 
     885             :             /* ignore if not in the same namespace */
     886        1466 :             if (namspace == NULL)
     887             :             {
     888        1240 :                 if (def->defnamespace != NULL)
     889          22 :                     continue;
     890             :             }
     891         226 :             else if (def->defnamespace == NULL)
     892         204 :                 continue;
     893          22 :             else if (strcmp(def->defnamespace, namspace) != 0)
     894           0 :                 continue;
     895             : 
     896             :             /*
     897             :              * Flatten the DefElem into a text string like "name=arg". If we
     898             :              * have just "name", assume "name=true" is meant.  Note: the
     899             :              * namespace is not output.
     900             :              */
     901        1240 :             if (def->arg != NULL)
     902         890 :                 value = defGetString(def);
     903             :             else
     904         350 :                 value = "true";
     905             : 
     906             :             /*
     907             :              * This is not a great place for this test, but there's no other
     908             :              * convenient place to filter the option out. As WITH (oids =
     909             :              * false) will be removed someday, this seems like an acceptable
     910             :              * amount of ugly.
     911             :              */
     912        1870 :             if (acceptOidsOff && def->defnamespace == NULL &&
     913         630 :                 strcmp(def->defname, "oids") == 0)
     914             :             {
     915          12 :                 if (defGetBoolean(def))
     916           8 :                     ereport(ERROR,
     917             :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     918             :                              errmsg("tables declared WITH OIDS are not supported")));
     919             :                 /* skip over option, reloptions machinery doesn't know it */
     920           4 :                 continue;
     921             :             }
     922             : 
     923        1228 :             len = VARHDRSZ + strlen(def->defname) + 1 + strlen(value);
     924             :             /* +1 leaves room for sprintf's trailing null */
     925        1228 :             t = (text *) palloc(len + 1);
     926        1228 :             SET_VARSIZE(t, len);
     927        1228 :             sprintf(VARDATA(t), "%s=%s", def->defname, value);
     928             : 
     929        1228 :             astate = accumArrayResult(astate, PointerGetDatum(t),
     930             :                                       false, TEXTOID,
     931             :                                       CurrentMemoryContext);
     932             :         }
     933             :     }
     934             : 
     935        1452 :     if (astate)
     936        1166 :         result = makeArrayResult(astate, CurrentMemoryContext);
     937             :     else
     938         286 :         result = (Datum) 0;
     939             : 
     940        1452 :     return result;
     941             : }
     942             : 
     943             : 
     944             : /*
     945             :  * Convert the text-array format of reloptions into a List of DefElem.
     946             :  * This is the inverse of transformRelOptions().
     947             :  */
     948             : List *
     949       16730 : untransformRelOptions(Datum options)
     950             : {
     951       16730 :     List       *result = NIL;
     952             :     ArrayType  *array;
     953             :     Datum      *optiondatums;
     954             :     int         noptions;
     955             :     int         i;
     956             : 
     957             :     /* Nothing to do if no options */
     958       16730 :     if (!PointerIsValid(DatumGetPointer(options)))
     959         872 :         return result;
     960             : 
     961       15858 :     array = DatumGetArrayTypeP(options);
     962             : 
     963       15858 :     deconstruct_array(array, TEXTOID, -1, false, 'i',
     964             :                       &optiondatums, NULL, &noptions);
     965             : 
     966       46042 :     for (i = 0; i < noptions; i++)
     967             :     {
     968             :         char       *s;
     969             :         char       *p;
     970       30184 :         Node       *val = NULL;
     971             : 
     972       30184 :         s = TextDatumGetCString(optiondatums[i]);
     973       30184 :         p = strchr(s, '=');
     974       30184 :         if (p)
     975             :         {
     976       30184 :             *p++ = '\0';
     977       30184 :             val = (Node *) makeString(pstrdup(p));
     978             :         }
     979       30184 :         result = lappend(result, makeDefElem(pstrdup(s), val, -1));
     980             :     }
     981             : 
     982       15858 :     return result;
     983             : }
     984             : 
     985             : /*
     986             :  * Extract and parse reloptions from a pg_class tuple.
     987             :  *
     988             :  * This is a low-level routine, expected to be used by relcache code and
     989             :  * callers that do not have a table's relcache entry (e.g. autovacuum).  For
     990             :  * other uses, consider grabbing the rd_options pointer from the relcache entry
     991             :  * instead.
     992             :  *
     993             :  * tupdesc is pg_class' tuple descriptor.  amoptions is a pointer to the index
     994             :  * AM's options parser function in the case of a tuple corresponding to an
     995             :  * index, or NULL otherwise.
     996             :  */
     997             : bytea *
     998      672784 : extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
     999             :                   amoptions_function amoptions)
    1000             : {
    1001             :     bytea      *options;
    1002             :     bool        isnull;
    1003             :     Datum       datum;
    1004             :     Form_pg_class classForm;
    1005             : 
    1006      672784 :     datum = fastgetattr(tuple,
    1007             :                         Anum_pg_class_reloptions,
    1008             :                         tupdesc,
    1009             :                         &isnull);
    1010      672784 :     if (isnull)
    1011      667410 :         return NULL;
    1012             : 
    1013        5374 :     classForm = (Form_pg_class) GETSTRUCT(tuple);
    1014             : 
    1015             :     /* Parse into appropriate format; don't error out here */
    1016        5374 :     switch (classForm->relkind)
    1017             :     {
    1018             :         case RELKIND_RELATION:
    1019             :         case RELKIND_TOASTVALUE:
    1020             :         case RELKIND_MATVIEW:
    1021             :         case RELKIND_PARTITIONED_TABLE:
    1022        3498 :             options = heap_reloptions(classForm->relkind, datum, false);
    1023        3498 :             break;
    1024             :         case RELKIND_VIEW:
    1025         882 :             options = view_reloptions(datum, false);
    1026         882 :             break;
    1027             :         case RELKIND_INDEX:
    1028             :         case RELKIND_PARTITIONED_INDEX:
    1029         994 :             options = index_reloptions(amoptions, datum, false);
    1030         994 :             break;
    1031             :         case RELKIND_FOREIGN_TABLE:
    1032           0 :             options = NULL;
    1033           0 :             break;
    1034             :         default:
    1035             :             Assert(false);      /* can't get here */
    1036           0 :             options = NULL;     /* keep compiler quiet */
    1037           0 :             break;
    1038             :     }
    1039             : 
    1040        5374 :     return options;
    1041             : }
    1042             : 
    1043             : /*
    1044             :  * Interpret reloptions that are given in text-array format.
    1045             :  *
    1046             :  * options is a reloption text array as constructed by transformRelOptions.
    1047             :  * kind specifies the family of options to be processed.
    1048             :  *
    1049             :  * The return value is a relopt_value * array on which the options actually
    1050             :  * set in the options array are marked with isset=true.  The length of this
    1051             :  * array is returned in *numrelopts.  Options not set are also present in the
    1052             :  * array; this is so that the caller can easily locate the default values.
    1053             :  *
    1054             :  * If there are no options of the given kind, numrelopts is set to 0 and NULL
    1055             :  * is returned (unless options are illegally supplied despite none being
    1056             :  * defined, in which case an error occurs).
    1057             :  *
    1058             :  * Note: values of type int, bool and real are allocated as part of the
    1059             :  * returned array.  Values of type string are allocated separately and must
    1060             :  * be freed by the caller.
    1061             :  */
    1062             : relopt_value *
    1063       79206 : parseRelOptions(Datum options, bool validate, relopt_kind kind,
    1064             :                 int *numrelopts)
    1065             : {
    1066       79206 :     relopt_value *reloptions = NULL;
    1067       79206 :     int         numoptions = 0;
    1068             :     int         i;
    1069             :     int         j;
    1070             : 
    1071       79206 :     if (need_initialization)
    1072        3290 :         initialize_reloptions();
    1073             : 
    1074             :     /* Build a list of expected options, based on kind */
    1075             : 
    1076     2932074 :     for (i = 0; relOpts[i]; i++)
    1077     2852868 :         if (relOpts[i]->kinds & kind)
    1078      670680 :             numoptions++;
    1079             : 
    1080       79206 :     if (numoptions > 0)
    1081             :     {
    1082       77646 :         reloptions = palloc(numoptions * sizeof(relopt_value));
    1083             : 
    1084     2874354 :         for (i = 0, j = 0; relOpts[i]; i++)
    1085             :         {
    1086     2796708 :             if (relOpts[i]->kinds & kind)
    1087             :             {
    1088      670680 :                 reloptions[j].gen = relOpts[i];
    1089      670680 :                 reloptions[j].isset = false;
    1090      670680 :                 j++;
    1091             :             }
    1092             :         }
    1093             :     }
    1094             : 
    1095             :     /* Done if no options */
    1096       79206 :     if (PointerIsValid(DatumGetPointer(options)))
    1097             :     {
    1098        6548 :         ArrayType  *array = DatumGetArrayTypeP(options);
    1099             :         Datum      *optiondatums;
    1100             :         int         noptions;
    1101             : 
    1102        6548 :         deconstruct_array(array, TEXTOID, -1, false, 'i',
    1103             :                           &optiondatums, NULL, &noptions);
    1104             : 
    1105       13658 :         for (i = 0; i < noptions; i++)
    1106             :         {
    1107        7262 :             char       *text_str = VARDATA(optiondatums[i]);
    1108        7262 :             int         text_len = VARSIZE(optiondatums[i]) - VARHDRSZ;
    1109             :             int         j;
    1110             : 
    1111             :             /* Search for a match in reloptions */
    1112       36972 :             for (j = 0; j < numoptions; j++)
    1113             :             {
    1114       36936 :                 int         kw_len = reloptions[j].gen->namelen;
    1115             : 
    1116       44306 :                 if (text_len > kw_len && text_str[kw_len] == '=' &&
    1117        7370 :                     strncmp(text_str, reloptions[j].gen->name, kw_len) == 0)
    1118             :                 {
    1119        7226 :                     parse_one_reloption(&reloptions[j], text_str, text_len,
    1120             :                                         validate);
    1121        7110 :                     break;
    1122             :                 }
    1123             :             }
    1124             : 
    1125        7146 :             if (j >= numoptions && validate)
    1126             :             {
    1127             :                 char       *s;
    1128             :                 char       *p;
    1129             : 
    1130          36 :                 s = TextDatumGetCString(optiondatums[i]);
    1131          36 :                 p = strchr(s, '=');
    1132          36 :                 if (p)
    1133          36 :                     *p = '\0';
    1134          36 :                 ereport(ERROR,
    1135             :                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1136             :                          errmsg("unrecognized parameter \"%s\"", s)));
    1137             :             }
    1138             :         }
    1139             : 
    1140             :         /* It's worth avoiding memory leaks in this function */
    1141        6396 :         pfree(optiondatums);
    1142        6396 :         if (((void *) array) != DatumGetPointer(options))
    1143        5382 :             pfree(array);
    1144             :     }
    1145             : 
    1146       79054 :     *numrelopts = numoptions;
    1147       79054 :     return reloptions;
    1148             : }
    1149             : 
    1150             : /*
    1151             :  * Subroutine for parseRelOptions, to parse and validate a single option's
    1152             :  * value
    1153             :  */
    1154             : static void
    1155        7226 : parse_one_reloption(relopt_value *option, char *text_str, int text_len,
    1156             :                     bool validate)
    1157             : {
    1158             :     char       *value;
    1159             :     int         value_len;
    1160             :     bool        parsed;
    1161        7226 :     bool        nofree = false;
    1162             : 
    1163        7226 :     if (option->isset && validate)
    1164           4 :         ereport(ERROR,
    1165             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1166             :                  errmsg("parameter \"%s\" specified more than once",
    1167             :                         option->gen->name)));
    1168             : 
    1169        7222 :     value_len = text_len - option->gen->namelen - 1;
    1170        7222 :     value = (char *) palloc(value_len + 1);
    1171        7222 :     memcpy(value, text_str + option->gen->namelen + 1, value_len);
    1172        7222 :     value[value_len] = '\0';
    1173             : 
    1174        7222 :     switch (option->gen->type)
    1175             :     {
    1176             :         case RELOPT_TYPE_BOOL:
    1177             :             {
    1178        2714 :                 parsed = parse_bool(value, &option->values.bool_val);
    1179        2714 :                 if (validate && !parsed)
    1180          16 :                     ereport(ERROR,
    1181             :                             (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1182             :                              errmsg("invalid value for boolean option \"%s\": %s",
    1183             :                                     option->gen->name, value)));
    1184             :             }
    1185        2698 :             break;
    1186             :         case RELOPT_TYPE_INT:
    1187             :             {
    1188        3860 :                 relopt_int *optint = (relopt_int *) option->gen;
    1189             : 
    1190        3860 :                 parsed = parse_int(value, &option->values.int_val, 0, NULL);
    1191        3860 :                 if (validate && !parsed)
    1192          16 :                     ereport(ERROR,
    1193             :                             (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1194             :                              errmsg("invalid value for integer option \"%s\": %s",
    1195             :                                     option->gen->name, value)));
    1196        4162 :                 if (validate && (option->values.int_val < optint->min ||
    1197         318 :                                  option->values.int_val > optint->max))
    1198          44 :                     ereport(ERROR,
    1199             :                             (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1200             :                              errmsg("value %s out of bounds for option \"%s\"",
    1201             :                                     value, option->gen->name),
    1202             :                              errdetail("Valid values are between \"%d\" and \"%d\".",
    1203             :                                        optint->min, optint->max)));
    1204             :             }
    1205        3800 :             break;
    1206             :         case RELOPT_TYPE_REAL:
    1207             :             {
    1208         194 :                 relopt_real *optreal = (relopt_real *) option->gen;
    1209             : 
    1210         194 :                 parsed = parse_real(value, &option->values.real_val);
    1211         194 :                 if (validate && !parsed)
    1212          16 :                     ereport(ERROR,
    1213             :                             (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1214             :                              errmsg("invalid value for floating point option \"%s\": %s",
    1215             :                                     option->gen->name, value)));
    1216         264 :                 if (validate && (option->values.real_val < optreal->min ||
    1217          86 :                                  option->values.real_val > optreal->max))
    1218          12 :                     ereport(ERROR,
    1219             :                             (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1220             :                              errmsg("value %s out of bounds for option \"%s\"",
    1221             :                                     value, option->gen->name),
    1222             :                              errdetail("Valid values are between \"%f\" and \"%f\".",
    1223             :                                        optreal->min, optreal->max)));
    1224             :             }
    1225         166 :             break;
    1226             :         case RELOPT_TYPE_STRING:
    1227             :             {
    1228         454 :                 relopt_string *optstring = (relopt_string *) option->gen;
    1229             : 
    1230         454 :                 option->values.string_val = value;
    1231         454 :                 nofree = true;
    1232         454 :                 if (validate && optstring->validate_cb)
    1233         110 :                     (optstring->validate_cb) (value);
    1234         446 :                 parsed = true;
    1235             :             }
    1236         446 :             break;
    1237             :         default:
    1238           0 :             elog(ERROR, "unsupported reloption type %d", option->gen->type);
    1239             :             parsed = true;      /* quiet compiler */
    1240             :             break;
    1241             :     }
    1242             : 
    1243        7110 :     if (parsed)
    1244        7110 :         option->isset = true;
    1245        7110 :     if (!nofree)
    1246        6664 :         pfree(value);
    1247        7110 : }
    1248             : 
    1249             : /*
    1250             :  * Given the result from parseRelOptions, allocate a struct that's of the
    1251             :  * specified base size plus any extra space that's needed for string variables.
    1252             :  *
    1253             :  * "base" should be sizeof(struct) of the reloptions struct (StdRdOptions or
    1254             :  * equivalent).
    1255             :  */
    1256             : void *
    1257       77494 : allocateReloptStruct(Size base, relopt_value *options, int numoptions)
    1258             : {
    1259       77494 :     Size        size = base;
    1260             :     int         i;
    1261             : 
    1262      746650 :     for (i = 0; i < numoptions; i++)
    1263      669156 :         if (options[i].gen->type == RELOPT_TYPE_STRING)
    1264       37404 :             size += GET_STRING_RELOPTION_LEN(options[i]) + 1;
    1265             : 
    1266       77494 :     return palloc0(size);
    1267             : }
    1268             : 
    1269             : /*
    1270             :  * Given the result of parseRelOptions and a parsing table, fill in the
    1271             :  * struct (previously allocated with allocateReloptStruct) with the parsed
    1272             :  * values.
    1273             :  *
    1274             :  * rdopts is the pointer to the allocated struct to be filled.
    1275             :  * basesize is the sizeof(struct) that was passed to allocateReloptStruct.
    1276             :  * options, of length numoptions, is parseRelOptions' output.
    1277             :  * elems, of length numelems, is the table describing the allowed options.
    1278             :  * When validate is true, it is expected that all options appear in elems.
    1279             :  */
    1280             : void
    1281       77494 : fillRelOptions(void *rdopts, Size basesize,
    1282             :                relopt_value *options, int numoptions,
    1283             :                bool validate,
    1284             :                const relopt_parse_elt *elems, int numelems)
    1285             : {
    1286             :     int         i;
    1287       77494 :     int         offset = basesize;
    1288             : 
    1289      746650 :     for (i = 0; i < numoptions; i++)
    1290             :     {
    1291             :         int         j;
    1292      669156 :         bool        found = false;
    1293             : 
    1294     5512058 :         for (j = 0; j < numelems; j++)
    1295             :         {
    1296     5511128 :             if (strcmp(options[i].gen->name, elems[j].optname) == 0)
    1297             :             {
    1298             :                 relopt_string *optstring;
    1299      668226 :                 char       *itempos = ((char *) rdopts) + elems[j].offset;
    1300             :                 char       *string_val;
    1301             : 
    1302      668226 :                 switch (options[i].gen->type)
    1303             :                 {
    1304             :                     case RELOPT_TYPE_BOOL:
    1305      195216 :                         *(bool *) itempos = options[i].isset ?
    1306      192518 :                             options[i].values.bool_val :
    1307       94910 :                             ((relopt_bool *) options[i].gen)->default_val;
    1308       97608 :                         break;
    1309             :                     case RELOPT_TYPE_INT:
    1310      946944 :                         *(int *) itempos = options[i].isset ?
    1311      943148 :                             options[i].values.int_val :
    1312      469676 :                             ((relopt_int *) options[i].gen)->default_val;
    1313      473472 :                         break;
    1314             :                     case RELOPT_TYPE_REAL:
    1315      119484 :                         *(double *) itempos = options[i].isset ?
    1316      119326 :                             options[i].values.real_val :
    1317       59584 :                             ((relopt_real *) options[i].gen)->default_val;
    1318       59742 :                         break;
    1319             :                     case RELOPT_TYPE_STRING:
    1320       37404 :                         optstring = (relopt_string *) options[i].gen;
    1321       37404 :                         if (options[i].isset)
    1322         446 :                             string_val = options[i].values.string_val;
    1323       36958 :                         else if (!optstring->default_isnull)
    1324          32 :                             string_val = optstring->default_val;
    1325             :                         else
    1326       36926 :                             string_val = NULL;
    1327             : 
    1328       37404 :                         if (string_val == NULL)
    1329       36926 :                             *(int *) itempos = 0;
    1330             :                         else
    1331             :                         {
    1332         478 :                             strcpy((char *) rdopts + offset, string_val);
    1333         478 :                             *(int *) itempos = offset;
    1334         478 :                             offset += strlen(string_val) + 1;
    1335             :                         }
    1336       37404 :                         break;
    1337             :                     default:
    1338           0 :                         elog(ERROR, "unsupported reloption type %d",
    1339             :                              options[i].gen->type);
    1340             :                         break;
    1341             :                 }
    1342      668226 :                 found = true;
    1343      668226 :                 break;
    1344             :             }
    1345             :         }
    1346      669156 :         if (validate && !found && options[i].gen->kinds != RELOPT_KIND_INDEX)
    1347           0 :             elog(ERROR, "reloption \"%s\" not found in parse table",
    1348             :                  options[i].gen->name);
    1349             :     }
    1350       77494 :     SET_VARSIZE(rdopts, offset);
    1351       77494 : }
    1352             : 
    1353             : 
    1354             : /*
    1355             :  * Option parser for anything that uses StdRdOptions.
    1356             :  */
    1357             : bytea *
    1358       40848 : default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
    1359             : {
    1360             :     relopt_value *options;
    1361             :     StdRdOptions *rdopts;
    1362             :     int         numoptions;
    1363             :     static const relopt_parse_elt tab[] = {
    1364             :         {"fillfactor", RELOPT_TYPE_INT, offsetof(StdRdOptions, fillfactor)},
    1365             :         {"autovacuum_enabled", RELOPT_TYPE_BOOL,
    1366             :         offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, enabled)},
    1367             :         {"autovacuum_vacuum_threshold", RELOPT_TYPE_INT,
    1368             :         offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_threshold)},
    1369             :         {"autovacuum_analyze_threshold", RELOPT_TYPE_INT,
    1370             :         offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, analyze_threshold)},
    1371             :         {"autovacuum_vacuum_cost_delay", RELOPT_TYPE_INT,
    1372             :         offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_cost_delay)},
    1373             :         {"autovacuum_vacuum_cost_limit", RELOPT_TYPE_INT,
    1374             :         offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_cost_limit)},
    1375             :         {"autovacuum_freeze_min_age", RELOPT_TYPE_INT,
    1376             :         offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_min_age)},
    1377             :         {"autovacuum_freeze_max_age", RELOPT_TYPE_INT,
    1378             :         offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_max_age)},
    1379             :         {"autovacuum_freeze_table_age", RELOPT_TYPE_INT,
    1380             :         offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_table_age)},
    1381             :         {"autovacuum_multixact_freeze_min_age", RELOPT_TYPE_INT,
    1382             :         offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_min_age)},
    1383             :         {"autovacuum_multixact_freeze_max_age", RELOPT_TYPE_INT,
    1384             :         offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_max_age)},
    1385             :         {"autovacuum_multixact_freeze_table_age", RELOPT_TYPE_INT,
    1386             :         offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_table_age)},
    1387             :         {"log_autovacuum_min_duration", RELOPT_TYPE_INT,
    1388             :         offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, log_min_duration)},
    1389             :         {"toast_tuple_target", RELOPT_TYPE_INT,
    1390             :         offsetof(StdRdOptions, toast_tuple_target)},
    1391             :         {"autovacuum_vacuum_scale_factor", RELOPT_TYPE_REAL,
    1392             :         offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_scale_factor)},
    1393             :         {"autovacuum_analyze_scale_factor", RELOPT_TYPE_REAL,
    1394             :         offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, analyze_scale_factor)},
    1395             :         {"user_catalog_table", RELOPT_TYPE_BOOL,
    1396             :         offsetof(StdRdOptions, user_catalog_table)},
    1397             :         {"parallel_workers", RELOPT_TYPE_INT,
    1398             :         offsetof(StdRdOptions, parallel_workers)},
    1399             :         {"vacuum_cleanup_index_scale_factor", RELOPT_TYPE_REAL,
    1400             :         offsetof(StdRdOptions, vacuum_cleanup_index_scale_factor)}
    1401             :     };
    1402             : 
    1403       40848 :     options = parseRelOptions(reloptions, validate, kind, &numoptions);
    1404             : 
    1405             :     /* if none set, we're done */
    1406       40740 :     if (numoptions == 0)
    1407        1560 :         return NULL;
    1408             : 
    1409       39180 :     rdopts = allocateReloptStruct(sizeof(StdRdOptions), options, numoptions);
    1410             : 
    1411       39180 :     fillRelOptions((void *) rdopts, sizeof(StdRdOptions), options, numoptions,
    1412             :                    validate, tab, lengthof(tab));
    1413             : 
    1414       39180 :     pfree(options);
    1415             : 
    1416       39180 :     return (bytea *) rdopts;
    1417             : }
    1418             : 
    1419             : /*
    1420             :  * Option parser for views
    1421             :  */
    1422             : bytea *
    1423       37344 : view_reloptions(Datum reloptions, bool validate)
    1424             : {
    1425             :     relopt_value *options;
    1426             :     ViewOptions *vopts;
    1427             :     int         numoptions;
    1428             :     static const relopt_parse_elt tab[] = {
    1429             :         {"security_barrier", RELOPT_TYPE_BOOL,
    1430             :         offsetof(ViewOptions, security_barrier)},
    1431             :         {"check_option", RELOPT_TYPE_STRING,
    1432             :         offsetof(ViewOptions, check_option_offset)}
    1433             :     };
    1434             : 
    1435       37344 :     options = parseRelOptions(reloptions, validate, RELOPT_KIND_VIEW, &numoptions);
    1436             : 
    1437             :     /* if none set, we're done */
    1438       37324 :     if (numoptions == 0)
    1439           0 :         return NULL;
    1440             : 
    1441       37324 :     vopts = allocateReloptStruct(sizeof(ViewOptions), options, numoptions);
    1442             : 
    1443       37324 :     fillRelOptions((void *) vopts, sizeof(ViewOptions), options, numoptions,
    1444             :                    validate, tab, lengthof(tab));
    1445             : 
    1446       37324 :     pfree(options);
    1447             : 
    1448       37324 :     return (bytea *) vopts;
    1449             : }
    1450             : 
    1451             : /*
    1452             :  * Parse options for heaps, views and toast tables.
    1453             :  */
    1454             : bytea *
    1455       41994 : heap_reloptions(char relkind, Datum reloptions, bool validate)
    1456             : {
    1457             :     StdRdOptions *rdopts;
    1458             : 
    1459       41994 :     switch (relkind)
    1460             :     {
    1461             :         case RELKIND_TOASTVALUE:
    1462       18314 :             rdopts = (StdRdOptions *)
    1463       18314 :                 default_reloptions(reloptions, validate, RELOPT_KIND_TOAST);
    1464       18310 :             if (rdopts != NULL)
    1465             :             {
    1466             :                 /* adjust default-only parameters for TOAST relations */
    1467       18310 :                 rdopts->fillfactor = 100;
    1468       18310 :                 rdopts->autovacuum.analyze_threshold = -1;
    1469       18310 :                 rdopts->autovacuum.analyze_scale_factor = -1;
    1470             :             }
    1471       18310 :             return (bytea *) rdopts;
    1472             :         case RELKIND_RELATION:
    1473             :         case RELKIND_MATVIEW:
    1474       20642 :             return default_reloptions(reloptions, validate, RELOPT_KIND_HEAP);
    1475             :         case RELKIND_PARTITIONED_TABLE:
    1476        1560 :             return default_reloptions(reloptions, validate,
    1477             :                                       RELOPT_KIND_PARTITIONED);
    1478             :         default:
    1479             :             /* other relkinds are not supported */
    1480        1478 :             return NULL;
    1481             :     }
    1482             : }
    1483             : 
    1484             : 
    1485             : /*
    1486             :  * Parse options for indexes.
    1487             :  *
    1488             :  *  amoptions   index AM's option parser function
    1489             :  *  reloptions  options as text[] datum
    1490             :  *  validate    error flag
    1491             :  */
    1492             : bytea *
    1493       42314 : index_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
    1494             : {
    1495             :     Assert(amoptions != NULL);
    1496             : 
    1497             :     /* Assume function is strict */
    1498       42314 :     if (!PointerIsValid(DatumGetPointer(reloptions)))
    1499       41036 :         return NULL;
    1500             : 
    1501        1278 :     return amoptions(reloptions, validate);
    1502             : }
    1503             : 
    1504             : /*
    1505             :  * Parse generic options for all indexes.
    1506             :  *
    1507             :  *  reloptions  options as text[] datum
    1508             :  *  validate    error flag
    1509             :  */
    1510             : bytea *
    1511           0 : index_generic_reloptions(Datum reloptions, bool validate)
    1512             : {
    1513             :     int         numoptions;
    1514             :     GenericIndexOpts *idxopts;
    1515             :     relopt_value *options;
    1516             :     static const relopt_parse_elt tab[] = {
    1517             :         {"recheck_on_update", RELOPT_TYPE_BOOL, offsetof(GenericIndexOpts, recheck_on_update)}
    1518             :     };
    1519             : 
    1520           0 :     options = parseRelOptions(reloptions, validate,
    1521             :                               RELOPT_KIND_INDEX,
    1522             :                               &numoptions);
    1523             : 
    1524             :     /* if none set, we're done */
    1525           0 :     if (numoptions == 0)
    1526           0 :         return NULL;
    1527             : 
    1528           0 :     idxopts = allocateReloptStruct(sizeof(GenericIndexOpts), options, numoptions);
    1529             : 
    1530           0 :     fillRelOptions((void *) idxopts, sizeof(GenericIndexOpts), options, numoptions,
    1531             :                    validate, tab, lengthof(tab));
    1532             : 
    1533           0 :     pfree(options);
    1534             : 
    1535           0 :     return (bytea *) idxopts;
    1536             : }
    1537             : 
    1538             : /*
    1539             :  * Option parser for attribute reloptions
    1540             :  */
    1541             : bytea *
    1542          26 : attribute_reloptions(Datum reloptions, bool validate)
    1543             : {
    1544             :     relopt_value *options;
    1545             :     AttributeOpts *aopts;
    1546             :     int         numoptions;
    1547             :     static const relopt_parse_elt tab[] = {
    1548             :         {"n_distinct", RELOPT_TYPE_REAL, offsetof(AttributeOpts, n_distinct)},
    1549             :         {"n_distinct_inherited", RELOPT_TYPE_REAL, offsetof(AttributeOpts, n_distinct_inherited)}
    1550             :     };
    1551             : 
    1552          26 :     options = parseRelOptions(reloptions, validate, RELOPT_KIND_ATTRIBUTE,
    1553             :                               &numoptions);
    1554             : 
    1555             :     /* if none set, we're done */
    1556          26 :     if (numoptions == 0)
    1557           0 :         return NULL;
    1558             : 
    1559          26 :     aopts = allocateReloptStruct(sizeof(AttributeOpts), options, numoptions);
    1560             : 
    1561          26 :     fillRelOptions((void *) aopts, sizeof(AttributeOpts), options, numoptions,
    1562             :                    validate, tab, lengthof(tab));
    1563             : 
    1564          26 :     pfree(options);
    1565             : 
    1566          26 :     return (bytea *) aopts;
    1567             : }
    1568             : 
    1569             : /*
    1570             :  * Option parser for tablespace reloptions
    1571             :  */
    1572             : bytea *
    1573          42 : tablespace_reloptions(Datum reloptions, bool validate)
    1574             : {
    1575             :     relopt_value *options;
    1576             :     TableSpaceOpts *tsopts;
    1577             :     int         numoptions;
    1578             :     static const relopt_parse_elt tab[] = {
    1579             :         {"random_page_cost", RELOPT_TYPE_REAL, offsetof(TableSpaceOpts, random_page_cost)},
    1580             :         {"seq_page_cost", RELOPT_TYPE_REAL, offsetof(TableSpaceOpts, seq_page_cost)},
    1581             :         {"effective_io_concurrency", RELOPT_TYPE_INT, offsetof(TableSpaceOpts, effective_io_concurrency)}
    1582             :     };
    1583             : 
    1584          42 :     options = parseRelOptions(reloptions, validate, RELOPT_KIND_TABLESPACE,
    1585             :                               &numoptions);
    1586             : 
    1587             :     /* if none set, we're done */
    1588          34 :     if (numoptions == 0)
    1589           0 :         return NULL;
    1590             : 
    1591          34 :     tsopts = allocateReloptStruct(sizeof(TableSpaceOpts), options, numoptions);
    1592             : 
    1593          34 :     fillRelOptions((void *) tsopts, sizeof(TableSpaceOpts), options, numoptions,
    1594             :                    validate, tab, lengthof(tab));
    1595             : 
    1596          34 :     pfree(options);
    1597             : 
    1598          34 :     return (bytea *) tsopts;
    1599             : }
    1600             : 
    1601             : /*
    1602             :  * Determine the required LOCKMODE from an option list.
    1603             :  *
    1604             :  * Called from AlterTableGetLockLevel(), see that function
    1605             :  * for a longer explanation of how this works.
    1606             :  */
    1607             : LOCKMODE
    1608         332 : AlterTableGetRelOptionsLockLevel(List *defList)
    1609             : {
    1610         332 :     LOCKMODE    lockmode = NoLock;
    1611             :     ListCell   *cell;
    1612             : 
    1613         332 :     if (defList == NIL)
    1614           0 :         return AccessExclusiveLock;
    1615             : 
    1616         332 :     if (need_initialization)
    1617           6 :         initialize_reloptions();
    1618             : 
    1619         680 :     foreach(cell, defList)
    1620             :     {
    1621         348 :         DefElem    *def = (DefElem *) lfirst(cell);
    1622             :         int         i;
    1623             : 
    1624       12876 :         for (i = 0; relOpts[i]; i++)
    1625             :         {
    1626       25056 :             if (strncmp(relOpts[i]->name,
    1627       12528 :                         def->defname,
    1628       12528 :                         relOpts[i]->namelen + 1) == 0)
    1629             :             {
    1630         540 :                 if (lockmode < relOpts[i]->lockmode)
    1631         332 :                     lockmode = relOpts[i]->lockmode;
    1632             :             }
    1633             :         }
    1634             :     }
    1635             : 
    1636         332 :     return lockmode;
    1637             : }

Generated by: LCOV version 1.13