LCOV - code coverage report
Current view: top level - src/bin/psql - variables.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13devel Lines: 126 151 83.4 %
Date: 2019-09-22 08:06:49 Functions: 9 11 81.8 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13