PostgreSQL Source Code git master
Loading...
Searching...
No Matches
variables.c
Go to the documentation of this file.
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 */
23static bool
25{
26 const unsigned char *ptr = (const unsigned char *) name;
27
28 /* Mustn't be zero-length */
29 if (*ptr == '\0')
30 return false;
31
32 while (*ptr)
33 {
34 if (IS_HIGHBIT_SET(*ptr) ||
35 strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz"
36 "_0123456789", *ptr) != NULL)
37 ptr++;
38 else
39 return false;
40 }
41
42 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 */
54{
55 struct _variable *ptr;
56
57 ptr = pg_malloc_object(struct _variable);
58 ptr->name = NULL;
59 ptr->value = NULL;
60 ptr->substitute_hook = NULL;
61 ptr->assign_hook = NULL;
62 ptr->next = NULL;
63
64 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 */
72const char *
73GetVariable(VariableSpace space, const char *name)
74{
75 struct _variable *current;
76
77 if (!space)
78 return NULL;
79
80 for (current = space->next; current; current = current->next)
81 {
82 int cmp = strcmp(current->name, name);
83
84 if (cmp == 0)
85 {
86 /* this is correct answer when value is NULL, too */
87 return current->value;
88 }
89 if (cmp > 0)
90 break; /* it's not there */
91 }
92
93 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 */
108bool
109ParseVariableBool(const char *value, const char *name, bool *result)
110{
111 size_t len;
112 bool valid = true;
113
114 /* Treat "unset" as an empty string, which will lead to error below */
115 if (value == NULL)
116 value = "";
117
118 len = strlen(value);
119
120 if (len > 0 && pg_strncasecmp(value, "true", len) == 0)
121 *result = true;
122 else if (len > 0 && pg_strncasecmp(value, "false", len) == 0)
123 *result = false;
124 else if (len > 0 && pg_strncasecmp(value, "yes", len) == 0)
125 *result = true;
126 else if (len > 0 && pg_strncasecmp(value, "no", len) == 0)
127 *result = false;
128 /* 'o' is not unique enough */
129 else if (pg_strncasecmp(value, "on", (len > 2 ? len : 2)) == 0)
130 *result = true;
131 else if (pg_strncasecmp(value, "off", (len > 2 ? len : 2)) == 0)
132 *result = false;
133 else if (pg_strcasecmp(value, "1") == 0)
134 *result = true;
135 else if (pg_strcasecmp(value, "0") == 0)
136 *result = false;
137 else
138 {
139 /* string is not recognized; don't clobber *result */
140 if (name)
141 pg_log_error("unrecognized value \"%s\" for \"%s\": Boolean expected",
142 value, name);
143 valid = false;
144 }
145 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 */
157bool
158ParseVariableNum(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 if (value == NULL)
165 value = "";
166
167 errno = 0;
168 numval = strtol(value, &end, 0);
169 if (errno == 0 && *end == '\0' && end != value && numval == (int) numval)
170 {
171 *result = (int) numval;
172 return true;
173 }
174 else
175 {
176 /* string is not recognized; don't clobber *result */
177 if (name)
178 pg_log_error("invalid value \"%s\" for \"%s\": integer expected",
179 value, name);
180 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 */
194bool
195ParseVariableDouble(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 if ((value == NULL) || (*value == '\0'))
205 {
206 if (name)
207 pg_log_error("invalid input syntax for variable \"%s\"", name);
208 return false;
209 }
210
211 errno = 0;
212 dblval = strtod(value, &end);
213 if (errno == 0 && *end == '\0' && end != value)
214 {
215 if (dblval < min)
216 {
217 if (name)
218 pg_log_error("invalid value \"%s\" for variable \"%s\": must be greater than %.2f",
219 value, name, min);
220 return false;
221 }
222 else if (dblval > max)
223 {
224 if (name)
225 pg_log_error("invalid value \"%s\" for variable \"%s\": must be less than %.2f",
226 value, name, max);
227 return false;
228 }
229 *result = dblval;
230 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 else if ((errno == ERANGE) &&
239 (dblval == 0.0 || dblval >= HUGE_VAL || dblval <= -HUGE_VAL))
240 {
241 if (name)
242 pg_log_error("value \"%s\" is out of range for variable \"%s\"", value, name);
243 return false;
244 }
245 else
246 {
247 if (name)
248 pg_log_error("invalid value \"%s\" for variable \"%s\"", value, name);
249 return false;
250 }
251}
252
253/*
254 * Print values of all variables.
255 */
256void
258{
259 struct _variable *ptr;
260
261 if (!space)
262 return;
263
264 for (ptr = space->next; ptr; ptr = ptr->next)
265 {
266 if (ptr->value)
267 printf("%s = '%s'\n", ptr->name, ptr->value);
268 if (cancel_pressed)
269 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 */
281bool
282SetVariable(VariableSpace space, const char *name, const char *value)
283{
284 struct _variable *current,
285 *previous;
286
287 if (!space || !name)
288 return false;
289
291 {
292 /* Deletion of non-existent variable is not an error */
293 if (!value)
294 return true;
295 pg_log_error("invalid variable name: \"%s\"", name);
296 return false;
297 }
298
299 for (previous = space, current = space->next;
300 current;
301 previous = current, current = current->next)
302 {
303 int cmp = strcmp(current->name, name);
304
305 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 char *new_value = value ? pg_strdup(value) : NULL;
317 bool confirmed;
318
319 if (current->substitute_hook)
321
322 if (current->assign_hook)
323 confirmed = current->assign_hook(new_value);
324 else
325 confirmed = true;
326
327 if (confirmed)
328 {
329 pg_free(current->value);
330 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 if (new_value == NULL &&
337 current->substitute_hook == NULL &&
338 current->assign_hook == NULL)
339 {
340 previous->next = current->next;
341 free(current->name);
342 free(current);
343 }
344 }
345 else
346 pg_free(new_value); /* current->value is left unchanged */
347
348 return confirmed;
349 }
350 if (cmp > 0)
351 break; /* it's not there */
352 }
353
354 /* not present, make new entry ... unless we were asked to delete */
355 if (value)
356 {
357 current = pg_malloc_object(struct _variable);
358 current->name = pg_strdup(name);
359 current->value = pg_strdup(value);
360 current->substitute_hook = NULL;
361 current->assign_hook = NULL;
362 current->next = previous->next;
363 previous->next = current;
364 }
365 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 */
384void
388{
389 struct _variable *current,
390 *previous;
391
392 if (!space || !name)
393 return;
394
396 return;
397
398 for (previous = space, current = space->next;
399 current;
400 previous = current, current = current->next)
401 {
402 int cmp = strcmp(current->name, name);
403
404 if (cmp == 0)
405 {
406 /* found entry, so update */
407 current->substitute_hook = shook;
408 current->assign_hook = ahook;
409 if (shook)
410 current->value = (*shook) (current->value);
411 if (ahook)
412 (void) (*ahook) (current->value);
413 return;
414 }
415 if (cmp > 0)
416 break; /* it's not there */
417 }
418
419 /* not present, make new entry */
420 current = pg_malloc_object(struct _variable);
421 current->name = pg_strdup(name);
422 current->value = NULL;
423 current->substitute_hook = shook;
424 current->assign_hook = ahook;
425 current->next = previous->next;
426 previous->next = current;
427 if (shook)
428 current->value = (*shook) (current->value);
429 if (ahook)
430 (void) (*ahook) (current->value);
431}
432
433/*
434 * Return true iff the named variable has substitute and/or assign hook
435 * functions.
436 */
437bool
439{
440 struct _variable *current;
441
442 Assert(space);
443 Assert(name);
444
445 for (current = space->next; current; current = current->next)
446 {
447 int cmp = strcmp(current->name, name);
448
449 if (cmp == 0)
450 return (current->substitute_hook != NULL ||
451 current->assign_hook != NULL);
452 if (cmp > 0)
453 break; /* it's not there */
454 }
455
456 return false;
457}
458
459/*
460 * Convenience function to set a variable's value to "on".
461 */
462bool
464{
465 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 */
474bool
476{
477 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 */
486void
487PsqlVarEnumError(const char *name, const char *value, const char *suggestions)
488{
489 pg_log_error("unrecognized value \"%s\" for \"%s\"\n"
490 "Available values are: %s.",
492}
#define IS_HIGHBIT_SET(ch)
Definition c.h:1244
#define Assert(condition)
Definition c.h:943
uint32 result
char * pg_strdup(const char *in)
Definition fe_memutils.c:91
void pg_free(void *ptr)
#define pg_malloc_object(type)
Definition fe_memutils.h:60
volatile sig_atomic_t cancel_pressed
Definition print.c:48
static struct @177 value
#define pg_log_error(...)
Definition logging.h:108
const void size_t len
int pg_strcasecmp(const char *s1, const char *s2)
#define printf(...)
Definition port.h:267
int pg_strncasecmp(const char *s1, const char *s2, size_t n)
static int fb(int x)
static int cmp(const chr *x, const chr *y, size_t len)
#define free(a)
VariableSubstituteHook substitute_hook
Definition variables.h:66
VariableAssignHook assign_hook
Definition variables.h:67
struct _variable * next
Definition variables.h:68
char * name
Definition variables.h:64
char * value
Definition variables.h:65
void PrintVariables(VariableSpace space)
Definition variables.c:257
bool DeleteVariable(VariableSpace space, const char *name)
Definition variables.c:475
void SetVariableHooks(VariableSpace space, const char *name, VariableSubstituteHook shook, VariableAssignHook ahook)
Definition variables.c:385
void PsqlVarEnumError(const char *name, const char *value, const char *suggestions)
Definition variables.c:487
bool ParseVariableBool(const char *value, const char *name, bool *result)
Definition variables.c:109
bool ParseVariableDouble(const char *value, const char *name, double *result, double min, double max)
Definition variables.c:195
bool SetVariableBool(VariableSpace space, const char *name)
Definition variables.c:463
bool ParseVariableNum(const char *value, const char *name, int *result)
Definition variables.c:158
bool VariableHasHook(VariableSpace space, const char *name)
Definition variables.c:438
bool SetVariable(VariableSpace space, const char *name, const char *value)
Definition variables.c:282
static bool valid_variable_name(const char *name)
Definition variables.c:24
const char * GetVariable(VariableSpace space, const char *name)
Definition variables.c:73
VariableSpace CreateVariableSpace(void)
Definition variables.c:53
char *(* VariableSubstituteHook)(char *newval)
Definition variables.h:54
bool(* VariableAssignHook)(const char *newval)
Definition variables.h:31
const char * name