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

Generated by: LCOV version 1.13