LCOV - differential code coverage report
Current view: top level - src/bin/psql - variables.c (source / functions) Coverage Total Hit UIC UBC GBC GIC GNC CBC EUB ECB DCB
Current: Differential Code Coverage HEAD vs 15 Lines: 84.6 % 162 137 5 20 2 26 1 108 3 26 3
Current Date: 2023-04-08 17:13:01 Functions: 83.3 % 12 10 1 1 4 1 5 1 4
Baseline: 15 Line coverage date bins:
Baseline Date: 2023-04-08 15:09:40 (240..) days: 84.6 % 162 137 5 20 2 26 1 108 3 26
Legend: Lines: hit not hit Function coverage date bins:
(240..) days: 58.8 % 17 10 1 1 4 1 5 1 4

 Age         Owner                  TLA  Line data    Source code
                                  1                 : /*
                                  2                 :  * psql - the PostgreSQL interactive terminal
                                  3                 :  *
                                  4                 :  * Copyright (c) 2000-2023, 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
 4244 tgl                        22 CBC      810453 : valid_variable_name(const char *name)
                                 23                 : {
                                 24          810453 :     const unsigned char *ptr = (const unsigned char *) name;
                                 25                 : 
                                 26                 :     /* Mustn't be zero-length */
                                 27          810453 :     if (*ptr == '\0')
 4244 tgl                        28 UBC           0 :         return false;
                                 29                 : 
 4244 tgl                        30 CBC     7958925 :     while (*ptr)
                                 31                 :     {
                                 32         7148478 :         if (IS_HIGHBIT_SET(*ptr) ||
                                 33         7148478 :             strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz"
                                 34         7148478 :                    "_0123456789", *ptr) != NULL)
                                 35         7148472 :             ptr++;
                                 36                 :         else
                                 37               6 :             return false;
                                 38                 :     }
                                 39                 : 
                                 40          810447 :     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
 8557 bruce                      51            6478 : CreateVariableSpace(void)
                                 52                 : {
                                 53                 :     struct _variable *ptr;
                                 54                 : 
 6067 tgl                        55            6478 :     ptr = pg_malloc(sizeof *ptr);
                                 56            6478 :     ptr->name = NULL;
                                 57            6478 :     ptr->value = NULL;
 2258                            58            6478 :     ptr->substitute_hook = NULL;
 6067                            59            6478 :     ptr->assign_hook = NULL;
                                 60            6478 :     ptr->next = NULL;
                                 61                 : 
 8557 bruce                      62            6478 :     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            1669 : GetVariable(VariableSpace space, const char *name)
                                 72                 : {
                                 73                 :     struct _variable *current;
                                 74                 : 
                                 75            1669 :     if (!space)
 8557 bruce                      76 UBC           0 :         return NULL;
                                 77                 : 
 6136 tgl                        78 CBC       57148 :     for (current = space->next; current; current = current->next)
                                 79                 :     {
 2258                            80           57147 :         int         cmp = strcmp(current->name, name);
                                 81                 : 
                                 82           57147 :         if (cmp == 0)
                                 83                 :         {
                                 84                 :             /* this is correct answer when value is NULL, too */
 8557 bruce                      85            1449 :             return current->value;
                                 86                 :         }
 2258 tgl                        87           55698 :         if (cmp > 0)
                                 88             219 :             break;              /* it's not there */
                                 89                 :     }
                                 90                 : 
 8557 bruce                      91             220 :     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
 2260 tgl                       107           89150 : ParseVariableBool(const char *value, const char *name, bool *result)
                                108                 : {
                                109                 :     size_t      len;
                                110           89150 :     bool        valid = true;
                                111                 : 
                                112                 :     /* Treat "unset" as an empty string, which will lead to error below */
 5450 bruce                     113           89150 :     if (value == NULL)
 2258 tgl                       114 UBC           0 :         value = "";
                                115                 : 
 5450 bruce                     116 CBC       89150 :     len = strlen(value);
                                117                 : 
 2260 tgl                       118           89150 :     if (len > 0 && pg_strncasecmp(value, "true", len) == 0)
                                119             106 :         *result = true;
                                120           89044 :     else if (len > 0 && pg_strncasecmp(value, "false", len) == 0)
                                121             119 :         *result = false;
                                122           88925 :     else if (len > 0 && pg_strncasecmp(value, "yes", len) == 0)
                                123               3 :         *result = true;
                                124           88922 :     else if (len > 0 && pg_strncasecmp(value, "no", len) == 0)
                                125               3 :         *result = false;
                                126                 :     /* 'o' is not unique enough */
 5450 bruce                     127           88919 :     else if (pg_strncasecmp(value, "on", (len > 2 ? len : 2)) == 0)
 2260 tgl                       128           20161 :         *result = true;
 5450 bruce                     129           68758 :     else if (pg_strncasecmp(value, "off", (len > 2 ? len : 2)) == 0)
 2260 tgl                       130           64880 :         *result = false;
 5450 bruce                     131            3878 :     else if (pg_strcasecmp(value, "1") == 0)
 2260 tgl                       132            3866 :         *result = true;
 5450 bruce                     133              12 :     else if (pg_strcasecmp(value, "0") == 0)
 2260 tgl                       134               3 :         *result = false;
                                135                 :     else
                                136                 :     {
                                137                 :         /* string is not recognized; don't clobber *result */
 3021                           138               9 :         if (name)
 1469 peter                     139               6 :             pg_log_error("unrecognized value \"%s\" for \"%s\": Boolean expected",
                                140                 :                          value, name);
 2260 tgl                       141               9 :         valid = false;
                                142                 :     }
                                143           89150 :     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           19462 : 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           19462 :     if (value == NULL)
 2258 tgl                       163 UBC           0 :         value = "";
                                164                 : 
 2260 tgl                       165 CBC       19462 :     errno = 0;
                                166           19462 :     numval = strtol(value, &end, 0);
                                167           19462 :     if (errno == 0 && *end == '\0' && end != value && numval == (int) numval)
                                168                 :     {
                                169           19459 :         *result = (int) numval;
                                170           19459 :         return true;
                                171                 :     }
                                172                 :     else
                                173                 :     {
                                174                 :         /* string is not recognized; don't clobber *result */
                                175               3 :         if (name)
 1469 peter                     176               3 :             pg_log_error("invalid value \"%s\" for \"%s\": integer expected",
                                177                 :                          value, name);
 2260 tgl                       178               3 :         return false;
                                179                 :     }
                                180                 : }
                                181                 : 
                                182                 : /*
                                183                 :  * Print values of all variables.
                                184                 :  */
                                185                 : void
 7325 bruce                     186 UBC           0 : PrintVariables(VariableSpace space)
                                187                 : {
                                188                 :     struct _variable *ptr;
                                189                 : 
 6136 tgl                       190               0 :     if (!space)
                                191               0 :         return;
                                192                 : 
 7188 bruce                     193               0 :     for (ptr = space->next; ptr; ptr = ptr->next)
                                194                 :     {
 6067 tgl                       195               0 :         if (ptr->value)
                                196               0 :             printf("%s = '%s'\n", ptr->name, ptr->value);
 6143                           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
 8557 bruce                     211 CBC      667937 : SetVariable(VariableSpace space, const char *name, const char *value)
                                212                 : {
                                213                 :     struct _variable *current,
                                214                 :                *previous;
                                215                 : 
 2260 tgl                       216          667937 :     if (!space || !name)
 8557 bruce                     217 UBC           0 :         return false;
                                218                 : 
 4244 tgl                       219 CBC      667937 :     if (!valid_variable_name(name))
                                220                 :     {
                                221                 :         /* Deletion of non-existent variable is not an error */
 2258                           222               6 :         if (!value)
 2258 tgl                       223 UBC           0 :             return true;
 1469 peter                     224 CBC           6 :         pg_log_error("invalid variable name: \"%s\"", name);
 8557 bruce                     225               6 :         return false;
                                226                 :     }
                                227                 : 
 6136 tgl                       228          667931 :     for (previous = space, current = space->next;
                                229        13409358 :          current;
                                230        12741427 :          previous = current, current = current->next)
                                231                 :     {
 2258                           232        13389629 :         int         cmp = strcmp(current->name, name);
                                233                 : 
                                234        13389629 :         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          570538 :             char       *new_value = value ? pg_strdup(value) : NULL;
                                246                 :             bool        confirmed;
                                247                 : 
                                248          570538 :             if (current->substitute_hook)
 2040 peter_e                   249           25081 :                 new_value = current->substitute_hook(new_value);
                                250                 : 
 6067 tgl                       251          570538 :             if (current->assign_hook)
 2040 peter_e                   252           44515 :                 confirmed = current->assign_hook(new_value);
                                253                 :             else
 2260 tgl                       254          526023 :                 confirmed = true;
                                255                 : 
                                256          570538 :             if (confirmed)
                                257                 :             {
  296 peter                     258 GNC      570529 :                 pg_free(current->value);
 2260 tgl                       259 GIC      570529 :                 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.
 2258 tgl                       264 ECB             :                  */
 2258 tgl                       265 CBC      570529 :                 if (new_value == NULL &&
                                266               3 :                     current->substitute_hook == NULL &&
 2258 tgl                       267 GIC           3 :                     current->assign_hook == NULL)
 2258 tgl                       268 ECB             :                 {
 2258 tgl                       269 CBC           3 :                     previous->next = current->next;
                                270               3 :                     free(current->name);
 2258 tgl                       271 GIC           3 :                     free(current);
                                272                 :                 }
                                273                 :             }
                                274                 :             else
 2118                           275               9 :                 pg_free(new_value); /* current->value is left unchanged */
 2260 tgl                       276 ECB             : 
 2260 tgl                       277 GIC      570538 :             return confirmed;
 6067 tgl                       278 ECB             :         }
 2258 tgl                       279 CBC    12819091 :         if (cmp > 0)
 2258 tgl                       280 GIC       77664 :             break;              /* it's not there */
                                281                 :     }
                                282                 : 
 2258 tgl                       283 ECB             :     /* not present, make new entry ... unless we were asked to delete */
 2258 tgl                       284 GIC       97393 :     if (value)
 2258 tgl                       285 ECB             :     {
 2258 tgl                       286 CBC       97393 :         current = pg_malloc(sizeof *current);
                                287           97393 :         current->name = pg_strdup(name);
                                288           97393 :         current->value = pg_strdup(value);
                                289           97393 :         current->substitute_hook = NULL;
                                290           97393 :         current->assign_hook = NULL;
                                291           97393 :         current->next = previous->next;
 2258 tgl                       292 GIC       97393 :         previous->next = current;
 2258 tgl                       293 ECB             :     }
 6067 tgl                       294 GIC       97393 :     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                 :  */
 2260 tgl                       313 ECB             : void
 2258 tgl                       314 GIC      142516 : SetVariableHooks(VariableSpace space, const char *name,
                                315                 :                  VariableSubstituteHook shook,
                                316                 :                  VariableAssignHook ahook)
                                317                 : {
                                318                 :     struct _variable *current,
                                319                 :                *previous;
 6067 tgl                       320 ECB             : 
 2260 tgl                       321 GBC      142516 :     if (!space || !name)
 2260 tgl                       322 UIC           0 :         return;
 6067 tgl                       323 ECB             : 
 4244 tgl                       324 GBC      142516 :     if (!valid_variable_name(name))
 2260 tgl                       325 UIC           0 :         return;
 6067 tgl                       326 ECB             : 
 6067 tgl                       327 CBC      142516 :     for (previous = space, current = space->next;
                                328          984656 :          current;
 6067 tgl                       329 GIC      842140 :          previous = current, current = current->next)
 6067 tgl                       330 ECB             :     {
 2258 tgl                       331 GIC      945788 :         int         cmp = strcmp(current->name, name);
 2258 tgl                       332 ECB             : 
 2258 tgl                       333 GIC      945788 :         if (cmp == 0)
                                334                 :         {
 6067 tgl                       335 EUB             :             /* found entry, so update */
 2258 tgl                       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);
 2260 tgl                       342 UIC           0 :             return;
 8557 bruce                     343 ECB             :         }
 2258 tgl                       344 CBC      945788 :         if (cmp > 0)
 2258 tgl                       345 GIC      103648 :             break;              /* it's not there */
                                346                 :     }
                                347                 : 
 6067 tgl                       348 ECB             :     /* not present, make new entry */
 6067 tgl                       349 CBC      142516 :     current = pg_malloc(sizeof *current);
                                350          142516 :     current->name = pg_strdup(name);
                                351          142516 :     current->value = NULL;
 2258                           352          142516 :     current->substitute_hook = shook;
                                353          142516 :     current->assign_hook = ahook;
                                354          142516 :     current->next = previous->next;
 6067                           355          142516 :     previous->next = current;
 2258                           356          142516 :     if (shook)
                                357          116604 :         current->value = (*shook) (current->value);
                                358          142516 :     if (ahook)
 2258 tgl                       359 GIC      142516 :         (void) (*ahook) (current->value);
                                360                 : }
                                361                 : 
                                362                 : /*
                                363                 :  * Return true iff the named variable has substitute and/or assign hook
                                364                 :  * functions.
                                365                 :  */
  881 noah                      366 ECB             : bool
  881 noah                      367 GIC         357 : VariableHasHook(VariableSpace space, const char *name)
                                368                 : {
                                369                 :     struct _variable *current;
  881 noah                      370 ECB             : 
  881 noah                      371 CBC         357 :     Assert(space);
  881 noah                      372 GIC         357 :     Assert(name);
  881 noah                      373 ECB             : 
  881 noah                      374 GIC       15438 :     for (current = space->next; current; current = current->next)
  881 noah                      375 ECB             :     {
  881 noah                      376 GIC       15307 :         int         cmp = strcmp(current->name, name);
  881 noah                      377 ECB             : 
  881 noah                      378 CBC       15307 :         if (cmp == 0)
                                379             143 :             return (current->substitute_hook != NULL ||
                                380              70 :                     current->assign_hook != NULL);
                                381           15234 :         if (cmp > 0)
  881 noah                      382 GIC         153 :             break;              /* it's not there */
                                383                 :     }
  881 noah                      384 ECB             : 
  881 noah                      385 GIC         284 :     return false;
                                386                 : }
                                387                 : 
                                388                 : /*
                                389                 :  * Convenience function to set a variable's value to "on".
                                390                 :  */
 8456 peter_e                   391 ECB             : bool
 8456 peter_e                   392 GIC       18223 : SetVariableBool(VariableSpace space, const char *name)
 8456 peter_e                   393 ECB             : {
 7225 tgl                       394 GIC       18223 :     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                 :  */
 8557 bruce                     403 EUB             : bool
 8557 bruce                     404 UIC           0 : DeleteVariable(VariableSpace space, const char *name)
 8557 bruce                     405 EUB             : {
 2258 tgl                       406 UIC           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                 :  */
 2260 tgl                       415 ECB             : void
 2260 tgl                       416 GIC           3 : PsqlVarEnumError(const char *name, const char *value, const char *suggestions)
 2260 tgl                       417 ECB             : {
 1469 peter                     418 GIC           3 :     pg_log_error("unrecognized value \"%s\" for \"%s\"\n"
                                419                 :                  "Available values are: %s.",
 1418 tgl                       420 ECB             :                  value, name, suggestions);
 2260 tgl                       421 GIC           3 : }
        

Generated by: LCOV version v1.16-55-g56c0a2a