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 : }
|