Line data Source code
1 : /*
2 : * psql - the PostgreSQL interactive terminal
3 : *
4 : * Copyright (c) 2000-2026, PostgreSQL Global Development Group
5 : *
6 : * src/bin/psql/variables.c
7 : */
8 : #include "postgres_fe.h"
9 :
10 : #include <math.h>
11 :
12 : #include "common.h"
13 : #include "common/logging.h"
14 : #include "variables.h"
15 :
16 : /*
17 : * Check whether a variable's name is allowed.
18 : *
19 : * We allow any non-ASCII character, as well as ASCII letters, digits, and
20 : * underscore. Keep this in sync with the definition of variable_char in
21 : * psqlscan.l and psqlscanslash.l.
22 : */
23 : static bool
24 2193631 : valid_variable_name(const char *name)
25 : {
26 2193631 : const unsigned char *ptr = (const unsigned char *) name;
27 :
28 : /* Mustn't be zero-length */
29 2193631 : if (*ptr == '\0')
30 0 : return false;
31 :
32 30914834 : while (*ptr)
33 : {
34 28721211 : if (IS_HIGHBIT_SET(*ptr) ||
35 28721211 : strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz"
36 28721211 : "_0123456789", *ptr) != NULL)
37 28721203 : ptr++;
38 : else
39 8 : return false;
40 : }
41 :
42 2193623 : return true;
43 : }
44 :
45 : /*
46 : * A "variable space" is represented by an otherwise-unused struct _variable
47 : * that serves as list header.
48 : *
49 : * The list entries are kept in name order (according to strcmp). This
50 : * is mainly to make the output of PrintVariables() more pleasing.
51 : */
52 : VariableSpace
53 10469 : CreateVariableSpace(void)
54 : {
55 : struct _variable *ptr;
56 :
57 10469 : ptr = pg_malloc_object(struct _variable);
58 10469 : ptr->name = NULL;
59 10469 : ptr->value = NULL;
60 10469 : ptr->substitute_hook = NULL;
61 10469 : ptr->assign_hook = NULL;
62 10469 : ptr->next = NULL;
63 :
64 10469 : return ptr;
65 : }
66 :
67 : /*
68 : * Get string value of variable, or NULL if it's not defined.
69 : *
70 : * Note: result is valid until variable is next assigned to.
71 : */
72 : const char *
73 2712 : GetVariable(VariableSpace space, const char *name)
74 : {
75 : struct _variable *current;
76 :
77 2712 : if (!space)
78 0 : return NULL;
79 :
80 104794 : for (current = space->next; current; current = current->next)
81 : {
82 104793 : int cmp = strcmp(current->name, name);
83 :
84 104793 : if (cmp == 0)
85 : {
86 : /* this is correct answer when value is NULL, too */
87 2379 : return current->value;
88 : }
89 102414 : if (cmp > 0)
90 332 : break; /* it's not there */
91 : }
92 :
93 333 : return NULL;
94 : }
95 :
96 : /*
97 : * Try to interpret "value" as a boolean value, and if successful,
98 : * store it in *result. Otherwise don't clobber *result.
99 : *
100 : * Valid values are: true, false, yes, no, on, off, 1, 0; as well as unique
101 : * prefixes thereof.
102 : *
103 : * "name" is the name of the variable we're assigning to, to use in error
104 : * report if any. Pass name == NULL to suppress the error report.
105 : *
106 : * Return true when "value" is syntactically valid, false otherwise.
107 : */
108 : bool
109 145563 : ParseVariableBool(const char *value, const char *name, bool *result)
110 : {
111 : size_t len;
112 145563 : bool valid = true;
113 :
114 : /* Treat "unset" as an empty string, which will lead to error below */
115 145563 : if (value == NULL)
116 0 : value = "";
117 :
118 145563 : len = strlen(value);
119 :
120 145563 : if (len > 0 && pg_strncasecmp(value, "true", len) == 0)
121 167 : *result = true;
122 145396 : else if (len > 0 && pg_strncasecmp(value, "false", len) == 0)
123 205 : *result = false;
124 145191 : else if (len > 0 && pg_strncasecmp(value, "yes", len) == 0)
125 4 : *result = true;
126 145187 : else if (len > 0 && pg_strncasecmp(value, "no", len) == 0)
127 4 : *result = false;
128 : /* 'o' is not unique enough */
129 145183 : else if (pg_strncasecmp(value, "on", (len > 2 ? len : 2)) == 0)
130 33020 : *result = true;
131 112163 : else if (pg_strncasecmp(value, "off", (len > 2 ? len : 2)) == 0)
132 104831 : *result = false;
133 7332 : else if (pg_strcasecmp(value, "1") == 0)
134 7315 : *result = true;
135 17 : else if (pg_strcasecmp(value, "0") == 0)
136 5 : *result = false;
137 : else
138 : {
139 : /* string is not recognized; don't clobber *result */
140 12 : if (name)
141 8 : pg_log_error("unrecognized value \"%s\" for \"%s\": Boolean expected",
142 : value, name);
143 12 : valid = false;
144 : }
145 145563 : return valid;
146 : }
147 :
148 : /*
149 : * Try to interpret "value" as an integer value, and if successful,
150 : * store it in *result. Otherwise don't clobber *result.
151 : *
152 : * "name" is the name of the variable we're assigning to, to use in error
153 : * report if any. Pass name == NULL to suppress the error report.
154 : *
155 : * Return true when "value" is syntactically valid, false otherwise.
156 : */
157 : bool
158 31460 : ParseVariableNum(const char *value, const char *name, int *result)
159 : {
160 : char *end;
161 : long numval;
162 :
163 : /* Treat "unset" as an empty string, which will lead to error below */
164 31460 : if (value == NULL)
165 0 : value = "";
166 :
167 31460 : errno = 0;
168 31460 : numval = strtol(value, &end, 0);
169 31460 : if (errno == 0 && *end == '\0' && end != value && numval == (int) numval)
170 : {
171 31456 : *result = (int) numval;
172 31456 : return true;
173 : }
174 : else
175 : {
176 : /* string is not recognized; don't clobber *result */
177 4 : if (name)
178 4 : pg_log_error("invalid value \"%s\" for \"%s\": integer expected",
179 : value, name);
180 4 : return false;
181 : }
182 : }
183 :
184 : /*
185 : * Try to interpret "value" as a double value, and if successful store it in
186 : * *result. If unsuccessful, *result isn't clobbered. "name" is the variable
187 : * which is being assigned, the value of which is only used to produce a good
188 : * error message. Pass NULL as the name to suppress the error message. The
189 : * value must be within the range [min,max] in order to be considered valid.
190 : *
191 : * Returns true, with *result containing the interpreted value, if "value" is
192 : * syntactically valid, else false (with *result unchanged).
193 : */
194 : bool
195 10473 : ParseVariableDouble(const char *value, const char *name, double *result, double min, double max)
196 : {
197 : char *end;
198 : double dblval;
199 :
200 : /*
201 : * Empty-string input has historically been treated differently by strtod
202 : * on various platforms, so handle that by specifically checking for it.
203 : */
204 10473 : if ((value == NULL) || (*value == '\0'))
205 : {
206 0 : if (name)
207 0 : pg_log_error("invalid input syntax for variable \"%s\"", name);
208 0 : return false;
209 : }
210 :
211 10473 : errno = 0;
212 10473 : dblval = strtod(value, &end);
213 10473 : if (errno == 0 && *end == '\0' && end != value)
214 : {
215 10472 : if (dblval < min)
216 : {
217 0 : if (name)
218 0 : pg_log_error("invalid value \"%s\" for variable \"%s\": must be greater than %.2f",
219 : value, name, min);
220 0 : return false;
221 : }
222 10472 : else if (dblval > max)
223 : {
224 0 : if (name)
225 0 : pg_log_error("invalid value \"%s\" for variable \"%s\": must be less than %.2f",
226 : value, name, max);
227 0 : return false;
228 : }
229 10472 : *result = dblval;
230 10472 : return true;
231 : }
232 :
233 : /*
234 : * Cater for platforms which treat values which aren't zero, but that are
235 : * too close to zero to have full precision, by checking for zero or real
236 : * out-of-range values.
237 : */
238 1 : else if ((errno == ERANGE) &&
239 1 : (dblval == 0.0 || dblval >= HUGE_VAL || dblval <= -HUGE_VAL))
240 : {
241 1 : if (name)
242 1 : pg_log_error("value \"%s\" is out of range for variable \"%s\"", value, name);
243 1 : return false;
244 : }
245 : else
246 : {
247 0 : if (name)
248 0 : pg_log_error("invalid value \"%s\" for variable \"%s\"", value, name);
249 0 : return false;
250 : }
251 : }
252 :
253 : /*
254 : * Print values of all variables.
255 : */
256 : void
257 0 : PrintVariables(VariableSpace space)
258 : {
259 : struct _variable *ptr;
260 :
261 0 : if (!space)
262 0 : return;
263 :
264 0 : for (ptr = space->next; ptr; ptr = ptr->next)
265 : {
266 0 : if (ptr->value)
267 0 : printf("%s = '%s'\n", ptr->name, ptr->value);
268 0 : if (cancel_pressed)
269 0 : break;
270 : }
271 : }
272 :
273 : /*
274 : * Set the variable named "name" to value "value",
275 : * or delete it if "value" is NULL.
276 : *
277 : * Returns true if successful, false if not; in the latter case a suitable
278 : * error message has been printed, except for the unexpected case of
279 : * space or name being NULL.
280 : */
281 : bool
282 1952844 : SetVariable(VariableSpace space, const char *name, const char *value)
283 : {
284 : struct _variable *current,
285 : *previous;
286 :
287 1952844 : if (!space || !name)
288 0 : return false;
289 :
290 1952844 : if (!valid_variable_name(name))
291 : {
292 : /* Deletion of non-existent variable is not an error */
293 8 : if (!value)
294 0 : return true;
295 8 : pg_log_error("invalid variable name: \"%s\"", name);
296 8 : return false;
297 : }
298 :
299 1952836 : for (previous = space, current = space->next;
300 42342618 : current;
301 40389782 : previous = current, current = current->next)
302 : {
303 42342129 : int cmp = strcmp(current->name, name);
304 :
305 42342129 : if (cmp == 0)
306 : {
307 : /*
308 : * Found entry, so update, unless assign hook returns false.
309 : *
310 : * We must duplicate the passed value to start with. This
311 : * simplifies the API for substitute hooks. Moreover, some assign
312 : * hooks assume that the passed value has the same lifespan as the
313 : * variable. Having to free the string again on failure is a
314 : * small price to pay for keeping these APIs simple.
315 : */
316 1740053 : char *new_value = value ? pg_strdup(value) : NULL;
317 : bool confirmed;
318 :
319 1740053 : if (current->substitute_hook)
320 41909 : new_value = current->substitute_hook(new_value);
321 :
322 1740053 : if (current->assign_hook)
323 73316 : confirmed = current->assign_hook(new_value);
324 : else
325 1666737 : confirmed = true;
326 :
327 1740053 : if (confirmed)
328 : {
329 1740040 : pg_free(current->value);
330 1740040 : current->value = new_value;
331 :
332 : /*
333 : * If we deleted the value, and there are no hooks to
334 : * remember, we can discard the variable altogether.
335 : */
336 1740040 : if (new_value == NULL &&
337 4 : current->substitute_hook == NULL &&
338 4 : current->assign_hook == NULL)
339 : {
340 4 : previous->next = current->next;
341 4 : free(current->name);
342 4 : free(current);
343 : }
344 : }
345 : else
346 13 : pg_free(new_value); /* current->value is left unchanged */
347 :
348 1740053 : return confirmed;
349 : }
350 40602076 : if (cmp > 0)
351 212294 : break; /* it's not there */
352 : }
353 :
354 : /* not present, make new entry ... unless we were asked to delete */
355 212783 : if (value)
356 : {
357 191833 : current = pg_malloc_object(struct _variable);
358 191833 : current->name = pg_strdup(name);
359 191833 : current->value = pg_strdup(value);
360 191833 : current->substitute_hook = NULL;
361 191833 : current->assign_hook = NULL;
362 191833 : current->next = previous->next;
363 191833 : previous->next = current;
364 : }
365 212783 : return true;
366 : }
367 :
368 : /*
369 : * Attach substitute and/or assign hook functions to the named variable.
370 : * If you need only one hook, pass NULL for the other.
371 : *
372 : * If the variable doesn't already exist, create it with value NULL, just so
373 : * we have a place to store the hook function(s). (The substitute hook might
374 : * immediately change the NULL to something else; if not, this state is
375 : * externally the same as the variable not being defined.)
376 : *
377 : * The substitute hook, if given, is immediately called on the variable's
378 : * value. Then the assign hook, if given, is called on the variable's value.
379 : * This is meant to let it update any derived psql state. If the assign hook
380 : * doesn't like the current value, it will print a message to that effect,
381 : * but we'll ignore it. Generally we do not expect any such failure here,
382 : * because this should get called before any user-supplied value is assigned.
383 : */
384 : void
385 240787 : SetVariableHooks(VariableSpace space, const char *name,
386 : VariableSubstituteHook shook,
387 : VariableAssignHook ahook)
388 : {
389 : struct _variable *current,
390 : *previous;
391 :
392 240787 : if (!space || !name)
393 0 : return;
394 :
395 240787 : if (!valid_variable_name(name))
396 0 : return;
397 :
398 240787 : for (previous = space, current = space->next;
399 1832075 : current;
400 1591288 : previous = current, current = current->next)
401 : {
402 1758792 : int cmp = strcmp(current->name, name);
403 :
404 1758792 : if (cmp == 0)
405 : {
406 : /* found entry, so update */
407 0 : current->substitute_hook = shook;
408 0 : current->assign_hook = ahook;
409 0 : if (shook)
410 0 : current->value = (*shook) (current->value);
411 0 : if (ahook)
412 0 : (void) (*ahook) (current->value);
413 0 : return;
414 : }
415 1758792 : if (cmp > 0)
416 167504 : break; /* it's not there */
417 : }
418 :
419 : /* not present, make new entry */
420 240787 : current = pg_malloc_object(struct _variable);
421 240787 : current->name = pg_strdup(name);
422 240787 : current->value = NULL;
423 240787 : current->substitute_hook = shook;
424 240787 : current->assign_hook = ahook;
425 240787 : current->next = previous->next;
426 240787 : previous->next = current;
427 240787 : if (shook)
428 198911 : current->value = (*shook) (current->value);
429 240787 : if (ahook)
430 240787 : (void) (*ahook) (current->value);
431 : }
432 :
433 : /*
434 : * Return true iff the named variable has substitute and/or assign hook
435 : * functions.
436 : */
437 : bool
438 654 : VariableHasHook(VariableSpace space, const char *name)
439 : {
440 : struct _variable *current;
441 :
442 : Assert(space);
443 : Assert(name);
444 :
445 32581 : for (current = space->next; current; current = current->next)
446 : {
447 32345 : int cmp = strcmp(current->name, name);
448 :
449 32345 : if (cmp == 0)
450 188 : return (current->substitute_hook != NULL ||
451 92 : current->assign_hook != NULL);
452 32249 : if (cmp > 0)
453 322 : break; /* it's not there */
454 : }
455 :
456 558 : return false;
457 : }
458 :
459 : /*
460 : * Convenience function to set a variable's value to "on".
461 : */
462 : bool
463 30234 : SetVariableBool(VariableSpace space, const char *name)
464 : {
465 30234 : return SetVariable(space, name, "on");
466 : }
467 :
468 : /*
469 : * Attempt to delete variable.
470 : *
471 : * If unsuccessful, print a message and return "false".
472 : * Deleting a nonexistent variable is not an error.
473 : */
474 : bool
475 0 : DeleteVariable(VariableSpace space, const char *name)
476 : {
477 0 : return SetVariable(space, name, NULL);
478 : }
479 :
480 : /*
481 : * Emit error with suggestions for variables or commands
482 : * accepting enum-style arguments.
483 : * This function just exists to standardize the wording.
484 : * suggestions should follow the format "fee, fi, fo, fum".
485 : */
486 : void
487 4 : PsqlVarEnumError(const char *name, const char *value, const char *suggestions)
488 : {
489 4 : pg_log_error("unrecognized value \"%s\" for \"%s\"\n"
490 : "Available values are: %s.",
491 : value, name, suggestions);
492 4 : }
|