LCOV - code coverage report
Current view: top level - src/bin/psql - variables.c (source / functions) Hit Total Coverage
Test: PostgreSQL 14devel Lines: 135 160 84.4 %
Date: 2020-11-27 12:05:55 Functions: 10 12 83.3 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * psql - the PostgreSQL interactive terminal
       3             :  *
       4             :  * Copyright (c) 2000-2020, 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      820228 : valid_variable_name(const char *name)
      23             : {
      24      820228 :     const unsigned char *ptr = (const unsigned char *) name;
      25             : 
      26             :     /* Mustn't be zero-length */
      27      820228 :     if (*ptr == '\0')
      28           0 :         return false;
      29             : 
      30     7759236 :     while (*ptr)
      31             :     {
      32     6939016 :         if (IS_HIGHBIT_SET(*ptr) ||
      33     6939016 :             strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz"
      34     6939016 :                    "_0123456789", *ptr) != NULL)
      35     6939008 :             ptr++;
      36             :         else
      37           8 :             return false;
      38             :     }
      39             : 
      40      820220 :     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        5588 : CreateVariableSpace(void)
      52             : {
      53             :     struct _variable *ptr;
      54             : 
      55        5588 :     ptr = pg_malloc(sizeof *ptr);
      56        5588 :     ptr->name = NULL;
      57        5588 :     ptr->value = NULL;
      58        5588 :     ptr->substitute_hook = NULL;
      59        5588 :     ptr->assign_hook = NULL;
      60        5588 :     ptr->next = NULL;
      61             : 
      62        5588 :     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         902 : GetVariable(VariableSpace space, const char *name)
      72             : {
      73             :     struct _variable *current;
      74             : 
      75         902 :     if (!space)
      76           0 :         return NULL;
      77             : 
      78       20998 :     for (current = space->next; current; current = current->next)
      79             :     {
      80       20998 :         int         cmp = strcmp(current->name, name);
      81             : 
      82       20998 :         if (cmp == 0)
      83             :         {
      84             :             /* this is correct answer when value is NULL, too */
      85         634 :             return current->value;
      86             :         }
      87       20364 :         if (cmp > 0)
      88         268 :             break;              /* it's not there */
      89             :     }
      90             : 
      91         268 :     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       58596 : ParseVariableBool(const char *value, const char *name, bool *result)
     108             : {
     109             :     size_t      len;
     110       58596 :     bool        valid = true;
     111             : 
     112             :     /* Treat "unset" as an empty string, which will lead to error below */
     113       58596 :     if (value == NULL)
     114           0 :         value = "";
     115             : 
     116       58596 :     len = strlen(value);
     117             : 
     118       58596 :     if (len > 0 && pg_strncasecmp(value, "true", len) == 0)
     119         136 :         *result = true;
     120       58460 :     else if (len > 0 && pg_strncasecmp(value, "false", len) == 0)
     121         140 :         *result = false;
     122       58320 :     else if (len > 0 && pg_strncasecmp(value, "yes", len) == 0)
     123           4 :         *result = true;
     124       58316 :     else if (len > 0 && pg_strncasecmp(value, "no", len) == 0)
     125           4 :         *result = false;
     126             :     /* 'o' is not unique enough */
     127       58312 :     else if (pg_strncasecmp(value, "on", (len > 2 ? len : 2)) == 0)
     128       11002 :         *result = true;
     129       47310 :     else if (pg_strncasecmp(value, "off", (len > 2 ? len : 2)) == 0)
     130       44816 :         *result = false;
     131        2494 :     else if (pg_strcasecmp(value, "1") == 0)
     132        2478 :         *result = true;
     133          16 :     else if (pg_strcasecmp(value, "0") == 0)
     134           4 :         *result = false;
     135             :     else
     136             :     {
     137             :         /* string is not recognized; don't clobber *result */
     138          12 :         if (name)
     139           8 :             pg_log_error("unrecognized value \"%s\" for \"%s\": Boolean expected",
     140             :                          value, name);
     141          12 :         valid = false;
     142             :     }
     143       58596 :     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       16800 : 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       16800 :     if (value == NULL)
     163           0 :         value = "";
     164             : 
     165       16800 :     errno = 0;
     166       16800 :     numval = strtol(value, &end, 0);
     167       16800 :     if (errno == 0 && *end == '\0' && end != value && numval == (int) numval)
     168             :     {
     169       16796 :         *result = (int) numval;
     170       16796 :         return true;
     171             :     }
     172             :     else
     173             :     {
     174             :         /* string is not recognized; don't clobber *result */
     175           4 :         if (name)
     176           4 :             pg_log_error("invalid value \"%s\" for \"%s\": integer expected",
     177             :                          value, name);
     178           4 :         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      708468 : SetVariable(VariableSpace space, const char *name, const char *value)
     212             : {
     213             :     struct _variable *current,
     214             :                *previous;
     215             : 
     216      708468 :     if (!space || !name)
     217           0 :         return false;
     218             : 
     219      708468 :     if (!valid_variable_name(name))
     220             :     {
     221             :         /* Deletion of non-existent variable is not an error */
     222           8 :         if (!value)
     223           0 :             return true;
     224           8 :         pg_log_error("invalid variable name: \"%s\"", name);
     225           8 :         return false;
     226             :     }
     227             : 
     228    13626676 :     for (previous = space, current = space->next;
     229             :          current;
     230    12918216 :          previous = current, current = current->next)
     231             :     {
     232    13609792 :         int         cmp = strcmp(current->name, name);
     233             : 
     234    13609792 :         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      625568 :             char       *new_value = value ? pg_strdup(value) : NULL;
     246             :             bool        confirmed;
     247             : 
     248      625568 :             if (current->substitute_hook)
     249       14858 :                 new_value = current->substitute_hook(new_value);
     250             : 
     251      625568 :             if (current->assign_hook)
     252       31622 :                 confirmed = current->assign_hook(new_value);
     253             :             else
     254      593946 :                 confirmed = true;
     255             : 
     256      625568 :             if (confirmed)
     257             :             {
     258      625556 :                 if (current->value)
     259      608792 :                     pg_free(current->value);
     260      625556 :                 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      625556 :                 if (new_value == NULL &&
     267           4 :                     current->substitute_hook == NULL &&
     268           4 :                     current->assign_hook == NULL)
     269             :                 {
     270           4 :                     previous->next = current->next;
     271           4 :                     free(current->name);
     272           4 :                     free(current);
     273             :                 }
     274             :             }
     275          12 :             else if (new_value)
     276          12 :                 pg_free(new_value); /* current->value is left unchanged */
     277             : 
     278      625568 :             return confirmed;
     279             :         }
     280    12984224 :         if (cmp > 0)
     281       66008 :             break;              /* it's not there */
     282             :     }
     283             : 
     284             :     /* not present, make new entry ... unless we were asked to delete */
     285       82892 :     if (value)
     286             :     {
     287       82892 :         current = pg_malloc(sizeof *current);
     288       82892 :         current->name = pg_strdup(name);
     289       82892 :         current->value = pg_strdup(value);
     290       82892 :         current->substitute_hook = NULL;
     291       82892 :         current->assign_hook = NULL;
     292       82892 :         current->next = previous->next;
     293       82892 :         previous->next = current;
     294             :     }
     295       82892 :     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      111760 : SetVariableHooks(VariableSpace space, const char *name,
     316             :                  VariableSubstituteHook shook,
     317             :                  VariableAssignHook ahook)
     318             : {
     319             :     struct _variable *current,
     320             :                *previous;
     321             : 
     322      111760 :     if (!space || !name)
     323           0 :         return;
     324             : 
     325      111760 :     if (!valid_variable_name(name))
     326           0 :         return;
     327             : 
     328      720852 :     for (previous = space, current = space->next;
     329             :          current;
     330      609092 :          previous = current, current = current->next)
     331             :     {
     332      687324 :         int         cmp = strcmp(current->name, name);
     333             : 
     334      687324 :         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      687324 :         if (cmp > 0)
     346       78232 :             break;              /* it's not there */
     347             :     }
     348             : 
     349             :     /* not present, make new entry */
     350      111760 :     current = pg_malloc(sizeof *current);
     351      111760 :     current->name = pg_strdup(name);
     352      111760 :     current->value = NULL;
     353      111760 :     current->substitute_hook = shook;
     354      111760 :     current->assign_hook = ahook;
     355      111760 :     current->next = previous->next;
     356      111760 :     previous->next = current;
     357      111760 :     if (shook)
     358       89408 :         current->value = (*shook) (current->value);
     359      111760 :     if (ahook)
     360      111760 :         (void) (*ahook) (current->value);
     361             : }
     362             : 
     363             : /*
     364             :  * Return true iff the named variable has substitute and/or assign hook
     365             :  * functions.
     366             :  */
     367             : bool
     368         188 : VariableHasHook(VariableSpace space, const char *name)
     369             : {
     370             :     struct _variable *current;
     371             : 
     372             :     Assert(space);
     373             :     Assert(name);
     374             : 
     375        7014 :     for (current = space->next; current; current = current->next)
     376             :     {
     377        6914 :         int         cmp = strcmp(current->name, name);
     378             : 
     379        6914 :         if (cmp == 0)
     380         104 :             return (current->substitute_hook != NULL ||
     381          50 :                     current->assign_hook != NULL);
     382        6860 :         if (cmp > 0)
     383          34 :             break;              /* it's not there */
     384             :     }
     385             : 
     386         134 :     return false;
     387             : }
     388             : 
     389             : /*
     390             :  * Convenience function to set a variable's value to "on".
     391             :  */
     392             : bool
     393        9590 : SetVariableBool(VariableSpace space, const char *name)
     394             : {
     395        9590 :     return SetVariable(space, name, "on");
     396             : }
     397             : 
     398             : /*
     399             :  * Attempt to delete variable.
     400             :  *
     401             :  * If unsuccessful, print a message and return "false".
     402             :  * Deleting a nonexistent variable is not an error.
     403             :  */
     404             : bool
     405           0 : DeleteVariable(VariableSpace space, const char *name)
     406             : {
     407           0 :     return SetVariable(space, name, NULL);
     408             : }
     409             : 
     410             : /*
     411             :  * Emit error with suggestions for variables or commands
     412             :  * accepting enum-style arguments.
     413             :  * This function just exists to standardize the wording.
     414             :  * suggestions should follow the format "fee, fi, fo, fum".
     415             :  */
     416             : void
     417           4 : PsqlVarEnumError(const char *name, const char *value, const char *suggestions)
     418             : {
     419           4 :     pg_log_error("unrecognized value \"%s\" for \"%s\"\n"
     420             :                  "Available values are: %s.",
     421             :                  value, name, suggestions);
     422           4 : }

Generated by: LCOV version 1.13