LCOV - code coverage report
Current view: top level - src/bin/psql - variables.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 126 151 83.4 %
Date: 2017-09-29 13:40:31 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-2017, 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             : 
      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       18009 : valid_variable_name(const char *name)
      23             : {
      24       18009 :     const unsigned char *ptr = (const unsigned char *) name;
      25             : 
      26             :     /* Mustn't be zero-length */
      27       18009 :     if (*ptr == '\0')
      28           0 :         return false;
      29             : 
      30      169714 :     while (*ptr)
      31             :     {
      32      267396 :         if (IS_HIGHBIT_SET(*ptr) ||
      33      133698 :             strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz"
      34      133698 :                    "_0123456789", *ptr) != NULL)
      35      133696 :             ptr++;
      36             :         else
      37           2 :             return false;
      38             :     }
      39             : 
      40       18007 :     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 results of PrintVariables() more pleasing.
      49             :  */
      50             : VariableSpace
      51         184 : CreateVariableSpace(void)
      52             : {
      53             :     struct _variable *ptr;
      54             : 
      55         184 :     ptr = pg_malloc(sizeof *ptr);
      56         184 :     ptr->name = NULL;
      57         184 :     ptr->value = NULL;
      58         184 :     ptr->substitute_hook = NULL;
      59         184 :     ptr->assign_hook = NULL;
      60         184 :     ptr->next = NULL;
      61             : 
      62         184 :     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         106 : GetVariable(VariableSpace space, const char *name)
      72             : {
      73             :     struct _variable *current;
      74             : 
      75         106 :     if (!space)
      76           0 :         return NULL;
      77             : 
      78        1338 :     for (current = space->next; current; current = current->next)
      79             :     {
      80        1338 :         int         cmp = strcmp(current->name, name);
      81             : 
      82        1338 :         if (cmp == 0)
      83             :         {
      84             :             /* this is correct answer when value is NULL, too */
      85          47 :             return current->value;
      86             :         }
      87        1291 :         if (cmp > 0)
      88          59 :             break;              /* it's not there */
      89             :     }
      90             : 
      91          59 :     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        1697 : ParseVariableBool(const char *value, const char *name, bool *result)
     108             : {
     109             :     size_t      len;
     110        1697 :     bool        valid = true;
     111             : 
     112             :     /* Treat "unset" as an empty string, which will lead to error below */
     113        1697 :     if (value == NULL)
     114           0 :         value = "";
     115             : 
     116        1697 :     len = strlen(value);
     117             : 
     118        1697 :     if (len > 0 && pg_strncasecmp(value, "true", len) == 0)
     119          11 :         *result = true;
     120        1686 :     else if (len > 0 && pg_strncasecmp(value, "false", len) == 0)
     121          11 :         *result = false;
     122        1675 :     else if (len > 0 && pg_strncasecmp(value, "yes", len) == 0)
     123           1 :         *result = true;
     124        1674 :     else if (len > 0 && pg_strncasecmp(value, "no", len) == 0)
     125           1 :         *result = false;
     126             :     /* 'o' is not unique enough */
     127        1673 :     else if (pg_strncasecmp(value, "on", (len > 2 ? len : 2)) == 0)
     128         372 :         *result = true;
     129        1301 :     else if (pg_strncasecmp(value, "off", (len > 2 ? len : 2)) == 0)
     130        1296 :         *result = false;
     131           5 :     else if (pg_strcasecmp(value, "1") == 0)
     132           1 :         *result = true;
     133           4 :     else if (pg_strcasecmp(value, "0") == 0)
     134           1 :         *result = false;
     135             :     else
     136             :     {
     137             :         /* string is not recognized; don't clobber *result */
     138           3 :         if (name)
     139           2 :             psql_error("unrecognized value \"%s\" for \"%s\": boolean expected\n",
     140             :                        value, name);
     141           3 :         valid = false;
     142             :     }
     143        1697 :     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         559 : 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         559 :     if (value == NULL)
     163           0 :         value = "";
     164             : 
     165         559 :     errno = 0;
     166         559 :     numval = strtol(value, &end, 0);
     167         559 :     if (errno == 0 && *end == '\0' && end != value && numval == (int) numval)
     168             :     {
     169         558 :         *result = (int) numval;
     170         558 :         return true;
     171             :     }
     172             :     else
     173             :     {
     174             :         /* string is not recognized; don't clobber *result */
     175           1 :         if (name)
     176           1 :             psql_error("invalid value \"%s\" for \"%s\": integer expected\n",
     177             :                        value, name);
     178           1 :         return false;
     179             :     }
     180             : }
     181             : 
     182             : /*
     183             :  * Print values of all variables.
     184             :  */
     185             : void
     186           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       14513 : SetVariable(VariableSpace space, const char *name, const char *value)
     212             : {
     213             :     struct _variable *current,
     214             :                *previous;
     215             : 
     216       14513 :     if (!space || !name)
     217           0 :         return false;
     218             : 
     219       14513 :     if (!valid_variable_name(name))
     220             :     {
     221             :         /* Deletion of non-existent variable is not an error */
     222           2 :         if (!value)
     223           0 :             return true;
     224           2 :         psql_error("invalid variable name: \"%s\"\n", name);
     225           2 :         return false;
     226             :     }
     227             : 
     228      199362 :     for (previous = space, current = space->next;
     229             :          current;
     230      170340 :          previous = current, current = current->next)
     231             :     {
     232      184656 :         int         cmp = strcmp(current->name, name);
     233             : 
     234      184656 :         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       13227 :             char       *new_value = value ? pg_strdup(value) : NULL;
     246             :             bool        confirmed;
     247             : 
     248       13227 :             if (current->substitute_hook)
     249         603 :                 new_value = (*current->substitute_hook) (new_value);
     250             : 
     251       13227 :             if (current->assign_hook)
     252        1155 :                 confirmed = (*current->assign_hook) (new_value);
     253             :             else
     254       12072 :                 confirmed = true;
     255             : 
     256       13227 :             if (confirmed)
     257             :             {
     258       13224 :                 if (current->value)
     259       12672 :                     pg_free(current->value);
     260       13224 :                 current->value = new_value;
     261             : 
     262             :                 /*
     263             :                  * If we deleted the value, and there are no hooks to
     264             :                  * remember, we can discard the variable altogether.
     265             :                  */
     266       13225 :                 if (new_value == NULL &&
     267           2 :                     current->substitute_hook == NULL &&
     268           1 :                     current->assign_hook == NULL)
     269             :                 {
     270           1 :                     previous->next = current->next;
     271           1 :                     free(current->name);
     272           1 :                     free(current);
     273             :                 }
     274             :             }
     275           3 :             else if (new_value)
     276           3 :                 pg_free(new_value); /* current->value is left unchanged */
     277             : 
     278       13227 :             return confirmed;
     279             :         }
     280      171429 :         if (cmp > 0)
     281        1089 :             break;              /* it's not there */
     282             :     }
     283             : 
     284             :     /* not present, make new entry ... unless we were asked to delete */
     285        1284 :     if (value)
     286             :     {
     287        1284 :         current = pg_malloc(sizeof *current);
     288        1284 :         current->name = pg_strdup(name);
     289        1284 :         current->value = pg_strdup(value);
     290        1284 :         current->substitute_hook = NULL;
     291        1284 :         current->assign_hook = NULL;
     292        1284 :         current->next = previous->next;
     293        1284 :         previous->next = current;
     294             :     }
     295        1284 :     return true;
     296             : }
     297             : 
     298             : /*
     299             :  * Attach substitute and/or assign hook functions to the named variable.
     300             :  * If you need only one hook, pass NULL for the other.
     301             :  *
     302             :  * If the variable doesn't already exist, create it with value NULL, just so
     303             :  * we have a place to store the hook function(s).  (The substitute hook might
     304             :  * immediately change the NULL to something else; if not, this state is
     305             :  * externally the same as the variable not being defined.)
     306             :  *
     307             :  * The substitute hook, if given, is immediately called on the variable's
     308             :  * value.  Then the assign hook, if given, is called on the variable's value.
     309             :  * This is meant to let it update any derived psql state.  If the assign hook
     310             :  * doesn't like the current value, it will print a message to that effect,
     311             :  * but we'll ignore it.  Generally we do not expect any such failure here,
     312             :  * because this should get called before any user-supplied value is assigned.
     313             :  */
     314             : void
     315        3496 : SetVariableHooks(VariableSpace space, const char *name,
     316             :                  VariableSubstituteHook shook,
     317             :                  VariableAssignHook ahook)
     318             : {
     319             :     struct _variable *current,
     320             :                *previous;
     321             : 
     322        3496 :     if (!space || !name)
     323           0 :         return;
     324             : 
     325        3496 :     if (!valid_variable_name(name))
     326           0 :         return;
     327             : 
     328       26128 :     for (previous = space, current = space->next;
     329             :          current;
     330       19136 :          previous = current, current = current->next)
     331             :     {
     332       21528 :         int         cmp = strcmp(current->name, name);
     333             : 
     334       21528 :         if (cmp == 0)
     335             :         {
     336             :             /* found entry, so update */
     337           0 :             current->substitute_hook = shook;
     338           0 :             current->assign_hook = ahook;
     339           0 :             if (shook)
     340           0 :                 current->value = (*shook) (current->value);
     341           0 :             if (ahook)
     342           0 :                 (void) (*ahook) (current->value);
     343           0 :             return;
     344             :         }
     345       21528 :         if (cmp > 0)
     346        2392 :             break;              /* it's not there */
     347             :     }
     348             : 
     349             :     /* not present, make new entry */
     350        3496 :     current = pg_malloc(sizeof *current);
     351        3496 :     current->name = pg_strdup(name);
     352        3496 :     current->value = NULL;
     353        3496 :     current->substitute_hook = shook;
     354        3496 :     current->assign_hook = ahook;
     355        3496 :     current->next = previous->next;
     356        3496 :     previous->next = current;
     357        3496 :     if (shook)
     358        2760 :         current->value = (*shook) (current->value);
     359        3496 :     if (ahook)
     360        3496 :         (void) (*ahook) (current->value);
     361             : }
     362             : 
     363             : /*
     364             :  * Convenience function to set a variable's value to "on".
     365             :  */
     366             : bool
     367         363 : SetVariableBool(VariableSpace space, const char *name)
     368             : {
     369         363 :     return SetVariable(space, name, "on");
     370             : }
     371             : 
     372             : /*
     373             :  * Attempt to delete variable.
     374             :  *
     375             :  * If unsuccessful, print a message and return "false".
     376             :  * Deleting a nonexistent variable is not an error.
     377             :  */
     378             : bool
     379           0 : DeleteVariable(VariableSpace space, const char *name)
     380             : {
     381           0 :     return SetVariable(space, name, NULL);
     382             : }
     383             : 
     384             : /*
     385             :  * Emit error with suggestions for variables or commands
     386             :  * accepting enum-style arguments.
     387             :  * This function just exists to standardize the wording.
     388             :  * suggestions should follow the format "fee, fi, fo, fum".
     389             :  */
     390             : void
     391           1 : PsqlVarEnumError(const char *name, const char *value, const char *suggestions)
     392             : {
     393           1 :     psql_error("unrecognized value \"%s\" for \"%s\"\nAvailable values are: %s.\n",
     394             :                value, name, suggestions);
     395           1 : }

Generated by: LCOV version 1.11