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 | */ |
23 | static bool |
24 | valid_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 | */ |
52 | VariableSpace |
53 | CreateVariableSpace(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 | */ |
72 | const char * |
73 | GetVariable(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 | */ |
108 | bool |
109 | ParseVariableBool(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 | */ |
157 | bool |
158 | 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 | 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 | */ |
187 | void |
188 | PrintVariables(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 | */ |
212 | bool |
213 | SetVariable(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 | */ |
316 | void |
317 | SetVariableHooks(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 | */ |
368 | bool |
369 | SetVariableBool(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 | */ |
380 | bool |
381 | DeleteVariable(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 | */ |
392 | void |
393 | PsqlVarEnumError(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 | |