Line data Source code
1 : /*
2 : * psql - the PostgreSQL interactive terminal
3 : *
4 : * Copyright (c) 2000-2024, 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
22 1896082 : valid_variable_name(const char *name)
23 : {
24 1896082 : const unsigned char *ptr = (const unsigned char *) name;
25 :
26 : /* Mustn't be zero-length */
27 1896082 : if (*ptr == '\0')
28 0 : return false;
29 :
30 18794672 : while (*ptr)
31 : {
32 16898602 : if (IS_HIGHBIT_SET(*ptr) ||
33 16898602 : strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz"
34 16898602 : "_0123456789", *ptr) != NULL)
35 16898590 : ptr++;
36 : else
37 12 : return false;
38 : }
39 :
40 1896070 : 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
51 16402 : CreateVariableSpace(void)
52 : {
53 : struct _variable *ptr;
54 :
55 16402 : ptr = pg_malloc(sizeof *ptr);
56 16402 : ptr->name = NULL;
57 16402 : ptr->value = NULL;
58 16402 : ptr->substitute_hook = NULL;
59 16402 : ptr->assign_hook = NULL;
60 16402 : ptr->next = NULL;
61 :
62 16402 : 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 3566 : GetVariable(VariableSpace space, const char *name)
72 : {
73 : struct _variable *current;
74 :
75 3566 : if (!space)
76 0 : return NULL;
77 :
78 123704 : for (current = space->next; current; current = current->next)
79 : {
80 123702 : int cmp = strcmp(current->name, name);
81 :
82 123702 : if (cmp == 0)
83 : {
84 : /* this is correct answer when value is NULL, too */
85 3102 : return current->value;
86 : }
87 120600 : if (cmp > 0)
88 462 : break; /* it's not there */
89 : }
90 :
91 464 : 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
107 226484 : ParseVariableBool(const char *value, const char *name, bool *result)
108 : {
109 : size_t len;
110 226484 : bool valid = true;
111 :
112 : /* Treat "unset" as an empty string, which will lead to error below */
113 226484 : if (value == NULL)
114 0 : value = "";
115 :
116 226484 : len = strlen(value);
117 :
118 226484 : if (len > 0 && pg_strncasecmp(value, "true", len) == 0)
119 216 : *result = true;
120 226268 : else if (len > 0 && pg_strncasecmp(value, "false", len) == 0)
121 252 : *result = false;
122 226016 : else if (len > 0 && pg_strncasecmp(value, "yes", len) == 0)
123 6 : *result = true;
124 226010 : else if (len > 0 && pg_strncasecmp(value, "no", len) == 0)
125 6 : *result = false;
126 : /* 'o' is not unique enough */
127 226004 : else if (pg_strncasecmp(value, "on", (len > 2 ? len : 2)) == 0)
128 50794 : *result = true;
129 175210 : else if (pg_strncasecmp(value, "off", (len > 2 ? len : 2)) == 0)
130 164226 : *result = false;
131 10984 : else if (pg_strcasecmp(value, "1") == 0)
132 10958 : *result = true;
133 26 : else if (pg_strcasecmp(value, "0") == 0)
134 8 : *result = false;
135 : else
136 : {
137 : /* string is not recognized; don't clobber *result */
138 18 : if (name)
139 12 : pg_log_error("unrecognized value \"%s\" for \"%s\": Boolean expected",
140 : value, name);
141 18 : valid = false;
142 : }
143 226484 : 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 49262 : 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 49262 : if (value == NULL)
163 0 : value = "";
164 :
165 49262 : errno = 0;
166 49262 : numval = strtol(value, &end, 0);
167 49262 : if (errno == 0 && *end == '\0' && end != value && numval == (int) numval)
168 : {
169 49256 : *result = (int) numval;
170 49256 : return true;
171 : }
172 : else
173 : {
174 : /* string is not recognized; don't clobber *result */
175 6 : if (name)
176 6 : pg_log_error("invalid value \"%s\" for \"%s\": integer expected",
177 : value, name);
178 6 : return false;
179 : }
180 : }
181 :
182 : /*
183 : * Print values of all variables.
184 : */
185 : void
186 0 : PrintVariables(VariableSpace space)
187 : {
188 : struct _variable *ptr;
189 :
190 0 : if (!space)
191 0 : return;
192 :
193 0 : for (ptr = space->next; ptr; ptr = ptr->next)
194 : {
195 0 : if (ptr->value)
196 0 : printf("%s = '%s'\n", ptr->name, ptr->value);
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
211 1535238 : SetVariable(VariableSpace space, const char *name, const char *value)
212 : {
213 : struct _variable *current,
214 : *previous;
215 :
216 1535238 : if (!space || !name)
217 0 : return false;
218 :
219 1535238 : if (!valid_variable_name(name))
220 : {
221 : /* Deletion of non-existent variable is not an error */
222 12 : if (!value)
223 0 : return true;
224 12 : pg_log_error("invalid variable name: \"%s\"", name);
225 12 : return false;
226 : }
227 :
228 30679824 : for (previous = space, current = space->next;
229 : current;
230 29144598 : previous = current, current = current->next)
231 : {
232 30629980 : int cmp = strcmp(current->name, name);
233 :
234 30629980 : 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 1284432 : char *new_value = value ? pg_strdup(value) : NULL;
246 : bool confirmed;
247 :
248 1284432 : if (current->substitute_hook)
249 63978 : new_value = current->substitute_hook(new_value);
250 :
251 1284432 : if (current->assign_hook)
252 113184 : confirmed = current->assign_hook(new_value);
253 : else
254 1171248 : confirmed = true;
255 :
256 1284432 : if (confirmed)
257 : {
258 1284414 : pg_free(current->value);
259 1284414 : 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.
264 : */
265 1284414 : if (new_value == NULL &&
266 6 : current->substitute_hook == NULL &&
267 6 : current->assign_hook == NULL)
268 : {
269 6 : previous->next = current->next;
270 6 : free(current->name);
271 6 : free(current);
272 : }
273 : }
274 : else
275 18 : pg_free(new_value); /* current->value is left unchanged */
276 :
277 1284432 : return confirmed;
278 : }
279 29345548 : if (cmp > 0)
280 200950 : break; /* it's not there */
281 : }
282 :
283 : /* not present, make new entry ... unless we were asked to delete */
284 250794 : if (value)
285 : {
286 250788 : current = pg_malloc(sizeof *current);
287 250788 : current->name = pg_strdup(name);
288 250788 : current->value = pg_strdup(value);
289 250788 : current->substitute_hook = NULL;
290 250788 : current->assign_hook = NULL;
291 250788 : current->next = previous->next;
292 250788 : previous->next = current;
293 : }
294 250794 : 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 : */
313 : void
314 360844 : SetVariableHooks(VariableSpace space, const char *name,
315 : VariableSubstituteHook shook,
316 : VariableAssignHook ahook)
317 : {
318 : struct _variable *current,
319 : *previous;
320 :
321 360844 : if (!space || !name)
322 0 : return;
323 :
324 360844 : if (!valid_variable_name(name))
325 0 : return;
326 :
327 2493104 : for (previous = space, current = space->next;
328 : current;
329 2132260 : previous = current, current = current->next)
330 : {
331 2394692 : int cmp = strcmp(current->name, name);
332 :
333 2394692 : if (cmp == 0)
334 : {
335 : /* found entry, so update */
336 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);
342 0 : return;
343 : }
344 2394692 : if (cmp > 0)
345 262432 : break; /* it's not there */
346 : }
347 :
348 : /* not present, make new entry */
349 360844 : current = pg_malloc(sizeof *current);
350 360844 : current->name = pg_strdup(name);
351 360844 : current->value = NULL;
352 360844 : current->substitute_hook = shook;
353 360844 : current->assign_hook = ahook;
354 360844 : current->next = previous->next;
355 360844 : previous->next = current;
356 360844 : if (shook)
357 295236 : current->value = (*shook) (current->value);
358 360844 : if (ahook)
359 360844 : (void) (*ahook) (current->value);
360 : }
361 :
362 : /*
363 : * Return true iff the named variable has substitute and/or assign hook
364 : * functions.
365 : */
366 : bool
367 798 : VariableHasHook(VariableSpace space, const char *name)
368 : {
369 : struct _variable *current;
370 :
371 : Assert(space);
372 : Assert(name);
373 :
374 35154 : for (current = space->next; current; current = current->next)
375 : {
376 34856 : int cmp = strcmp(current->name, name);
377 :
378 34856 : if (cmp == 0)
379 274 : return (current->substitute_hook != NULL ||
380 134 : current->assign_hook != NULL);
381 34716 : if (cmp > 0)
382 360 : break; /* it's not there */
383 : }
384 :
385 658 : return false;
386 : }
387 :
388 : /*
389 : * Convenience function to set a variable's value to "on".
390 : */
391 : bool
392 46710 : SetVariableBool(VariableSpace space, const char *name)
393 : {
394 46710 : 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 : */
403 : bool
404 0 : DeleteVariable(VariableSpace space, const char *name)
405 : {
406 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 : */
415 : void
416 6 : PsqlVarEnumError(const char *name, const char *value, const char *suggestions)
417 : {
418 6 : pg_log_error("unrecognized value \"%s\" for \"%s\"\n"
419 : "Available values are: %s.",
420 : value, name, suggestions);
421 6 : }
|