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

Generated by: LCOV version 1.11