1/*
2 * psql - the PostgreSQL interactive terminal
3 *
4 * Copyright (c) 2000-2019, PostgreSQL Global Development Group
5 *
6 * src/bin/psql/variables.c
7 */
8#include "postgres_fe.h"
9
10#include "common.h"
11#include "variables.h"
12
13#include "common/logging.h"
14
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
24valid_variable_name(const char *name)
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 results of PrintVariables() more pleasing.
51 */
52VariableSpace
53CreateVariableSpace(void)
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 * Print values of all variables.
186 */
187void
188PrintVariables(VariableSpace space)
189{
190 struct _variable *ptr;
191
192 if (!space)
193 return;
194
195 for (ptr = space->next; ptr; ptr = ptr->next)
196 {
197 if (ptr->value)
198 printf("%s = '%s'\n", ptr->name, ptr->value);
199 if (cancel_pressed)
200 break;
201 }
202}
203
204/*
205 * Set the variable named "name" to value "value",
206 * or delete it if "value" is NULL.
207 *
208 * Returns true if successful, false if not; in the latter case a suitable
209 * error message has been printed, except for the unexpected case of
210 * space or name being NULL.
211 */
212bool
213SetVariable(VariableSpace space, const char *name, const char *value)
214{
215 struct _variable *current,
216 *previous;
217
218 if (!space || !name)
219 return false;
220
221 if (!valid_variable_name(name))
222 {
223 /* Deletion of non-existent variable is not an error */
224 if (!value)
225 return true;
226 pg_log_error("invalid variable name: \"%s\"", name);
227 return false;
228 }
229
230 for (previous = space, current = space->next;
231 current;
232 previous = current, current = current->next)
233 {
234 int cmp = strcmp(current->name, name);
235
236 if (cmp == 0)
237 {
238 /*
239 * Found entry, so update, unless assign hook returns false.
240 *
241 * We must duplicate the passed value to start with. This
242 * simplifies the API for substitute hooks. Moreover, some assign
243 * hooks assume that the passed value has the same lifespan as the
244 * variable. Having to free the string again on failure is a
245 * small price to pay for keeping these APIs simple.
246 */
247 char *new_value = value ? pg_strdup(value) : NULL;
248 bool confirmed;
249
250 if (current->substitute_hook)
251 new_value = current->substitute_hook(new_value);
252
253 if (current->assign_hook)
254 confirmed = current->assign_hook(new_value);
255 else
256 confirmed = true;
257
258 if (confirmed)
259 {
260 if (current->value)
261 pg_free(current->value);
262 current->value = new_value;
263
264 /*
265 * If we deleted the value, and there are no hooks to
266 * remember, we can discard the variable altogether.
267 */
268 if (new_value == NULL &&
269 current->substitute_hook == NULL &&
270 current->assign_hook == NULL)
271 {
272 previous->next = current->next;
273 free(current->name);
274 free(current);
275 }
276 }
277 else if (new_value)
278 pg_free(new_value); /* current->value is left unchanged */
279
280 return confirmed;
281 }
282 if (cmp > 0)
283 break; /* it's not there */
284 }
285
286 /* not present, make new entry ... unless we were asked to delete */
287 if (value)
288 {
289 current = pg_malloc(sizeof *current);
290 current->name = pg_strdup(name);
291 current->value = pg_strdup(value);
292 current->substitute_hook = NULL;
293 current->assign_hook = NULL;
294 current->next = previous->next;
295 previous->next = current;
296 }
297 return true;
298}
299
300/*
301 * Attach substitute and/or assign hook functions to the named variable.
302 * If you need only one hook, pass NULL for the other.
303 *
304 * If the variable doesn't already exist, create it with value NULL, just so
305 * we have a place to store the hook function(s). (The substitute hook might
306 * immediately change the NULL to something else; if not, this state is
307 * externally the same as the variable not being defined.)
308 *
309 * The substitute hook, if given, is immediately called on the variable's
310 * value. Then the assign hook, if given, is called on the variable's value.
311 * This is meant to let it update any derived psql state. If the assign hook
312 * doesn't like the current value, it will print a message to that effect,
313 * but we'll ignore it. Generally we do not expect any such failure here,
314 * because this should get called before any user-supplied value is assigned.
315 */
316void
317SetVariableHooks(VariableSpace space, const char *name,
318 VariableSubstituteHook shook,
319 VariableAssignHook ahook)
320{
321 struct _variable *current,
322 *previous;
323
324 if (!space || !name)
325 return;
326
327 if (!valid_variable_name(name))
328 return;
329
330 for (previous = space, current = space->next;
331 current;
332 previous = current, current = current->next)
333 {
334 int cmp = strcmp(current->name, name);
335
336 if (cmp == 0)
337 {
338 /* found entry, so update */
339 current->substitute_hook = shook;
340 current->assign_hook = ahook;
341 if (shook)
342 current->value = (*shook) (current->value);
343 if (ahook)
344 (void) (*ahook) (current->value);
345 return;
346 }
347 if (cmp > 0)
348 break; /* it's not there */
349 }
350
351 /* not present, make new entry */
352 current = pg_malloc(sizeof *current);
353 current->name = pg_strdup(name);
354 current->value = NULL;
355 current->substitute_hook = shook;
356 current->assign_hook = ahook;
357 current->next = previous->next;
358 previous->next = current;
359 if (shook)
360 current->value = (*shook) (current->value);
361 if (ahook)
362 (void) (*ahook) (current->value);
363}
364
365/*
366 * Convenience function to set a variable's value to "on".
367 */
368bool
369SetVariableBool(VariableSpace space, const char *name)
370{
371 return SetVariable(space, name, "on");
372}
373
374/*
375 * Attempt to delete variable.
376 *
377 * If unsuccessful, print a message and return "false".
378 * Deleting a nonexistent variable is not an error.
379 */
380bool
381DeleteVariable(VariableSpace space, const char *name)
382{
383 return SetVariable(space, name, NULL);
384}
385
386/*
387 * Emit error with suggestions for variables or commands
388 * accepting enum-style arguments.
389 * This function just exists to standardize the wording.
390 * suggestions should follow the format "fee, fi, fo, fum".
391 */
392void
393PsqlVarEnumError(const char *name, const char *value, const char *suggestions)
394{
395 pg_log_error("unrecognized value \"%s\" for \"%s\"\n"
396 "Available values are: %s.",
397 value, name, suggestions);
398}
399