LCOV - code coverage report
Current view: top level - src/bin/psql - variables.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 133 158 84.2 %
Date: 2024-12-12 21:14:38 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-2024, 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     1896082 : valid_variable_name(const char *name)
      23             : {
      24     1896082 :     const unsigned char *ptr = (const unsigned char *) name;
      25             : 
      26             :     /* Mustn't be zero-length */
      27     1896082 :     if (*ptr == '\0')
      28           0 :         return false;
      29             : 
      30    18794672 :     while (*ptr)
      31             :     {
      32    16898602 :         if (IS_HIGHBIT_SET(*ptr) ||
      33    16898602 :             strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz"
      34    16898602 :                    "_0123456789", *ptr) != NULL)
      35    16898590 :             ptr++;
      36             :         else
      37          12 :             return false;
      38             :     }
      39             : 
      40     1896070 :     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 output of PrintVariables() more pleasing.
      49             :  */
      50             : VariableSpace
      51       16402 : CreateVariableSpace(void)
      52             : {
      53             :     struct _variable *ptr;
      54             : 
      55       16402 :     ptr = pg_malloc(sizeof *ptr);
      56       16402 :     ptr->name = NULL;
      57       16402 :     ptr->value = NULL;
      58       16402 :     ptr->substitute_hook = NULL;
      59       16402 :     ptr->assign_hook = NULL;
      60       16402 :     ptr->next = NULL;
      61             : 
      62       16402 :     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        3566 : GetVariable(VariableSpace space, const char *name)
      72             : {
      73             :     struct _variable *current;
      74             : 
      75        3566 :     if (!space)
      76           0 :         return NULL;
      77             : 
      78      123704 :     for (current = space->next; current; current = current->next)
      79             :     {
      80      123702 :         int         cmp = strcmp(current->name, name);
      81             : 
      82      123702 :         if (cmp == 0)
      83             :         {
      84             :             /* this is correct answer when value is NULL, too */
      85        3102 :             return current->value;
      86             :         }
      87      120600 :         if (cmp > 0)
      88         462 :             break;              /* it's not there */
      89             :     }
      90             : 
      91         464 :     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      226484 : ParseVariableBool(const char *value, const char *name, bool *result)
     108             : {
     109             :     size_t      len;
     110      226484 :     bool        valid = true;
     111             : 
     112             :     /* Treat "unset" as an empty string, which will lead to error below */
     113      226484 :     if (value == NULL)
     114           0 :         value = "";
     115             : 
     116      226484 :     len = strlen(value);
     117             : 
     118      226484 :     if (len > 0 && pg_strncasecmp(value, "true", len) == 0)
     119         216 :         *result = true;
     120      226268 :     else if (len > 0 && pg_strncasecmp(value, "false", len) == 0)
     121         252 :         *result = false;
     122      226016 :     else if (len > 0 && pg_strncasecmp(value, "yes", len) == 0)
     123           6 :         *result = true;
     124      226010 :     else if (len > 0 && pg_strncasecmp(value, "no", len) == 0)
     125           6 :         *result = false;
     126             :     /* 'o' is not unique enough */
     127      226004 :     else if (pg_strncasecmp(value, "on", (len > 2 ? len : 2)) == 0)
     128       50794 :         *result = true;
     129      175210 :     else if (pg_strncasecmp(value, "off", (len > 2 ? len : 2)) == 0)
     130      164226 :         *result = false;
     131       10984 :     else if (pg_strcasecmp(value, "1") == 0)
     132       10958 :         *result = true;
     133          26 :     else if (pg_strcasecmp(value, "0") == 0)
     134           8 :         *result = false;
     135             :     else
     136             :     {
     137             :         /* string is not recognized; don't clobber *result */
     138          18 :         if (name)
     139          12 :             pg_log_error("unrecognized value \"%s\" for \"%s\": Boolean expected",
     140             :                          value, name);
     141          18 :         valid = false;
     142             :     }
     143      226484 :     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       49262 : 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       49262 :     if (value == NULL)
     163           0 :         value = "";
     164             : 
     165       49262 :     errno = 0;
     166       49262 :     numval = strtol(value, &end, 0);
     167       49262 :     if (errno == 0 && *end == '\0' && end != value && numval == (int) numval)
     168             :     {
     169       49256 :         *result = (int) numval;
     170       49256 :         return true;
     171             :     }
     172             :     else
     173             :     {
     174             :         /* string is not recognized; don't clobber *result */
     175           6 :         if (name)
     176           6 :             pg_log_error("invalid value \"%s\" for \"%s\": integer expected",
     177             :                          value, name);
     178           6 :         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     1535238 : SetVariable(VariableSpace space, const char *name, const char *value)
     212             : {
     213             :     struct _variable *current,
     214             :                *previous;
     215             : 
     216     1535238 :     if (!space || !name)
     217           0 :         return false;
     218             : 
     219     1535238 :     if (!valid_variable_name(name))
     220             :     {
     221             :         /* Deletion of non-existent variable is not an error */
     222          12 :         if (!value)
     223           0 :             return true;
     224          12 :         pg_log_error("invalid variable name: \"%s\"", name);
     225          12 :         return false;
     226             :     }
     227             : 
     228    30679824 :     for (previous = space, current = space->next;
     229             :          current;
     230    29144598 :          previous = current, current = current->next)
     231             :     {
     232    30629980 :         int         cmp = strcmp(current->name, name);
     233             : 
     234    30629980 :         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     1284432 :             char       *new_value = value ? pg_strdup(value) : NULL;
     246             :             bool        confirmed;
     247             : 
     248     1284432 :             if (current->substitute_hook)
     249       63978 :                 new_value = current->substitute_hook(new_value);
     250             : 
     251     1284432 :             if (current->assign_hook)
     252      113184 :                 confirmed = current->assign_hook(new_value);
     253             :             else
     254     1171248 :                 confirmed = true;
     255             : 
     256     1284432 :             if (confirmed)
     257             :             {
     258     1284414 :                 pg_free(current->value);
     259     1284414 :                 current->value = new_value;
     260             : 
     261             :                 /*
     262             :                  * If we deleted the value, and there are no hooks to
     263             :                  * remember, we can discard the variable altogether.
     264             :                  */
     265     1284414 :                 if (new_value == NULL &&
     266           6 :                     current->substitute_hook == NULL &&
     267           6 :                     current->assign_hook == NULL)
     268             :                 {
     269           6 :                     previous->next = current->next;
     270           6 :                     free(current->name);
     271           6 :                     free(current);
     272             :                 }
     273             :             }
     274             :             else
     275          18 :                 pg_free(new_value); /* current->value is left unchanged */
     276             : 
     277     1284432 :             return confirmed;
     278             :         }
     279    29345548 :         if (cmp > 0)
     280      200950 :             break;              /* it's not there */
     281             :     }
     282             : 
     283             :     /* not present, make new entry ... unless we were asked to delete */
     284      250794 :     if (value)
     285             :     {
     286      250788 :         current = pg_malloc(sizeof *current);
     287      250788 :         current->name = pg_strdup(name);
     288      250788 :         current->value = pg_strdup(value);
     289      250788 :         current->substitute_hook = NULL;
     290      250788 :         current->assign_hook = NULL;
     291      250788 :         current->next = previous->next;
     292      250788 :         previous->next = current;
     293             :     }
     294      250794 :     return true;
     295             : }
     296             : 
     297             : /*
     298             :  * Attach substitute and/or assign hook functions to the named variable.
     299             :  * If you need only one hook, pass NULL for the other.
     300             :  *
     301             :  * If the variable doesn't already exist, create it with value NULL, just so
     302             :  * we have a place to store the hook function(s).  (The substitute hook might
     303             :  * immediately change the NULL to something else; if not, this state is
     304             :  * externally the same as the variable not being defined.)
     305             :  *
     306             :  * The substitute hook, if given, is immediately called on the variable's
     307             :  * value.  Then the assign hook, if given, is called on the variable's value.
     308             :  * This is meant to let it update any derived psql state.  If the assign hook
     309             :  * doesn't like the current value, it will print a message to that effect,
     310             :  * but we'll ignore it.  Generally we do not expect any such failure here,
     311             :  * because this should get called before any user-supplied value is assigned.
     312             :  */
     313             : void
     314      360844 : SetVariableHooks(VariableSpace space, const char *name,
     315             :                  VariableSubstituteHook shook,
     316             :                  VariableAssignHook ahook)
     317             : {
     318             :     struct _variable *current,
     319             :                *previous;
     320             : 
     321      360844 :     if (!space || !name)
     322           0 :         return;
     323             : 
     324      360844 :     if (!valid_variable_name(name))
     325           0 :         return;
     326             : 
     327     2493104 :     for (previous = space, current = space->next;
     328             :          current;
     329     2132260 :          previous = current, current = current->next)
     330             :     {
     331     2394692 :         int         cmp = strcmp(current->name, name);
     332             : 
     333     2394692 :         if (cmp == 0)
     334             :         {
     335             :             /* found entry, so update */
     336           0 :             current->substitute_hook = shook;
     337           0 :             current->assign_hook = ahook;
     338           0 :             if (shook)
     339           0 :                 current->value = (*shook) (current->value);
     340           0 :             if (ahook)
     341           0 :                 (void) (*ahook) (current->value);
     342           0 :             return;
     343             :         }
     344     2394692 :         if (cmp > 0)
     345      262432 :             break;              /* it's not there */
     346             :     }
     347             : 
     348             :     /* not present, make new entry */
     349      360844 :     current = pg_malloc(sizeof *current);
     350      360844 :     current->name = pg_strdup(name);
     351      360844 :     current->value = NULL;
     352      360844 :     current->substitute_hook = shook;
     353      360844 :     current->assign_hook = ahook;
     354      360844 :     current->next = previous->next;
     355      360844 :     previous->next = current;
     356      360844 :     if (shook)
     357      295236 :         current->value = (*shook) (current->value);
     358      360844 :     if (ahook)
     359      360844 :         (void) (*ahook) (current->value);
     360             : }
     361             : 
     362             : /*
     363             :  * Return true iff the named variable has substitute and/or assign hook
     364             :  * functions.
     365             :  */
     366             : bool
     367         798 : VariableHasHook(VariableSpace space, const char *name)
     368             : {
     369             :     struct _variable *current;
     370             : 
     371             :     Assert(space);
     372             :     Assert(name);
     373             : 
     374       35154 :     for (current = space->next; current; current = current->next)
     375             :     {
     376       34856 :         int         cmp = strcmp(current->name, name);
     377             : 
     378       34856 :         if (cmp == 0)
     379         274 :             return (current->substitute_hook != NULL ||
     380         134 :                     current->assign_hook != NULL);
     381       34716 :         if (cmp > 0)
     382         360 :             break;              /* it's not there */
     383             :     }
     384             : 
     385         658 :     return false;
     386             : }
     387             : 
     388             : /*
     389             :  * Convenience function to set a variable's value to "on".
     390             :  */
     391             : bool
     392       46710 : SetVariableBool(VariableSpace space, const char *name)
     393             : {
     394       46710 :     return SetVariable(space, name, "on");
     395             : }
     396             : 
     397             : /*
     398             :  * Attempt to delete variable.
     399             :  *
     400             :  * If unsuccessful, print a message and return "false".
     401             :  * Deleting a nonexistent variable is not an error.
     402             :  */
     403             : bool
     404           0 : DeleteVariable(VariableSpace space, const char *name)
     405             : {
     406           0 :     return SetVariable(space, name, NULL);
     407             : }
     408             : 
     409             : /*
     410             :  * Emit error with suggestions for variables or commands
     411             :  * accepting enum-style arguments.
     412             :  * This function just exists to standardize the wording.
     413             :  * suggestions should follow the format "fee, fi, fo, fum".
     414             :  */
     415             : void
     416           6 : PsqlVarEnumError(const char *name, const char *value, const char *suggestions)
     417             : {
     418           6 :     pg_log_error("unrecognized value \"%s\" for \"%s\"\n"
     419             :                  "Available values are: %s.",
     420             :                  value, name, suggestions);
     421           6 : }

Generated by: LCOV version 1.14