LCOV - differential code coverage report
Current view: top level - src/bin/psql - variables.c (source / functions) Coverage Total Hit UIC UBC GBC GIC GNC CBC EUB ECB DCB
Current: Differential Code Coverage HEAD vs 15 Lines: 84.6 % 162 137 5 20 2 26 1 108 3 26 3
Current Date: 2023-04-08 15:15:32 Functions: 83.3 % 12 10 1 1 4 1 5 1 4
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

           TLA  Line data    Source code
       1                 : /*
       2                 :  * psql - the PostgreSQL interactive terminal
       3                 :  *
       4                 :  * Copyright (c) 2000-2023, PostgreSQL Global Development Group
       5                 :  *
       6                 :  * src/bin/psql/variables.c
       7                 :  */
       8                 : #include "postgres_fe.h"
       9                 : 
      10                 : #include "common.h"
      11                 : #include "common/logging.h"
      12                 : #include "variables.h"
      13                 : 
      14                 : /*
      15                 :  * Check whether a variable's name is allowed.
      16                 :  *
      17                 :  * We allow any non-ASCII character, as well as ASCII letters, digits, and
      18                 :  * underscore.  Keep this in sync with the definition of variable_char in
      19                 :  * psqlscan.l and psqlscanslash.l.
      20                 :  */
      21                 : static bool
      22 CBC      810453 : valid_variable_name(const char *name)
      23                 : {
      24          810453 :     const unsigned char *ptr = (const unsigned char *) name;
      25                 : 
      26                 :     /* Mustn't be zero-length */
      27          810453 :     if (*ptr == '\0')
      28 UBC           0 :         return false;
      29                 : 
      30 CBC     7958925 :     while (*ptr)
      31                 :     {
      32         7148478 :         if (IS_HIGHBIT_SET(*ptr) ||
      33         7148478 :             strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz"
      34         7148478 :                    "_0123456789", *ptr) != NULL)
      35         7148472 :             ptr++;
      36                 :         else
      37               6 :             return false;
      38                 :     }
      39                 : 
      40          810447 :     return true;
      41                 : }
      42                 : 
      43                 : /*
      44                 :  * A "variable space" is represented by an otherwise-unused struct _variable
      45                 :  * that serves as list header.
      46                 :  *
      47                 :  * The list entries are kept in name order (according to strcmp).  This
      48                 :  * is mainly to make the output of PrintVariables() more pleasing.
      49                 :  */
      50                 : VariableSpace
      51            6478 : CreateVariableSpace(void)
      52                 : {
      53                 :     struct _variable *ptr;
      54                 : 
      55            6478 :     ptr = pg_malloc(sizeof *ptr);
      56            6478 :     ptr->name = NULL;
      57            6478 :     ptr->value = NULL;
      58            6478 :     ptr->substitute_hook = NULL;
      59            6478 :     ptr->assign_hook = NULL;
      60            6478 :     ptr->next = NULL;
      61                 : 
      62            6478 :     return ptr;
      63                 : }
      64                 : 
      65                 : /*
      66                 :  * Get string value of variable, or NULL if it's not defined.
      67                 :  *
      68                 :  * Note: result is valid until variable is next assigned to.
      69                 :  */
      70                 : const char *
      71            1669 : GetVariable(VariableSpace space, const char *name)
      72                 : {
      73                 :     struct _variable *current;
      74                 : 
      75            1669 :     if (!space)
      76 UBC           0 :         return NULL;
      77                 : 
      78 CBC       57148 :     for (current = space->next; current; current = current->next)
      79                 :     {
      80           57147 :         int         cmp = strcmp(current->name, name);
      81                 : 
      82           57147 :         if (cmp == 0)
      83                 :         {
      84                 :             /* this is correct answer when value is NULL, too */
      85            1449 :             return current->value;
      86                 :         }
      87           55698 :         if (cmp > 0)
      88             219 :             break;              /* it's not there */
      89                 :     }
      90                 : 
      91             220 :     return NULL;
      92                 : }
      93                 : 
      94                 : /*
      95                 :  * Try to interpret "value" as a boolean value, and if successful,
      96                 :  * store it in *result.  Otherwise don't clobber *result.
      97                 :  *
      98                 :  * Valid values are: true, false, yes, no, on, off, 1, 0; as well as unique
      99                 :  * prefixes thereof.
     100                 :  *
     101                 :  * "name" is the name of the variable we're assigning to, to use in error
     102                 :  * report if any.  Pass name == NULL to suppress the error report.
     103                 :  *
     104                 :  * Return true when "value" is syntactically valid, false otherwise.
     105                 :  */
     106                 : bool
     107           89150 : ParseVariableBool(const char *value, const char *name, bool *result)
     108                 : {
     109                 :     size_t      len;
     110           89150 :     bool        valid = true;
     111                 : 
     112                 :     /* Treat "unset" as an empty string, which will lead to error below */
     113           89150 :     if (value == NULL)
     114 UBC           0 :         value = "";
     115                 : 
     116 CBC       89150 :     len = strlen(value);
     117                 : 
     118           89150 :     if (len > 0 && pg_strncasecmp(value, "true", len) == 0)
     119             106 :         *result = true;
     120           89044 :     else if (len > 0 && pg_strncasecmp(value, "false", len) == 0)
     121             119 :         *result = false;
     122           88925 :     else if (len > 0 && pg_strncasecmp(value, "yes", len) == 0)
     123               3 :         *result = true;
     124           88922 :     else if (len > 0 && pg_strncasecmp(value, "no", len) == 0)
     125               3 :         *result = false;
     126                 :     /* 'o' is not unique enough */
     127           88919 :     else if (pg_strncasecmp(value, "on", (len > 2 ? len : 2)) == 0)
     128           20161 :         *result = true;
     129           68758 :     else if (pg_strncasecmp(value, "off", (len > 2 ? len : 2)) == 0)
     130           64880 :         *result = false;
     131            3878 :     else if (pg_strcasecmp(value, "1") == 0)
     132            3866 :         *result = true;
     133              12 :     else if (pg_strcasecmp(value, "0") == 0)
     134               3 :         *result = false;
     135                 :     else
     136                 :     {
     137                 :         /* string is not recognized; don't clobber *result */
     138               9 :         if (name)
     139               6 :             pg_log_error("unrecognized value \"%s\" for \"%s\": Boolean expected",
     140                 :                          value, name);
     141               9 :         valid = false;
     142                 :     }
     143           89150 :     return valid;
     144                 : }
     145                 : 
     146                 : /*
     147                 :  * Try to interpret "value" as an integer value, and if successful,
     148                 :  * store it in *result.  Otherwise don't clobber *result.
     149                 :  *
     150                 :  * "name" is the name of the variable we're assigning to, to use in error
     151                 :  * report if any.  Pass name == NULL to suppress the error report.
     152                 :  *
     153                 :  * Return true when "value" is syntactically valid, false otherwise.
     154                 :  */
     155                 : bool
     156           19462 : ParseVariableNum(const char *value, const char *name, int *result)
     157                 : {
     158                 :     char       *end;
     159                 :     long        numval;
     160                 : 
     161                 :     /* Treat "unset" as an empty string, which will lead to error below */
     162           19462 :     if (value == NULL)
     163 UBC           0 :         value = "";
     164                 : 
     165 CBC       19462 :     errno = 0;
     166           19462 :     numval = strtol(value, &end, 0);
     167           19462 :     if (errno == 0 && *end == '\0' && end != value && numval == (int) numval)
     168                 :     {
     169           19459 :         *result = (int) numval;
     170           19459 :         return true;
     171                 :     }
     172                 :     else
     173                 :     {
     174                 :         /* string is not recognized; don't clobber *result */
     175               3 :         if (name)
     176               3 :             pg_log_error("invalid value \"%s\" for \"%s\": integer expected",
     177                 :                          value, name);
     178               3 :         return false;
     179                 :     }
     180                 : }
     181                 : 
     182                 : /*
     183                 :  * Print values of all variables.
     184                 :  */
     185                 : void
     186 UBC           0 : PrintVariables(VariableSpace space)
     187                 : {
     188                 :     struct _variable *ptr;
     189                 : 
     190               0 :     if (!space)
     191               0 :         return;
     192                 : 
     193               0 :     for (ptr = space->next; ptr; ptr = ptr->next)
     194                 :     {
     195               0 :         if (ptr->value)
     196               0 :             printf("%s = '%s'\n", ptr->name, ptr->value);
     197               0 :         if (cancel_pressed)
     198               0 :             break;
     199                 :     }
     200                 : }
     201                 : 
     202                 : /*
     203                 :  * Set the variable named "name" to value "value",
     204                 :  * or delete it if "value" is NULL.
     205                 :  *
     206                 :  * Returns true if successful, false if not; in the latter case a suitable
     207                 :  * error message has been printed, except for the unexpected case of
     208                 :  * space or name being NULL.
     209                 :  */
     210                 : bool
     211 CBC      667937 : SetVariable(VariableSpace space, const char *name, const char *value)
     212                 : {
     213                 :     struct _variable *current,
     214                 :                *previous;
     215                 : 
     216          667937 :     if (!space || !name)
     217 UBC           0 :         return false;
     218                 : 
     219 CBC      667937 :     if (!valid_variable_name(name))
     220                 :     {
     221                 :         /* Deletion of non-existent variable is not an error */
     222               6 :         if (!value)
     223 UBC           0 :             return true;
     224 CBC           6 :         pg_log_error("invalid variable name: \"%s\"", name);
     225               6 :         return false;
     226                 :     }
     227                 : 
     228          667931 :     for (previous = space, current = space->next;
     229        13409358 :          current;
     230        12741427 :          previous = current, current = current->next)
     231                 :     {
     232        13389629 :         int         cmp = strcmp(current->name, name);
     233                 : 
     234        13389629 :         if (cmp == 0)
     235                 :         {
     236                 :             /*
     237                 :              * Found entry, so update, unless assign hook returns false.
     238                 :              *
     239                 :              * We must duplicate the passed value to start with.  This
     240                 :              * simplifies the API for substitute hooks.  Moreover, some assign
     241                 :              * hooks assume that the passed value has the same lifespan as the
     242                 :              * variable.  Having to free the string again on failure is a
     243                 :              * small price to pay for keeping these APIs simple.
     244                 :              */
     245          570538 :             char       *new_value = value ? pg_strdup(value) : NULL;
     246                 :             bool        confirmed;
     247                 : 
     248          570538 :             if (current->substitute_hook)
     249           25081 :                 new_value = current->substitute_hook(new_value);
     250                 : 
     251          570538 :             if (current->assign_hook)
     252           44515 :                 confirmed = current->assign_hook(new_value);
     253                 :             else
     254          526023 :                 confirmed = true;
     255                 : 
     256          570538 :             if (confirmed)
     257                 :             {
     258 GNC      570529 :                 pg_free(current->value);
     259 GIC      570529 :                 current->value = new_value;
     260                 : 
     261                 :                 /*
     262                 :                  * If we deleted the value, and there are no hooks to
     263                 :                  * remember, we can discard the variable altogether.
     264 ECB             :                  */
     265 CBC      570529 :                 if (new_value == NULL &&
     266               3 :                     current->substitute_hook == NULL &&
     267 GIC           3 :                     current->assign_hook == NULL)
     268 ECB             :                 {
     269 CBC           3 :                     previous->next = current->next;
     270               3 :                     free(current->name);
     271 GIC           3 :                     free(current);
     272                 :                 }
     273                 :             }
     274                 :             else
     275               9 :                 pg_free(new_value); /* current->value is left unchanged */
     276 ECB             : 
     277 GIC      570538 :             return confirmed;
     278 ECB             :         }
     279 CBC    12819091 :         if (cmp > 0)
     280 GIC       77664 :             break;              /* it's not there */
     281                 :     }
     282                 : 
     283 ECB             :     /* not present, make new entry ... unless we were asked to delete */
     284 GIC       97393 :     if (value)
     285 ECB             :     {
     286 CBC       97393 :         current = pg_malloc(sizeof *current);
     287           97393 :         current->name = pg_strdup(name);
     288           97393 :         current->value = pg_strdup(value);
     289           97393 :         current->substitute_hook = NULL;
     290           97393 :         current->assign_hook = NULL;
     291           97393 :         current->next = previous->next;
     292 GIC       97393 :         previous->next = current;
     293 ECB             :     }
     294 GIC       97393 :     return true;
     295                 : }
     296                 : 
     297                 : /*
     298                 :  * Attach substitute and/or assign hook functions to the named variable.
     299                 :  * If you need only one hook, pass NULL for the other.
     300                 :  *
     301                 :  * If the variable doesn't already exist, create it with value NULL, just so
     302                 :  * we have a place to store the hook function(s).  (The substitute hook might
     303                 :  * immediately change the NULL to something else; if not, this state is
     304                 :  * externally the same as the variable not being defined.)
     305                 :  *
     306                 :  * The substitute hook, if given, is immediately called on the variable's
     307                 :  * value.  Then the assign hook, if given, is called on the variable's value.
     308                 :  * This is meant to let it update any derived psql state.  If the assign hook
     309                 :  * doesn't like the current value, it will print a message to that effect,
     310                 :  * but we'll ignore it.  Generally we do not expect any such failure here,
     311                 :  * because this should get called before any user-supplied value is assigned.
     312                 :  */
     313 ECB             : void
     314 GIC      142516 : SetVariableHooks(VariableSpace space, const char *name,
     315                 :                  VariableSubstituteHook shook,
     316                 :                  VariableAssignHook ahook)
     317                 : {
     318                 :     struct _variable *current,
     319                 :                *previous;
     320 ECB             : 
     321 GBC      142516 :     if (!space || !name)
     322 UIC           0 :         return;
     323 ECB             : 
     324 GBC      142516 :     if (!valid_variable_name(name))
     325 UIC           0 :         return;
     326 ECB             : 
     327 CBC      142516 :     for (previous = space, current = space->next;
     328          984656 :          current;
     329 GIC      842140 :          previous = current, current = current->next)
     330 ECB             :     {
     331 GIC      945788 :         int         cmp = strcmp(current->name, name);
     332 ECB             : 
     333 GIC      945788 :         if (cmp == 0)
     334                 :         {
     335 EUB             :             /* found entry, so update */
     336 UBC           0 :             current->substitute_hook = shook;
     337               0 :             current->assign_hook = ahook;
     338               0 :             if (shook)
     339               0 :                 current->value = (*shook) (current->value);
     340               0 :             if (ahook)
     341               0 :                 (void) (*ahook) (current->value);
     342 UIC           0 :             return;
     343 ECB             :         }
     344 CBC      945788 :         if (cmp > 0)
     345 GIC      103648 :             break;              /* it's not there */
     346                 :     }
     347                 : 
     348 ECB             :     /* not present, make new entry */
     349 CBC      142516 :     current = pg_malloc(sizeof *current);
     350          142516 :     current->name = pg_strdup(name);
     351          142516 :     current->value = NULL;
     352          142516 :     current->substitute_hook = shook;
     353          142516 :     current->assign_hook = ahook;
     354          142516 :     current->next = previous->next;
     355          142516 :     previous->next = current;
     356          142516 :     if (shook)
     357          116604 :         current->value = (*shook) (current->value);
     358          142516 :     if (ahook)
     359 GIC      142516 :         (void) (*ahook) (current->value);
     360                 : }
     361                 : 
     362                 : /*
     363                 :  * Return true iff the named variable has substitute and/or assign hook
     364                 :  * functions.
     365                 :  */
     366 ECB             : bool
     367 GIC         357 : VariableHasHook(VariableSpace space, const char *name)
     368                 : {
     369                 :     struct _variable *current;
     370 ECB             : 
     371 CBC         357 :     Assert(space);
     372 GIC         357 :     Assert(name);
     373 ECB             : 
     374 GIC       15438 :     for (current = space->next; current; current = current->next)
     375 ECB             :     {
     376 GIC       15307 :         int         cmp = strcmp(current->name, name);
     377 ECB             : 
     378 CBC       15307 :         if (cmp == 0)
     379             143 :             return (current->substitute_hook != NULL ||
     380              70 :                     current->assign_hook != NULL);
     381           15234 :         if (cmp > 0)
     382 GIC         153 :             break;              /* it's not there */
     383                 :     }
     384 ECB             : 
     385 GIC         284 :     return false;
     386                 : }
     387                 : 
     388                 : /*
     389                 :  * Convenience function to set a variable's value to "on".
     390                 :  */
     391 ECB             : bool
     392 GIC       18223 : SetVariableBool(VariableSpace space, const char *name)
     393 ECB             : {
     394 GIC       18223 :     return SetVariable(space, name, "on");
     395                 : }
     396                 : 
     397                 : /*
     398                 :  * Attempt to delete variable.
     399                 :  *
     400                 :  * If unsuccessful, print a message and return "false".
     401                 :  * Deleting a nonexistent variable is not an error.
     402                 :  */
     403 EUB             : bool
     404 UIC           0 : DeleteVariable(VariableSpace space, const char *name)
     405 EUB             : {
     406 UIC           0 :     return SetVariable(space, name, NULL);
     407                 : }
     408                 : 
     409                 : /*
     410                 :  * Emit error with suggestions for variables or commands
     411                 :  * accepting enum-style arguments.
     412                 :  * This function just exists to standardize the wording.
     413                 :  * suggestions should follow the format "fee, fi, fo, fum".
     414                 :  */
     415 ECB             : void
     416 GIC           3 : PsqlVarEnumError(const char *name, const char *value, const char *suggestions)
     417 ECB             : {
     418 GIC           3 :     pg_log_error("unrecognized value \"%s\" for \"%s\"\n"
     419                 :                  "Available values are: %s.",
     420 ECB             :                  value, name, suggestions);
     421 GIC           3 : }
        

Generated by: LCOV version v1.16-55-g56c0a2a