LCOV - code coverage report
Current view: top level - src/bin/psql - variables.c (source / functions) Hit Total Coverage
Test: PostgreSQL 12beta2 Lines: 126 151 83.4 %
Date: 2019-06-18 07:06:57 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      697400 : valid_variable_name(const char *name)
      25             : {
      26      697400 :     const unsigned char *ptr = (const unsigned char *) name;
      27             : 
      28             :     /* Mustn't be zero-length */
      29      697400 :     if (*ptr == '\0')
      30           0 :         return false;
      31             : 
      32     7243848 :     while (*ptr)
      33             :     {
      34    11698112 :         if (IS_HIGHBIT_SET(*ptr) ||
      35     5849056 :             strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz"
      36     5849056 :                    "_0123456789", *ptr) != NULL)
      37     5849048 :             ptr++;
      38             :         else
      39           8 :             return false;
      40             :     }
      41             : 
      42      697392 :     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        3972 : CreateVariableSpace(void)
      54             : {
      55             :     struct _variable *ptr;
      56             : 
      57        3972 :     ptr = pg_malloc(sizeof *ptr);
      58        3972 :     ptr->name = NULL;
      59        3972 :     ptr->value = NULL;
      60        3972 :     ptr->substitute_hook = NULL;
      61        3972 :     ptr->assign_hook = NULL;
      62        3972 :     ptr->next = NULL;
      63             : 
      64        3972 :     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         758 : GetVariable(VariableSpace space, const char *name)
      74             : {
      75             :     struct _variable *current;
      76             : 
      77         758 :     if (!space)
      78           0 :         return NULL;
      79             : 
      80       16654 :     for (current = space->next; current; current = current->next)
      81             :     {
      82       16654 :         int         cmp = strcmp(current->name, name);
      83             : 
      84       16654 :         if (cmp == 0)
      85             :         {
      86             :             /* this is correct answer when value is NULL, too */
      87         514 :             return current->value;
      88             :         }
      89       16140 :         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       41612 : ParseVariableBool(const char *value, const char *name, bool *result)
     110             : {
     111             :     size_t      len;
     112       41612 :     bool        valid = true;
     113             : 
     114             :     /* Treat "unset" as an empty string, which will lead to error below */
     115       41612 :     if (value == NULL)
     116           0 :         value = "";
     117             : 
     118       41612 :     len = strlen(value);
     119             : 
     120       41612 :     if (len > 0 && pg_strncasecmp(value, "true", len) == 0)
     121         124 :         *result = true;
     122       41488 :     else if (len > 0 && pg_strncasecmp(value, "false", len) == 0)
     123         124 :         *result = false;
     124       41364 :     else if (len > 0 && pg_strncasecmp(value, "yes", len) == 0)
     125           4 :         *result = true;
     126       41360 :     else if (len > 0 && pg_strncasecmp(value, "no", len) == 0)
     127           4 :         *result = false;
     128             :     /* 'o' is not unique enough */
     129       41356 :     else if (pg_strncasecmp(value, "on", (len > 2 ? len : 2)) == 0)
     130        8082 :         *result = true;
     131       33274 :     else if (pg_strncasecmp(value, "off", (len > 2 ? len : 2)) == 0)
     132       31884 :         *result = false;
     133        1390 :     else if (pg_strcasecmp(value, "1") == 0)
     134        1374 :         *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       41612 :     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       11952 : 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       11952 :     if (value == NULL)
     165           0 :         value = "";
     166             : 
     167       11952 :     errno = 0;
     168       11952 :     numval = strtol(value, &end, 0);
     169       11952 :     if (errno == 0 && *end == '\0' && end != value && numval == (int) numval)
     170             :     {
     171       11948 :         *result = (int) numval;
     172       11948 :         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      617960 : SetVariable(VariableSpace space, const char *name, const char *value)
     214             : {
     215             :     struct _variable *current,
     216             :                *previous;
     217             : 
     218      617960 :     if (!space || !name)
     219           0 :         return false;
     220             : 
     221      617960 :     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    12568768 :     for (previous = space, current = space->next;
     231             :          current;
     232    11332864 :          previous = current, current = current->next)
     233             :     {
     234    11938832 :         int         cmp = strcmp(current->name, name);
     235             : 
     236    11938832 :         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      559596 :             char       *new_value = value ? pg_strdup(value) : NULL;
     248             :             bool        confirmed;
     249             : 
     250      559596 :             if (current->substitute_hook)
     251       10764 :                 new_value = current->substitute_hook(new_value);
     252             : 
     253      559596 :             if (current->assign_hook)
     254       22680 :                 confirmed = current->assign_hook(new_value);
     255             :             else
     256      536916 :                 confirmed = true;
     257             : 
     258      559596 :             if (confirmed)
     259             :             {
     260      559584 :                 if (current->value)
     261      547668 :                     pg_free(current->value);
     262      559584 :                 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      559588 :                 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      559596 :             return confirmed;
     281             :         }
     282    11379236 :         if (cmp > 0)
     283       46372 :             break;              /* it's not there */
     284             :     }
     285             : 
     286             :     /* not present, make new entry ... unless we were asked to delete */
     287       58356 :     if (value)
     288             :     {
     289       58356 :         current = pg_malloc(sizeof *current);
     290       58356 :         current->name = pg_strdup(name);
     291       58356 :         current->value = pg_strdup(value);
     292       58356 :         current->substitute_hook = NULL;
     293       58356 :         current->assign_hook = NULL;
     294       58356 :         current->next = previous->next;
     295       58356 :         previous->next = current;
     296             :     }
     297       58356 :     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       79440 : SetVariableHooks(VariableSpace space, const char *name,
     318             :                  VariableSubstituteHook shook,
     319             :                  VariableAssignHook ahook)
     320             : {
     321             :     struct _variable *current,
     322             :                *previous;
     323             : 
     324       79440 :     if (!space || !name)
     325           0 :         return;
     326             : 
     327       79440 :     if (!valid_variable_name(name))
     328           0 :         return;
     329             : 
     330      591828 :     for (previous = space, current = space->next;
     331             :          current;
     332      432948 :          previous = current, current = current->next)
     333             :     {
     334      488556 :         int         cmp = strcmp(current->name, name);
     335             : 
     336      488556 :         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      488556 :         if (cmp > 0)
     348       55608 :             break;              /* it's not there */
     349             :     }
     350             : 
     351             :     /* not present, make new entry */
     352       79440 :     current = pg_malloc(sizeof *current);
     353       79440 :     current->name = pg_strdup(name);
     354       79440 :     current->value = NULL;
     355       79440 :     current->substitute_hook = shook;
     356       79440 :     current->assign_hook = ahook;
     357       79440 :     current->next = previous->next;
     358       79440 :     previous->next = current;
     359       79440 :     if (shook)
     360       63552 :         current->value = (*shook) (current->value);
     361       79440 :     if (ahook)
     362       79440 :         (void) (*ahook) (current->value);
     363             : }
     364             : 
     365             : /*
     366             :  * Convenience function to set a variable's value to "on".
     367             :  */
     368             : bool
     369        6732 : SetVariableBool(VariableSpace space, const char *name)
     370             : {
     371        6732 :     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