PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
variables.c
Go to the documentation of this file.
1/*
2 * psql - the PostgreSQL interactive terminal
3 *
4 * Copyright (c) 2000-2025, 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(sizeof *ptr);
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 \"%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 \"%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 \"%s\": must be less than %.2f",
226 value, name, max);
227 }
228 *result = dblval;
229 return true;
230 }
231
232 /*
233 * Cater for platforms which treat values which aren't zero, but that are
234 * too close to zero to have full precision, by checking for zero or real
235 * out-of-range values.
236 */
237 else if ((errno == ERANGE) &&
238 (dblval == 0.0 || dblval >= HUGE_VAL || dblval <= -HUGE_VAL))
239 {
240 if (name)
241 pg_log_error("\"%s\" is out of range for \"%s\"", value, name);
242 return false;
243 }
244 else
245 {
246 if (name)
247 pg_log_error("invalid value \"%s\" for \"%s\"", value, name);
248 return false;
249 }
250}
251
252/*
253 * Print values of all variables.
254 */
255void
257{
258 struct _variable *ptr;
259
260 if (!space)
261 return;
262
263 for (ptr = space->next; ptr; ptr = ptr->next)
264 {
265 if (ptr->value)
266 printf("%s = '%s'\n", ptr->name, ptr->value);
267 if (cancel_pressed)
268 break;
269 }
270}
271
272/*
273 * Set the variable named "name" to value "value",
274 * or delete it if "value" is NULL.
275 *
276 * Returns true if successful, false if not; in the latter case a suitable
277 * error message has been printed, except for the unexpected case of
278 * space or name being NULL.
279 */
280bool
281SetVariable(VariableSpace space, const char *name, const char *value)
282{
283 struct _variable *current,
284 *previous;
285
286 if (!space || !name)
287 return false;
288
290 {
291 /* Deletion of non-existent variable is not an error */
292 if (!value)
293 return true;
294 pg_log_error("invalid variable name: \"%s\"", name);
295 return false;
296 }
297
298 for (previous = space, current = space->next;
299 current;
300 previous = current, current = current->next)
301 {
302 int cmp = strcmp(current->name, name);
303
304 if (cmp == 0)
305 {
306 /*
307 * Found entry, so update, unless assign hook returns false.
308 *
309 * We must duplicate the passed value to start with. This
310 * simplifies the API for substitute hooks. Moreover, some assign
311 * hooks assume that the passed value has the same lifespan as the
312 * variable. Having to free the string again on failure is a
313 * small price to pay for keeping these APIs simple.
314 */
315 char *new_value = value ? pg_strdup(value) : NULL;
316 bool confirmed;
317
318 if (current->substitute_hook)
319 new_value = current->substitute_hook(new_value);
320
321 if (current->assign_hook)
322 confirmed = current->assign_hook(new_value);
323 else
324 confirmed = true;
325
326 if (confirmed)
327 {
328 pg_free(current->value);
329 current->value = new_value;
330
331 /*
332 * If we deleted the value, and there are no hooks to
333 * remember, we can discard the variable altogether.
334 */
335 if (new_value == NULL &&
336 current->substitute_hook == NULL &&
337 current->assign_hook == NULL)
338 {
339 previous->next = current->next;
340 free(current->name);
341 free(current);
342 }
343 }
344 else
345 pg_free(new_value); /* current->value is left unchanged */
346
347 return confirmed;
348 }
349 if (cmp > 0)
350 break; /* it's not there */
351 }
352
353 /* not present, make new entry ... unless we were asked to delete */
354 if (value)
355 {
356 current = pg_malloc(sizeof *current);
357 current->name = pg_strdup(name);
358 current->value = pg_strdup(value);
359 current->substitute_hook = NULL;
360 current->assign_hook = NULL;
361 current->next = previous->next;
362 previous->next = current;
363 }
364 return true;
365}
366
367/*
368 * Attach substitute and/or assign hook functions to the named variable.
369 * If you need only one hook, pass NULL for the other.
370 *
371 * If the variable doesn't already exist, create it with value NULL, just so
372 * we have a place to store the hook function(s). (The substitute hook might
373 * immediately change the NULL to something else; if not, this state is
374 * externally the same as the variable not being defined.)
375 *
376 * The substitute hook, if given, is immediately called on the variable's
377 * value. Then the assign hook, if given, is called on the variable's value.
378 * This is meant to let it update any derived psql state. If the assign hook
379 * doesn't like the current value, it will print a message to that effect,
380 * but we'll ignore it. Generally we do not expect any such failure here,
381 * because this should get called before any user-supplied value is assigned.
382 */
383void
386 VariableAssignHook ahook)
387{
388 struct _variable *current,
389 *previous;
390
391 if (!space || !name)
392 return;
393
395 return;
396
397 for (previous = space, current = space->next;
398 current;
399 previous = current, current = current->next)
400 {
401 int cmp = strcmp(current->name, name);
402
403 if (cmp == 0)
404 {
405 /* found entry, so update */
406 current->substitute_hook = shook;
407 current->assign_hook = ahook;
408 if (shook)
409 current->value = (*shook) (current->value);
410 if (ahook)
411 (void) (*ahook) (current->value);
412 return;
413 }
414 if (cmp > 0)
415 break; /* it's not there */
416 }
417
418 /* not present, make new entry */
419 current = pg_malloc(sizeof *current);
420 current->name = pg_strdup(name);
421 current->value = NULL;
422 current->substitute_hook = shook;
423 current->assign_hook = ahook;
424 current->next = previous->next;
425 previous->next = current;
426 if (shook)
427 current->value = (*shook) (current->value);
428 if (ahook)
429 (void) (*ahook) (current->value);
430}
431
432/*
433 * Return true iff the named variable has substitute and/or assign hook
434 * functions.
435 */
436bool
438{
439 struct _variable *current;
440
441 Assert(space);
442 Assert(name);
443
444 for (current = space->next; current; current = current->next)
445 {
446 int cmp = strcmp(current->name, name);
447
448 if (cmp == 0)
449 return (current->substitute_hook != NULL ||
450 current->assign_hook != NULL);
451 if (cmp > 0)
452 break; /* it's not there */
453 }
454
455 return false;
456}
457
458/*
459 * Convenience function to set a variable's value to "on".
460 */
461bool
463{
464 return SetVariable(space, name, "on");
465}
466
467/*
468 * Attempt to delete variable.
469 *
470 * If unsuccessful, print a message and return "false".
471 * Deleting a nonexistent variable is not an error.
472 */
473bool
475{
476 return SetVariable(space, name, NULL);
477}
478
479/*
480 * Emit error with suggestions for variables or commands
481 * accepting enum-style arguments.
482 * This function just exists to standardize the wording.
483 * suggestions should follow the format "fee, fi, fo, fum".
484 */
485void
486PsqlVarEnumError(const char *name, const char *value, const char *suggestions)
487{
488 pg_log_error("unrecognized value \"%s\" for \"%s\"\n"
489 "Available values are: %s.",
490 value, name, suggestions);
491}
#define IS_HIGHBIT_SET(ch)
Definition: c.h:1126
void * pg_malloc(size_t size)
Definition: fe_memutils.c:47
char * pg_strdup(const char *in)
Definition: fe_memutils.c:85
void pg_free(void *ptr)
Definition: fe_memutils.c:105
volatile sig_atomic_t cancel_pressed
Definition: print.c:43
Assert(PointerIsAligned(start, uint64))
#define free(a)
Definition: header.h:65
static struct @165 value
#define pg_log_error(...)
Definition: logging.h:106
const void size_t len
int pg_strcasecmp(const char *s1, const char *s2)
Definition: pgstrcasecmp.c:36
#define printf(...)
Definition: port.h:245
int pg_strncasecmp(const char *s1, const char *s2, size_t n)
Definition: pgstrcasecmp.c:69
static int cmp(const chr *x, const chr *y, size_t len)
Definition: regc_locale.c:743
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:256
bool DeleteVariable(VariableSpace space, const char *name)
Definition: variables.c:474
void SetVariableHooks(VariableSpace space, const char *name, VariableSubstituteHook shook, VariableAssignHook ahook)
Definition: variables.c:384
void PsqlVarEnumError(const char *name, const char *value, const char *suggestions)
Definition: variables.c:486
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:462
bool ParseVariableNum(const char *value, const char *name, int *result)
Definition: variables.c:158
bool VariableHasHook(VariableSpace space, const char *name)
Definition: variables.c:437
bool SetVariable(VariableSpace space, const char *name, const char *value)
Definition: variables.c:281
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