LCOV - code coverage report
Current view: top level - src/bin/psql - variables.c (source / functions) Hit Total Coverage
Test: PostgreSQL 15devel Lines: 135 160 84.4 %
Date: 2021-12-04 23:09:10 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-2021, 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      999222 : valid_variable_name(const char *name)
      23             : {
      24      999222 :     const unsigned char *ptr = (const unsigned char *) name;
      25             : 
      26             :     /* Mustn't be zero-length */
      27      999222 :     if (*ptr == '\0')
      28           0 :         return false;
      29             : 
      30     9686812 :     while (*ptr)
      31             :     {
      32     8687598 :         if (IS_HIGHBIT_SET(*ptr) ||
      33     8687598 :             strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz"
      34     8687598 :                    "_0123456789", *ptr) != NULL)
      35     8687590 :             ptr++;
      36             :         else
      37           8 :             return false;
      38             :     }
      39             : 
      40      999214 :     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        8250 : CreateVariableSpace(void)
      52             : {
      53             :     struct _variable *ptr;
      54             : 
      55        8250 :     ptr = pg_malloc(sizeof *ptr);
      56        8250 :     ptr->name = NULL;
      57        8250 :     ptr->value = NULL;
      58        8250 :     ptr->substitute_hook = NULL;
      59        8250 :     ptr->assign_hook = NULL;
      60        8250 :     ptr->next = NULL;
      61             : 
      62        8250 :     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         938 : GetVariable(VariableSpace space, const char *name)
      72             : {
      73             :     struct _variable *current;
      74             : 
      75         938 :     if (!space)
      76           0 :         return NULL;
      77             : 
      78       22540 :     for (current = space->next; current; current = current->next)
      79             :     {
      80       22534 :         int         cmp = strcmp(current->name, name);
      81             : 
      82       22534 :         if (cmp == 0)
      83             :         {
      84             :             /* this is correct answer when value is NULL, too */
      85         652 :             return current->value;
      86             :         }
      87       21882 :         if (cmp > 0)
      88         280 :             break;              /* it's not there */
      89             :     }
      90             : 
      91         286 :     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       96196 : ParseVariableBool(const char *value, const char *name, bool *result)
     108             : {
     109             :     size_t      len;
     110       96196 :     bool        valid = true;
     111             : 
     112             :     /* Treat "unset" as an empty string, which will lead to error below */
     113       96196 :     if (value == NULL)
     114           0 :         value = "";
     115             : 
     116       96196 :     len = strlen(value);
     117             : 
     118       96196 :     if (len > 0 && pg_strncasecmp(value, "true", len) == 0)
     119         140 :         *result = true;
     120       96056 :     else if (len > 0 && pg_strncasecmp(value, "false", len) == 0)
     121         146 :         *result = false;
     122       95910 :     else if (len > 0 && pg_strncasecmp(value, "yes", len) == 0)
     123           4 :         *result = true;
     124       95906 :     else if (len > 0 && pg_strncasecmp(value, "no", len) == 0)
     125           4 :         *result = false;
     126             :     /* 'o' is not unique enough */
     127       95902 :     else if (pg_strncasecmp(value, "on", (len > 2 ? len : 2)) == 0)
     128       17146 :         *result = true;
     129       78756 :     else if (pg_strncasecmp(value, "off", (len > 2 ? len : 2)) == 0)
     130       74370 :         *result = false;
     131        4386 :     else if (pg_strcasecmp(value, "1") == 0)
     132        4370 :         *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       96196 :     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       24786 : 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       24786 :     if (value == NULL)
     163           0 :         value = "";
     164             : 
     165       24786 :     errno = 0;
     166       24786 :     numval = strtol(value, &end, 0);
     167       24786 :     if (errno == 0 && *end == '\0' && end != value && numval == (int) numval)
     168             :     {
     169       24782 :         *result = (int) numval;
     170       24782 :         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      825972 : SetVariable(VariableSpace space, const char *name, const char *value)
     212             : {
     213             :     struct _variable *current,
     214             :                *previous;
     215             : 
     216      825972 :     if (!space || !name)
     217           0 :         return false;
     218             : 
     219      825972 :     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    16351986 :     for (previous = space, current = space->next;
     229             :          current;
     230    15526022 :          previous = current, current = current->next)
     231             :     {
     232    16327102 :         int         cmp = strcmp(current->name, name);
     233             : 
     234    16327102 :         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      702644 :             char       *new_value = value ? pg_strdup(value) : NULL;
     246             :             bool        confirmed;
     247             : 
     248      702644 :             if (current->substitute_hook)
     249       22990 :                 new_value = current->substitute_hook(new_value);
     250             : 
     251      702644 :             if (current->assign_hook)
     252       47740 :                 confirmed = current->assign_hook(new_value);
     253             :             else
     254      654904 :                 confirmed = true;
     255             : 
     256      702644 :             if (confirmed)
     257             :             {
     258      702632 :                 if (current->value)
     259      677882 :                     pg_free(current->value);
     260      702632 :                 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      702632 :                 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      702644 :             return confirmed;
     279             :         }
     280    15624458 :         if (cmp > 0)
     281       98436 :             break;              /* it's not there */
     282             :     }
     283             : 
     284             :     /* not present, make new entry ... unless we were asked to delete */
     285      123320 :     if (value)
     286             :     {
     287      123320 :         current = pg_malloc(sizeof *current);
     288      123320 :         current->name = pg_strdup(name);
     289      123320 :         current->value = pg_strdup(value);
     290      123320 :         current->substitute_hook = NULL;
     291      123320 :         current->assign_hook = NULL;
     292      123320 :         current->next = previous->next;
     293      123320 :         previous->next = current;
     294             :     }
     295      123320 :     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      173250 : SetVariableHooks(VariableSpace space, const char *name,
     316             :                  VariableSubstituteHook shook,
     317             :                  VariableAssignHook ahook)
     318             : {
     319             :     struct _variable *current,
     320             :                *previous;
     321             : 
     322      173250 :     if (!space || !name)
     323           0 :         return;
     324             : 
     325      173250 :     if (!valid_variable_name(name))
     326           0 :         return;
     327             : 
     328     1113750 :     for (previous = space, current = space->next;
     329             :          current;
     330      940500 :          previous = current, current = current->next)
     331             :     {
     332     1064250 :         int         cmp = strcmp(current->name, name);
     333             : 
     334     1064250 :         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     1064250 :         if (cmp > 0)
     346      123750 :             break;              /* it's not there */
     347             :     }
     348             : 
     349             :     /* not present, make new entry */
     350      173250 :     current = pg_malloc(sizeof *current);
     351      173250 :     current->name = pg_strdup(name);
     352      173250 :     current->value = NULL;
     353      173250 :     current->substitute_hook = shook;
     354      173250 :     current->assign_hook = ahook;
     355      173250 :     current->next = previous->next;
     356      173250 :     previous->next = current;
     357      173250 :     if (shook)
     358      140250 :         current->value = (*shook) (current->value);
     359      173250 :     if (ahook)
     360      173250 :         (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         202 : VariableHasHook(VariableSpace space, const char *name)
     369             : {
     370             :     struct _variable *current;
     371             : 
     372             :     Assert(space);
     373             :     Assert(name);
     374             : 
     375        7740 :     for (current = space->next; current; current = current->next)
     376             :     {
     377        7626 :         int         cmp = strcmp(current->name, name);
     378             : 
     379        7626 :         if (cmp == 0)
     380         104 :             return (current->substitute_hook != NULL ||
     381          50 :                     current->assign_hook != NULL);
     382        7572 :         if (cmp > 0)
     383          34 :             break;              /* it's not there */
     384             :     }
     385             : 
     386         148 :     return false;
     387             : }
     388             : 
     389             : /*
     390             :  * Convenience function to set a variable's value to "on".
     391             :  */
     392             : bool
     393       14326 : SetVariableBool(VariableSpace space, const char *name)
     394             : {
     395       14326 :     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.14