LCOV - differential code coverage report
Current view: top level - src/bin/psql - variables.c (source / functions) Coverage Total Hit UBC CBC
Current: Differential Code Coverage 16@8cea358b128 vs 17@8cea358b128 Lines: 84.6 % 162 137 25 137
Current Date: 2024-04-14 14:21:10 Functions: 83.3 % 12 10 2 10
Baseline: 16@8cea358b128 Branches: 70.3 % 128 90 38 90
Baseline Date: 2024-04-14 14:21:09 Line coverage date bins:
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed (240..) days: 84.6 % 162 137 25 137
Function coverage date bins:
(240..) days: 83.3 % 12 10 2 10
Branch coverage date bins:
(240..) days: 70.3 % 128 90 38 90

 Age         Owner                    Branch data    TLA  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
 4615 tgl@sss.pgh.pa.us          22                 :CBC      933711 : valid_variable_name(const char *name)
                                 23                 :                : {
                                 24                 :         933711 :     const unsigned char *ptr = (const unsigned char *) name;
                                 25                 :                : 
                                 26                 :                :     /* Mustn't be zero-length */
                                 27         [ -  + ]:         933711 :     if (*ptr == '\0')
 4615 tgl@sss.pgh.pa.us          28                 :UBC           0 :         return false;
                                 29                 :                : 
 4615 tgl@sss.pgh.pa.us          30         [ +  + ]:CBC     9259347 :     while (*ptr)
                                 31                 :                :     {
                                 32         [ +  - ]:        8325642 :         if (IS_HIGHBIT_SET(*ptr) ||
                                 33                 :        8325642 :             strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz"
                                 34         [ +  + ]:        8325642 :                    "_0123456789", *ptr) != NULL)
                                 35                 :        8325636 :             ptr++;
                                 36                 :                :         else
                                 37                 :              6 :             return false;
                                 38                 :                :     }
                                 39                 :                : 
                                 40                 :         933705 :     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
 8928 bruce@momjian.us           51                 :           8197 : CreateVariableSpace(void)
                                 52                 :                : {
                                 53                 :                :     struct _variable *ptr;
                                 54                 :                : 
 6438 tgl@sss.pgh.pa.us          55                 :           8197 :     ptr = pg_malloc(sizeof *ptr);
                                 56                 :           8197 :     ptr->name = NULL;
                                 57                 :           8197 :     ptr->value = NULL;
 2629                            58                 :           8197 :     ptr->substitute_hook = NULL;
 6438                            59                 :           8197 :     ptr->assign_hook = NULL;
                                 60                 :           8197 :     ptr->next = NULL;
                                 61                 :                : 
 8928 bruce@momjian.us           62                 :           8197 :     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                 :           1755 : GetVariable(VariableSpace space, const char *name)
                                 72                 :                : {
                                 73                 :                :     struct _variable *current;
                                 74                 :                : 
                                 75         [ -  + ]:           1755 :     if (!space)
 8928 bruce@momjian.us           76                 :UBC           0 :         return NULL;
                                 77                 :                : 
 6507 tgl@sss.pgh.pa.us          78         [ +  + ]:CBC       60929 :     for (current = space->next; current; current = current->next)
                                 79                 :                :     {
 2629                            80                 :          60928 :         int         cmp = strcmp(current->name, name);
                                 81                 :                : 
                                 82         [ +  + ]:          60928 :         if (cmp == 0)
                                 83                 :                :         {
                                 84                 :                :             /* this is correct answer when value is NULL, too */
 8928 bruce@momjian.us           85                 :           1529 :             return current->value;
                                 86                 :                :         }
 2629 tgl@sss.pgh.pa.us          87         [ +  + ]:          59399 :         if (cmp > 0)
                                 88                 :            225 :             break;              /* it's not there */
                                 89                 :                :     }
                                 90                 :                : 
 8928 bruce@momjian.us           91                 :            226 :     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
 2631 tgl@sss.pgh.pa.us         107                 :         112862 : ParseVariableBool(const char *value, const char *name, bool *result)
                                108                 :                : {
                                109                 :                :     size_t      len;
                                110                 :         112862 :     bool        valid = true;
                                111                 :                : 
                                112                 :                :     /* Treat "unset" as an empty string, which will lead to error below */
 5821 bruce@momjian.us          113         [ -  + ]:         112862 :     if (value == NULL)
 2629 tgl@sss.pgh.pa.us         114                 :UBC           0 :         value = "";
                                115                 :                : 
 5821 bruce@momjian.us          116                 :CBC      112862 :     len = strlen(value);
                                117                 :                : 
 2631 tgl@sss.pgh.pa.us         118   [ +  -  +  + ]:         112862 :     if (len > 0 && pg_strncasecmp(value, "true", len) == 0)
                                119                 :            108 :         *result = true;
                                120   [ +  -  +  + ]:         112754 :     else if (len > 0 && pg_strncasecmp(value, "false", len) == 0)
                                121                 :            125 :         *result = false;
                                122   [ +  -  +  + ]:         112629 :     else if (len > 0 && pg_strncasecmp(value, "yes", len) == 0)
                                123                 :              3 :         *result = true;
                                124   [ +  -  +  + ]:         112626 :     else if (len > 0 && pg_strncasecmp(value, "no", len) == 0)
                                125                 :              3 :         *result = false;
                                126                 :                :     /* 'o' is not unique enough */
 5821 bruce@momjian.us          127         [ +  + ]:         112623 :     else if (pg_strncasecmp(value, "on", (len > 2 ? len : 2)) == 0)
 2631 tgl@sss.pgh.pa.us         128                 :          25397 :         *result = true;
 5821 bruce@momjian.us          129         [ +  + ]:          87226 :     else if (pg_strncasecmp(value, "off", (len > 2 ? len : 2)) == 0)
 2631 tgl@sss.pgh.pa.us         130                 :          82073 :         *result = false;
 5821 bruce@momjian.us          131         [ +  + ]:           5153 :     else if (pg_strcasecmp(value, "1") == 0)
 2631 tgl@sss.pgh.pa.us         132                 :           5140 :         *result = true;
 5821 bruce@momjian.us          133         [ +  + ]:             13 :     else if (pg_strcasecmp(value, "0") == 0)
 2631 tgl@sss.pgh.pa.us         134                 :              4 :         *result = false;
                                135                 :                :     else
                                136                 :                :     {
                                137                 :                :         /* string is not recognized; don't clobber *result */
 3392                           138         [ +  + ]:              9 :         if (name)
 1840 peter@eisentraut.org      139                 :              6 :             pg_log_error("unrecognized value \"%s\" for \"%s\": Boolean expected",
                                140                 :                :                          value, name);
 2631 tgl@sss.pgh.pa.us         141                 :              9 :         valid = false;
                                142                 :                :     }
                                143                 :         112862 :     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                 :          24619 : 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         [ -  + ]:          24619 :     if (value == NULL)
 2629 tgl@sss.pgh.pa.us         163                 :UBC           0 :         value = "";
                                164                 :                : 
 2631 tgl@sss.pgh.pa.us         165                 :CBC       24619 :     errno = 0;
                                166                 :          24619 :     numval = strtol(value, &end, 0);
                                167   [ +  -  +  +  :          24619 :     if (errno == 0 && *end == '\0' && end != value && numval == (int) numval)
                                        +  -  +  - ]
                                168                 :                :     {
                                169                 :          24616 :         *result = (int) numval;
                                170                 :          24616 :         return true;
                                171                 :                :     }
                                172                 :                :     else
                                173                 :                :     {
                                174                 :                :         /* string is not recognized; don't clobber *result */
                                175         [ +  - ]:              3 :         if (name)
 1840 peter@eisentraut.org      176                 :              3 :             pg_log_error("invalid value \"%s\" for \"%s\": integer expected",
                                177                 :                :                          value, name);
 2631 tgl@sss.pgh.pa.us         178                 :              3 :         return false;
                                179                 :                :     }
                                180                 :                : }
                                181                 :                : 
                                182                 :                : /*
                                183                 :                :  * Print values of all variables.
                                184                 :                :  */
                                185                 :                : void
 7696 bruce@momjian.us          186                 :UBC           0 : PrintVariables(VariableSpace space)
                                187                 :                : {
                                188                 :                :     struct _variable *ptr;
                                189                 :                : 
 6507 tgl@sss.pgh.pa.us         190         [ #  # ]:              0 :     if (!space)
                                191                 :              0 :         return;
                                192                 :                : 
 7559 bruce@momjian.us          193         [ #  # ]:              0 :     for (ptr = space->next; ptr; ptr = ptr->next)
                                194                 :                :     {
 6438 tgl@sss.pgh.pa.us         195         [ #  # ]:              0 :         if (ptr->value)
                                196                 :              0 :             printf("%s = '%s'\n", ptr->name, ptr->value);
 6514                           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
 8928 bruce@momjian.us          211                 :CBC      753377 : SetVariable(VariableSpace space, const char *name, const char *value)
                                212                 :                : {
                                213                 :                :     struct _variable *current,
                                214                 :                :                *previous;
                                215                 :                : 
 2631 tgl@sss.pgh.pa.us         216   [ +  -  -  + ]:         753377 :     if (!space || !name)
 8928 bruce@momjian.us          217                 :UBC           0 :         return false;
                                218                 :                : 
 4615 tgl@sss.pgh.pa.us         219         [ +  + ]:CBC      753377 :     if (!valid_variable_name(name))
                                220                 :                :     {
                                221                 :                :         /* Deletion of non-existent variable is not an error */
 2629                           222         [ -  + ]:              6 :         if (!value)
 2629 tgl@sss.pgh.pa.us         223                 :UBC           0 :             return true;
 1840 peter@eisentraut.org      224                 :CBC           6 :         pg_log_error("invalid variable name: \"%s\"", name);
 8928 bruce@momjian.us          225                 :              6 :         return false;
                                226                 :                :     }
                                227                 :                : 
 6507 tgl@sss.pgh.pa.us         228                 :         753371 :     for (previous = space, current = space->next;
                                229         [ +  + ]:       15046982 :          current;
                                230                 :       14293611 :          previous = current, current = current->next)
                                231                 :                :     {
 2629                           232                 :       15022081 :         int         cmp = strcmp(current->name, name);
                                233                 :                : 
                                234         [ +  + ]:       15022081 :         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         [ +  + ]:         628926 :             char       *new_value = value ? pg_strdup(value) : NULL;
                                246                 :                :             bool        confirmed;
                                247                 :                : 
                                248         [ +  + ]:         628926 :             if (current->substitute_hook)
 2411 peter_e@gmx.net           249                 :          31654 :                 new_value = current->substitute_hook(new_value);
                                250                 :                : 
 6438 tgl@sss.pgh.pa.us         251         [ +  + ]:         628926 :             if (current->assign_hook)
 2411 peter_e@gmx.net           252                 :          56245 :                 confirmed = current->assign_hook(new_value);
                                253                 :                :             else
 2631 tgl@sss.pgh.pa.us         254                 :         572681 :                 confirmed = true;
                                255                 :                : 
                                256         [ +  + ]:         628926 :             if (confirmed)
                                257                 :                :             {
  667 peter@eisentraut.org      258                 :         628917 :                 pg_free(current->value);
 2631 tgl@sss.pgh.pa.us         259                 :         628917 :                 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                 :                :                  */
 2629                           265         [ +  + ]:         628917 :                 if (new_value == NULL &&
                                266         [ +  - ]:              3 :                     current->substitute_hook == NULL &&
                                267         [ +  - ]:              3 :                     current->assign_hook == NULL)
                                268                 :                :                 {
                                269                 :              3 :                     previous->next = current->next;
                                270                 :              3 :                     free(current->name);
                                271                 :              3 :                     free(current);
                                272                 :                :                 }
                                273                 :                :             }
                                274                 :                :             else
 2489                           275                 :              9 :                 pg_free(new_value); /* current->value is left unchanged */
                                276                 :                : 
 2631                           277                 :         628926 :             return confirmed;
                                278                 :                :         }
 2629                           279         [ +  + ]:       14393155 :         if (cmp > 0)
                                280                 :          99544 :             break;              /* it's not there */
                                281                 :                :     }
                                282                 :                : 
                                283                 :                :     /* not present, make new entry ... unless we were asked to delete */
                                284         [ +  + ]:         124445 :     if (value)
                                285                 :                :     {
                                286                 :         124442 :         current = pg_malloc(sizeof *current);
                                287                 :         124442 :         current->name = pg_strdup(name);
                                288                 :         124442 :         current->value = pg_strdup(value);
                                289                 :         124442 :         current->substitute_hook = NULL;
                                290                 :         124442 :         current->assign_hook = NULL;
                                291                 :         124442 :         current->next = previous->next;
                                292                 :         124442 :         previous->next = current;
                                293                 :                :     }
 6438                           294                 :         124445 :     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
 2629                           314                 :         180334 : SetVariableHooks(VariableSpace space, const char *name,
                                315                 :                :                  VariableSubstituteHook shook,
                                316                 :                :                  VariableAssignHook ahook)
                                317                 :                : {
                                318                 :                :     struct _variable *current,
                                319                 :                :                *previous;
                                320                 :                : 
 2631                           321   [ +  -  -  + ]:         180334 :     if (!space || !name)
 2631 tgl@sss.pgh.pa.us         322                 :UBC           0 :         return;
                                323                 :                : 
 4615 tgl@sss.pgh.pa.us         324         [ -  + ]:CBC      180334 :     if (!valid_variable_name(name))
 2631 tgl@sss.pgh.pa.us         325                 :UBC           0 :         return;
                                326                 :                : 
 6438 tgl@sss.pgh.pa.us         327                 :CBC      180334 :     for (previous = space, current = space->next;
                                328         [ +  + ]:        1245944 :          current;
                                329                 :        1065610 :          previous = current, current = current->next)
                                330                 :                :     {
 2629                           331                 :        1196762 :         int         cmp = strcmp(current->name, name);
                                332                 :                : 
                                333         [ -  + ]:        1196762 :         if (cmp == 0)
                                334                 :                :         {
                                335                 :                :             /* found entry, so update */
 2629 tgl@sss.pgh.pa.us         336                 :UBC           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);
 2631                           342                 :              0 :             return;
                                343                 :                :         }
 2629 tgl@sss.pgh.pa.us         344         [ +  + ]:CBC     1196762 :         if (cmp > 0)
                                345                 :         131152 :             break;              /* it's not there */
                                346                 :                :     }
                                347                 :                : 
                                348                 :                :     /* not present, make new entry */
 6438                           349                 :         180334 :     current = pg_malloc(sizeof *current);
                                350                 :         180334 :     current->name = pg_strdup(name);
                                351                 :         180334 :     current->value = NULL;
 2629                           352                 :         180334 :     current->substitute_hook = shook;
                                353                 :         180334 :     current->assign_hook = ahook;
                                354                 :         180334 :     current->next = previous->next;
 6438                           355                 :         180334 :     previous->next = current;
 2629                           356         [ +  + ]:         180334 :     if (shook)
                                357                 :         147546 :         current->value = (*shook) (current->value);
                                358         [ +  - ]:         180334 :     if (ahook)
                                359                 :         180334 :         (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
 1252 noah@leadboat.com         367                 :            389 : VariableHasHook(VariableSpace space, const char *name)
                                368                 :                : {
                                369                 :                :     struct _variable *current;
                                370                 :                : 
                                371         [ -  + ]:            389 :     Assert(space);
                                372         [ -  + ]:            389 :     Assert(name);
                                373                 :                : 
                                374         [ +  + ]:          17140 :     for (current = space->next; current; current = current->next)
                                375                 :                :     {
                                376                 :          16998 :         int         cmp = strcmp(current->name, name);
                                377                 :                : 
                                378         [ +  + ]:          16998 :         if (cmp == 0)
                                379         [ +  + ]:            137 :             return (current->substitute_hook != NULL ||
                                380         [ -  + ]:             67 :                     current->assign_hook != NULL);
                                381         [ +  + ]:          16928 :         if (cmp > 0)
                                382                 :            177 :             break;              /* it's not there */
                                383                 :                :     }
                                384                 :                : 
                                385                 :            319 :     return false;
                                386                 :                : }
                                387                 :                : 
                                388                 :                : /*
                                389                 :                :  * Convenience function to set a variable's value to "on".
                                390                 :                :  */
                                391                 :                : bool
 8827 peter_e@gmx.net           392                 :          23376 : SetVariableBool(VariableSpace space, const char *name)
                                393                 :                : {
 7596 tgl@sss.pgh.pa.us         394                 :          23376 :     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
 8928 bruce@momjian.us          404                 :UBC           0 : DeleteVariable(VariableSpace space, const char *name)
                                405                 :                : {
 2629 tgl@sss.pgh.pa.us         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
 2631 tgl@sss.pgh.pa.us         416                 :CBC           3 : PsqlVarEnumError(const char *name, const char *value, const char *suggestions)
                                417                 :                : {
 1840 peter@eisentraut.org      418                 :              3 :     pg_log_error("unrecognized value \"%s\" for \"%s\"\n"
                                419                 :                :                  "Available values are: %s.",
                                420                 :                :                  value, name, suggestions);
 2631 tgl@sss.pgh.pa.us         421                 :              3 : }
        

Generated by: LCOV version 2.1-beta2-3-g6141622