LCOV - code coverage report
Current view: top level - src/backend/commands - explain_state.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 83.9 % 161 135
Test Date: 2026-07-03 19:57:34 Functions: 100.0 % 9 9
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 73.1 % 156 114

             Branch data     Line data    Source code
       1                 :             : /*-------------------------------------------------------------------------
       2                 :             :  *
       3                 :             :  * explain_state.c
       4                 :             :  *    Code for initializing and accessing ExplainState objects
       5                 :             :  *
       6                 :             :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       7                 :             :  * Portions Copyright (c) 1994-5, Regents of the University of California
       8                 :             :  *
       9                 :             :  * In-core options have hard-coded fields inside ExplainState; e.g. if
      10                 :             :  * the user writes EXPLAIN (BUFFERS) then ExplainState's "buffers" member
      11                 :             :  * will be set to true. Extensions can also register options using
      12                 :             :  * RegisterExtensionExplainOption; so that e.g. EXPLAIN (BICYCLE 'red')
      13                 :             :  * will invoke a designated handler that knows what the legal values are
      14                 :             :  * for the BICYCLE option. However, it's not enough for an extension to be
      15                 :             :  * able to parse new options: it also needs a place to store the results
      16                 :             :  * of that parsing, and an ExplainState has no 'bicycle' field.
      17                 :             :  *
      18                 :             :  * To solve this problem, an ExplainState can contain an array of opaque
      19                 :             :  * pointers, one per extension. An extension can use GetExplainExtensionId
      20                 :             :  * to acquire an integer ID to acquire an offset into this array that is
      21                 :             :  * reserved for its exclusive use, and then use GetExplainExtensionState
      22                 :             :  * and SetExplainExtensionState to read and write its own private state
      23                 :             :  * within an ExplainState.
      24                 :             :  *
      25                 :             :  * Note that there is no requirement that the name of the option match
      26                 :             :  * the name of the extension; e.g. a pg_explain_conveyance extension could
      27                 :             :  * implement options for BICYCLE, MONORAIL, etc.
      28                 :             :  *
      29                 :             :  * IDENTIFICATION
      30                 :             :  *    src/backend/commands/explain_state.c
      31                 :             :  *
      32                 :             :  *-------------------------------------------------------------------------
      33                 :             :  */
      34                 :             : #include "postgres.h"
      35                 :             : 
      36                 :             : #include "commands/defrem.h"
      37                 :             : #include "commands/explain.h"
      38                 :             : #include "commands/explain_state.h"
      39                 :             : #include "utils/builtins.h"
      40                 :             : #include "utils/guc.h"
      41                 :             : 
      42                 :             : /* Hook to perform additional EXPLAIN options validation */
      43                 :             : explain_validate_options_hook_type explain_validate_options_hook = NULL;
      44                 :             : 
      45                 :             : typedef struct
      46                 :             : {
      47                 :             :     const char *option_name;
      48                 :             :     ExplainOptionHandler option_handler;
      49                 :             :     ExplainOptionGUCCheckHandler guc_check_handler;
      50                 :             : } ExplainExtensionOption;
      51                 :             : 
      52                 :             : static const char **ExplainExtensionNameArray = NULL;
      53                 :             : static int  ExplainExtensionNamesAssigned = 0;
      54                 :             : static int  ExplainExtensionNamesAllocated = 0;
      55                 :             : 
      56                 :             : static ExplainExtensionOption *ExplainExtensionOptionArray = NULL;
      57                 :             : static int  ExplainExtensionOptionsAssigned = 0;
      58                 :             : static int  ExplainExtensionOptionsAllocated = 0;
      59                 :             : 
      60                 :             : /*
      61                 :             :  * Create a new ExplainState struct initialized with default options.
      62                 :             :  */
      63                 :             : ExplainState *
      64                 :       16538 : NewExplainState(void)
      65                 :             : {
      66                 :       16538 :     ExplainState *es = palloc0_object(ExplainState);
      67                 :             : 
      68                 :             :     /* Set default options (most fields can be left as zeroes). */
      69                 :       16538 :     es->costs = true;
      70                 :             :     /* Prepare output buffer. */
      71                 :       16538 :     es->str = makeStringInfo();
      72                 :             : 
      73                 :       16538 :     return es;
      74                 :             : }
      75                 :             : 
      76                 :             : /*
      77                 :             :  * Parse a list of EXPLAIN options and update an ExplainState accordingly.
      78                 :             :  */
      79                 :             : void
      80                 :       16527 : ParseExplainOptionList(ExplainState *es, List *options, ParseState *pstate)
      81                 :             : {
      82                 :             :     ListCell   *lc;
      83                 :       16527 :     bool        timing_set = false;
      84                 :       16527 :     bool        buffers_set = false;
      85                 :       16527 :     bool        summary_set = false;
      86                 :             : 
      87                 :             :     /* Parse options list. */
      88   [ +  +  +  +  :       32884 :     foreach(lc, options)
                   +  + ]
      89                 :             :     {
      90                 :       16361 :         DefElem    *opt = (DefElem *) lfirst(lc);
      91                 :             : 
      92         [ +  + ]:       16361 :         if (strcmp(opt->defname, "analyze") == 0)
      93                 :        2362 :             es->analyze = defGetBoolean(opt);
      94         [ +  + ]:       13999 :         else if (strcmp(opt->defname, "verbose") == 0)
      95                 :        1777 :             es->verbose = defGetBoolean(opt);
      96         [ +  + ]:       12222 :         else if (strcmp(opt->defname, "costs") == 0)
      97                 :        9949 :             es->costs = defGetBoolean(opt);
      98         [ +  + ]:        2273 :         else if (strcmp(opt->defname, "buffers") == 0)
      99                 :             :         {
     100                 :         650 :             buffers_set = true;
     101                 :         650 :             es->buffers = defGetBoolean(opt);
     102                 :             :         }
     103         [ -  + ]:        1623 :         else if (strcmp(opt->defname, "wal") == 0)
     104                 :           0 :             es->wal = defGetBoolean(opt);
     105         [ +  + ]:        1623 :         else if (strcmp(opt->defname, "settings") == 0)
     106                 :           8 :             es->settings = defGetBoolean(opt);
     107         [ +  + ]:        1615 :         else if (strcmp(opt->defname, "generic_plan") == 0)
     108                 :          12 :             es->generic = defGetBoolean(opt);
     109         [ +  + ]:        1603 :         else if (strcmp(opt->defname, "timing") == 0)
     110                 :             :         {
     111                 :         606 :             timing_set = true;
     112                 :         606 :             es->timing = defGetBoolean(opt);
     113                 :             :         }
     114         [ +  + ]:         997 :         else if (strcmp(opt->defname, "summary") == 0)
     115                 :             :         {
     116                 :         590 :             summary_set = true;
     117                 :         590 :             es->summary = defGetBoolean(opt);
     118                 :             :         }
     119         [ +  + ]:         407 :         else if (strcmp(opt->defname, "memory") == 0)
     120                 :          20 :             es->memory = defGetBoolean(opt);
     121         [ +  + ]:         387 :         else if (strcmp(opt->defname, "serialize") == 0)
     122                 :             :         {
     123         [ +  + ]:          20 :             if (opt->arg)
     124                 :             :             {
     125                 :           8 :                 char       *p = defGetString(opt);
     126                 :             : 
     127   [ +  -  -  + ]:           8 :                 if (strcmp(p, "off") == 0 || strcmp(p, "none") == 0)
     128                 :           0 :                     es->serialize = EXPLAIN_SERIALIZE_NONE;
     129         [ +  + ]:           8 :                 else if (strcmp(p, "text") == 0)
     130                 :           4 :                     es->serialize = EXPLAIN_SERIALIZE_TEXT;
     131         [ +  - ]:           4 :                 else if (strcmp(p, "binary") == 0)
     132                 :           4 :                     es->serialize = EXPLAIN_SERIALIZE_BINARY;
     133                 :             :                 else
     134         [ #  # ]:           0 :                     ereport(ERROR,
     135                 :             :                             (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     136                 :             :                              errmsg("unrecognized value for %s option \"%s\": \"%s\"",
     137                 :             :                                     "EXPLAIN", opt->defname, p),
     138                 :             :                              parser_errposition(pstate, opt->location)));
     139                 :             :             }
     140                 :             :             else
     141                 :             :             {
     142                 :             :                 /* SERIALIZE without an argument is taken as 'text' */
     143                 :          12 :                 es->serialize = EXPLAIN_SERIALIZE_TEXT;
     144                 :             :             }
     145                 :             :         }
     146         [ +  + ]:         367 :         else if (strcmp(opt->defname, "format") == 0)
     147                 :             :         {
     148                 :         204 :             char       *p = defGetString(opt);
     149                 :             : 
     150         [ +  + ]:         204 :             if (strcmp(p, "text") == 0)
     151                 :           9 :                 es->format = EXPLAIN_FORMAT_TEXT;
     152         [ +  + ]:         195 :             else if (strcmp(p, "xml") == 0)
     153                 :           5 :                 es->format = EXPLAIN_FORMAT_XML;
     154         [ +  + ]:         190 :             else if (strcmp(p, "json") == 0)
     155                 :         182 :                 es->format = EXPLAIN_FORMAT_JSON;
     156         [ +  - ]:           8 :             else if (strcmp(p, "yaml") == 0)
     157                 :           8 :                 es->format = EXPLAIN_FORMAT_YAML;
     158                 :             :             else
     159         [ #  # ]:           0 :                 ereport(ERROR,
     160                 :             :                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     161                 :             :                          errmsg("unrecognized value for %s option \"%s\": \"%s\"",
     162                 :             :                                 "EXPLAIN", opt->defname, p),
     163                 :             :                          parser_errposition(pstate, opt->location)));
     164                 :             :         }
     165         [ +  + ]:         163 :         else if (strcmp(opt->defname, "io") == 0)
     166                 :           8 :             es->io = defGetBoolean(opt);
     167         [ +  + ]:         155 :         else if (!ApplyExtensionExplainOption(es, opt, pstate))
     168         [ +  - ]:           4 :             ereport(ERROR,
     169                 :             :                     (errcode(ERRCODE_SYNTAX_ERROR),
     170                 :             :                      errmsg("unrecognized %s option \"%s\"",
     171                 :             :                             "EXPLAIN", opt->defname),
     172                 :             :                      parser_errposition(pstate, opt->location)));
     173                 :             :     }
     174                 :             : 
     175                 :             :     /* check that WAL is used with EXPLAIN ANALYZE */
     176   [ -  +  -  - ]:       16523 :     if (es->wal && !es->analyze)
     177         [ #  # ]:           0 :         ereport(ERROR,
     178                 :             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     179                 :             :                  errmsg("EXPLAIN option %s requires ANALYZE", "WAL")));
     180                 :             : 
     181                 :             :     /* if the timing was not set explicitly, set default value */
     182         [ +  + ]:       16523 :     es->timing = (timing_set) ? es->timing : es->analyze;
     183                 :             : 
     184                 :             :     /* if the buffers was not set explicitly, set default value */
     185         [ +  + ]:       16523 :     es->buffers = (buffers_set) ? es->buffers : es->analyze;
     186                 :             : 
     187                 :             :     /* check that timing is used with EXPLAIN ANALYZE */
     188   [ +  +  -  + ]:       16523 :     if (es->timing && !es->analyze)
     189         [ #  # ]:           0 :         ereport(ERROR,
     190                 :             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     191                 :             :                  errmsg("EXPLAIN option %s requires ANALYZE", "TIMING")));
     192                 :             : 
     193                 :             :     /* check that IO is used with EXPLAIN ANALYZE */
     194   [ +  +  -  + ]:       16523 :     if (es->io && !es->analyze)
     195         [ #  # ]:           0 :         ereport(ERROR,
     196                 :             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     197                 :             :                  errmsg("EXPLAIN option %s requires ANALYZE", "IO")));
     198                 :             : 
     199                 :             :     /* check that serialize is used with EXPLAIN ANALYZE */
     200   [ +  +  -  + ]:       16523 :     if (es->serialize != EXPLAIN_SERIALIZE_NONE && !es->analyze)
     201         [ #  # ]:           0 :         ereport(ERROR,
     202                 :             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     203                 :             :                  errmsg("EXPLAIN option %s requires ANALYZE", "SERIALIZE")));
     204                 :             : 
     205                 :             :     /* check that GENERIC_PLAN is not used with EXPLAIN ANALYZE */
     206   [ +  +  +  + ]:       16523 :     if (es->generic && es->analyze)
     207         [ +  - ]:           4 :         ereport(ERROR,
     208                 :             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     209                 :             :                  errmsg("%s options %s and %s cannot be used together",
     210                 :             :                         "EXPLAIN", "ANALYZE", "GENERIC_PLAN")));
     211                 :             : 
     212                 :             :     /* if the summary was not set explicitly, set default value */
     213         [ +  + ]:       16519 :     es->summary = (summary_set) ? es->summary : es->analyze;
     214                 :             : 
     215                 :             :     /* plugin specific option validation */
     216         [ -  + ]:       16519 :     if (explain_validate_options_hook)
     217                 :           0 :         (*explain_validate_options_hook) (es, options, pstate);
     218                 :       16519 : }
     219                 :             : 
     220                 :             : /*
     221                 :             :  * Map the name of an EXPLAIN extension to an integer ID.
     222                 :             :  *
     223                 :             :  * Within the lifetime of a particular backend, the same name will be mapped
     224                 :             :  * to the same ID every time. IDs are not stable across backends. Use the ID
     225                 :             :  * that you get from this function to call GetExplainExtensionState and
     226                 :             :  * SetExplainExtensionState.
     227                 :             :  *
     228                 :             :  * extension_name is assumed to be a constant string or allocated in storage
     229                 :             :  * that will never be freed.
     230                 :             :  */
     231                 :             : int
     232                 :          36 : GetExplainExtensionId(const char *extension_name)
     233                 :             : {
     234                 :             :     /* Search for an existing extension by this name; if found, return ID. */
     235         [ -  + ]:          36 :     for (int i = 0; i < ExplainExtensionNamesAssigned; ++i)
     236         [ #  # ]:           0 :         if (strcmp(ExplainExtensionNameArray[i], extension_name) == 0)
     237                 :           0 :             return i;
     238                 :             : 
     239                 :             :     /* If there is no array yet, create one. */
     240         [ +  - ]:          36 :     if (ExplainExtensionNameArray == NULL)
     241                 :             :     {
     242                 :          36 :         ExplainExtensionNamesAllocated = 16;
     243                 :          36 :         ExplainExtensionNameArray = (const char **)
     244                 :          36 :             MemoryContextAlloc(TopMemoryContext,
     245                 :             :                                ExplainExtensionNamesAllocated
     246                 :             :                                * sizeof(char *));
     247                 :             :     }
     248                 :             : 
     249                 :             :     /* If there's an array but it's currently full, expand it. */
     250         [ -  + ]:          36 :     if (ExplainExtensionNamesAssigned >= ExplainExtensionNamesAllocated)
     251                 :             :     {
     252                 :           0 :         int         i = pg_nextpower2_32(ExplainExtensionNamesAssigned + 1);
     253                 :             : 
     254                 :           0 :         ExplainExtensionNameArray = (const char **)
     255                 :           0 :             repalloc(ExplainExtensionNameArray, i * sizeof(char *));
     256                 :           0 :         ExplainExtensionNamesAllocated = i;
     257                 :             :     }
     258                 :             : 
     259                 :             :     /* Assign and return new ID. */
     260                 :          36 :     ExplainExtensionNameArray[ExplainExtensionNamesAssigned] = extension_name;
     261                 :          36 :     return ExplainExtensionNamesAssigned++;
     262                 :             : }
     263                 :             : 
     264                 :             : /*
     265                 :             :  * Get extension-specific state from an ExplainState.
     266                 :             :  *
     267                 :             :  * See comments for SetExplainExtensionState, below.
     268                 :             :  */
     269                 :             : void *
     270                 :        7680 : GetExplainExtensionState(ExplainState *es, int extension_id)
     271                 :             : {
     272                 :             :     Assert(extension_id >= 0);
     273                 :             : 
     274         [ +  + ]:        7680 :     if (extension_id >= es->extension_state_allocated)
     275                 :        7346 :         return NULL;
     276                 :             : 
     277                 :         334 :     return es->extension_state[extension_id];
     278                 :             : }
     279                 :             : 
     280                 :             : /*
     281                 :             :  * Store extension-specific state into an ExplainState.
     282                 :             :  *
     283                 :             :  * To use this function, first obtain an integer extension_id using
     284                 :             :  * GetExplainExtensionId. Then use this function to store an opaque pointer
     285                 :             :  * in the ExplainState. Later, you can retrieve the opaque pointer using
     286                 :             :  * GetExplainExtensionState.
     287                 :             :  */
     288                 :             : void
     289                 :         150 : SetExplainExtensionState(ExplainState *es, int extension_id, void *opaque)
     290                 :             : {
     291                 :             :     Assert(extension_id >= 0);
     292                 :             : 
     293                 :             :     /* If there is no array yet, create one. */
     294         [ +  - ]:         150 :     if (es->extension_state == NULL)
     295                 :             :     {
     296                 :         150 :         es->extension_state_allocated =
     297         [ -  + ]:         150 :             Max(16, pg_nextpower2_32(extension_id + 1));
     298                 :         150 :         es->extension_state =
     299                 :         150 :             palloc0(es->extension_state_allocated * sizeof(void *));
     300                 :             :     }
     301                 :             : 
     302                 :             :     /* If there's an array but it's currently full, expand it. */
     303         [ -  + ]:         150 :     if (extension_id >= es->extension_state_allocated)
     304                 :             :     {
     305                 :             :         int         i;
     306                 :             : 
     307                 :           0 :         i = pg_nextpower2_32(extension_id + 1);
     308                 :           0 :         es->extension_state = repalloc0_array(es->extension_state, void *, es->extension_state_allocated, i);
     309                 :           0 :         es->extension_state_allocated = i;
     310                 :             :     }
     311                 :             : 
     312                 :         150 :     es->extension_state[extension_id] = opaque;
     313                 :         150 : }
     314                 :             : 
     315                 :             : /*
     316                 :             :  * Register a new EXPLAIN option.
     317                 :             :  *
     318                 :             :  * option_name is assumed to be a constant string or allocated in storage
     319                 :             :  * that will never be freed.
     320                 :             :  *
     321                 :             :  * When option_name is used as an EXPLAIN option, handler will be called and
     322                 :             :  * should update the ExplainState passed to it. See comments at top of file
     323                 :             :  * for a more detailed explanation.
     324                 :             :  *
     325                 :             :  * guc_check_handler is a function that can be safely called from a
     326                 :             :  * GUC check hook to validate a proposed value for a custom EXPLAIN option.
     327                 :             :  * Boolean-valued options can pass GUCCheckBooleanExplainOption. See the
     328                 :             :  * comments for GUCCheckBooleanExplainOption for further information on
     329                 :             :  * how a guc_check_handler should behave.
     330                 :             :  */
     331                 :             : void
     332                 :          50 : RegisterExtensionExplainOption(const char *option_name,
     333                 :             :                                ExplainOptionHandler handler,
     334                 :             :                                ExplainOptionGUCCheckHandler guc_check_handler)
     335                 :             : {
     336                 :             :     ExplainExtensionOption *exopt;
     337                 :             : 
     338                 :             :     Assert(handler != NULL);
     339                 :             :     Assert(guc_check_handler != NULL);
     340                 :             : 
     341                 :             :     /* Search for an existing option by this name; if found, update handler. */
     342         [ +  + ]:          64 :     for (int i = 0; i < ExplainExtensionOptionsAssigned; ++i)
     343                 :             :     {
     344         [ -  + ]:          14 :         if (strcmp(ExplainExtensionOptionArray[i].option_name,
     345                 :             :                    option_name) == 0)
     346                 :             :         {
     347                 :           0 :             exopt = &ExplainExtensionOptionArray[i];
     348                 :             : 
     349                 :           0 :             exopt->option_handler = handler;
     350                 :           0 :             exopt->guc_check_handler = guc_check_handler;
     351                 :           0 :             return;
     352                 :             :         }
     353                 :             :     }
     354                 :             : 
     355                 :             :     /* If there is no array yet, create one. */
     356         [ +  + ]:          50 :     if (ExplainExtensionOptionArray == NULL)
     357                 :             :     {
     358                 :          36 :         ExplainExtensionOptionsAllocated = 16;
     359                 :          36 :         ExplainExtensionOptionArray = (ExplainExtensionOption *)
     360                 :          36 :             MemoryContextAlloc(TopMemoryContext,
     361                 :             :                                ExplainExtensionOptionsAllocated
     362                 :             :                                * sizeof(ExplainExtensionOption));
     363                 :             :     }
     364                 :             : 
     365                 :             :     /* If there's an array but it's currently full, expand it. */
     366         [ -  + ]:          50 :     if (ExplainExtensionOptionsAssigned >= ExplainExtensionOptionsAllocated)
     367                 :             :     {
     368                 :           0 :         int         i = pg_nextpower2_32(ExplainExtensionOptionsAssigned + 1);
     369                 :             : 
     370                 :           0 :         ExplainExtensionOptionArray = (ExplainExtensionOption *)
     371                 :           0 :             repalloc(ExplainExtensionOptionArray, i * sizeof(ExplainExtensionOption));
     372                 :           0 :         ExplainExtensionOptionsAllocated = i;
     373                 :             :     }
     374                 :             : 
     375                 :             :     /* Assign and return new ID. */
     376                 :          50 :     exopt = &ExplainExtensionOptionArray[ExplainExtensionOptionsAssigned++];
     377                 :          50 :     exopt->option_name = option_name;
     378                 :          50 :     exopt->option_handler = handler;
     379                 :          50 :     exopt->guc_check_handler = guc_check_handler;
     380                 :             : }
     381                 :             : 
     382                 :             : /*
     383                 :             :  * Apply an EXPLAIN option registered by an extension.
     384                 :             :  *
     385                 :             :  * If no extension has registered the named option, returns false. Otherwise,
     386                 :             :  * calls the appropriate handler function and then returns true.
     387                 :             :  */
     388                 :             : bool
     389                 :         156 : ApplyExtensionExplainOption(ExplainState *es, DefElem *opt, ParseState *pstate)
     390                 :             : {
     391         [ +  + ]:         167 :     for (int i = 0; i < ExplainExtensionOptionsAssigned; ++i)
     392                 :             :     {
     393                 :         163 :         if (strcmp(ExplainExtensionOptionArray[i].option_name,
     394         [ +  + ]:         163 :                    opt->defname) == 0)
     395                 :             :         {
     396                 :         152 :             ExplainExtensionOptionArray[i].option_handler(es, opt, pstate);
     397                 :         152 :             return true;
     398                 :             :         }
     399                 :             :     }
     400                 :             : 
     401                 :           4 :     return false;
     402                 :             : }
     403                 :             : 
     404                 :             : /*
     405                 :             :  * Determine whether an EXPLAIN extension option will be accepted without
     406                 :             :  * error. Returns true if so, and false if not. See the comments for
     407                 :             :  * GUCCheckBooleanExplainOption for more details.
     408                 :             :  *
     409                 :             :  * The caller need not know that the option_name is valid; this function
     410                 :             :  * will indicate that the option is unrecognized if that is the case.
     411                 :             :  */
     412                 :             : bool
     413                 :          23 : GUCCheckExplainExtensionOption(const char *option_name,
     414                 :             :                                const char *option_value,
     415                 :             :                                NodeTag option_type)
     416                 :             : {
     417         [ +  + ]:          32 :     for (int i = 0; i < ExplainExtensionOptionsAssigned; i++)
     418                 :             :     {
     419                 :          31 :         ExplainExtensionOption *exopt = &ExplainExtensionOptionArray[i];
     420                 :             : 
     421         [ +  + ]:          31 :         if (strcmp(exopt->option_name, option_name) == 0)
     422                 :          22 :             return exopt->guc_check_handler(option_name, option_value,
     423                 :             :                                             option_type);
     424                 :             :     }
     425                 :             : 
     426                 :             :     /* Unrecognized option name. */
     427                 :           1 :     GUC_check_errmsg("unrecognized EXPLAIN option \"%s\"", option_name);
     428                 :           1 :     return false;
     429                 :             : }
     430                 :             : 
     431                 :             : /*
     432                 :             :  * guc_check_handler for Boolean-valued EXPLAIN extension options.
     433                 :             :  *
     434                 :             :  * After receiving a "true" value from this or any other GUC check handler
     435                 :             :  * for an EXPLAIN extension option, the caller is entitled to assume that
     436                 :             :  * a suitably constructed DefElem passed to the main option handler will
     437                 :             :  * not cause an error. To construct this DefElem, the caller should set
     438                 :             :  * the DefElem's defname to option_name. If option_value is NULL, arg
     439                 :             :  * should be NULL. Otherwise, arg should be of the type given by
     440                 :             :  * option_type, with option_value as the associated value. The only option
     441                 :             :  * types that should be passed are T_String, T_Float, and T_Integer; in
     442                 :             :  * the last case, the caller will need to perform a string-to-integer
     443                 :             :  * conversion.
     444                 :             :  *
     445                 :             :  * A guc_check_handler should not throw an error, and should not allocate
     446                 :             :  * memory.  If it returns false to indicate that the option_value is not
     447                 :             :  * acceptable, it may use GUC_check_errmsg(), GUC_check_errdetail(), etc.
     448                 :             :  * to clarify the nature of the problem.
     449                 :             :  *
     450                 :             :  * Since we're concerned with Boolean options here, the logic below must
     451                 :             :  * exactly match the semantics of defGetBoolean.
     452                 :             :  */
     453                 :             : bool
     454                 :          22 : GUCCheckBooleanExplainOption(const char *option_name,
     455                 :             :                              const char *option_value,
     456                 :             :                              NodeTag option_type)
     457                 :             : {
     458                 :          22 :     bool        valid = false;
     459                 :             : 
     460         [ +  + ]:          22 :     if (option_value == NULL)
     461                 :             :     {
     462                 :             :         /* defGetBoolean treats no argument as valid */
     463                 :          12 :         valid = true;
     464                 :             :     }
     465         [ +  + ]:          10 :     else if (option_type == T_String)
     466                 :             :     {
     467                 :             :         /* defGetBoolean accepts exactly these string values */
     468   [ +  +  +  + ]:          12 :         if (pg_strcasecmp(option_value, "true") == 0 ||
     469         [ +  - ]:           8 :             pg_strcasecmp(option_value, "false") == 0 ||
     470         [ +  + ]:           6 :             pg_strcasecmp(option_value, "on") == 0 ||
     471                 :           3 :             pg_strcasecmp(option_value, "off") == 0)
     472                 :           5 :             valid = true;
     473                 :             :     }
     474         [ +  + ]:           3 :     else if (option_type == T_Integer)
     475                 :             :     {
     476                 :             :         long        value;
     477                 :             :         char       *end;
     478                 :             : 
     479                 :             :         /*
     480                 :             :          * defGetBoolean accepts only 0 and 1, but those can be spelled in
     481                 :             :          * various ways (e.g. 01, 0x01).
     482                 :             :          */
     483                 :           2 :         errno = 0;
     484                 :           2 :         value = strtol(option_value, &end, 0);
     485   [ +  -  +  -  :           2 :         if (errno == 0 && *end == '\0' && end != option_value &&
                   +  - ]
     486   [ +  -  +  -  :           2 :             value == (int) value && (value == 0 || value == 1))
                   +  + ]
     487                 :           1 :             valid = true;
     488                 :             :     }
     489                 :             : 
     490         [ +  + ]:          22 :     if (!valid)
     491                 :             :     {
     492                 :           4 :         GUC_check_errmsg("EXPLAIN option \"%s\" requires a Boolean value",
     493                 :             :                          option_name);
     494                 :           4 :         return false;
     495                 :             :     }
     496                 :             : 
     497                 :          18 :     return true;
     498                 :             : }
        

Generated by: LCOV version 2.0-1