1 /*
2  * psql - the PostgreSQL interactive terminal
3  *
4  * Copyright (c) 2000-2021, 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
valid_variable_name(const char * name)22 valid_variable_name(const char *name)
23 {
24 	const unsigned char *ptr = (const unsigned char *) name;
25 
26 	/* Mustn't be zero-length */
27 	if (*ptr == '\0')
28 		return false;
29 
30 	while (*ptr)
31 	{
32 		if (IS_HIGHBIT_SET(*ptr) ||
33 			strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz"
34 				   "_0123456789", *ptr) != NULL)
35 			ptr++;
36 		else
37 			return false;
38 	}
39 
40 	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 results of PrintVariables() more pleasing.
49  */
50 VariableSpace
CreateVariableSpace(void)51 CreateVariableSpace(void)
52 {
53 	struct _variable *ptr;
54 
55 	ptr = pg_malloc(sizeof *ptr);
56 	ptr->name = NULL;
57 	ptr->value = NULL;
58 	ptr->substitute_hook = NULL;
59 	ptr->assign_hook = NULL;
60 	ptr->next = NULL;
61 
62 	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 *
GetVariable(VariableSpace space,const char * name)71 GetVariable(VariableSpace space, const char *name)
72 {
73 	struct _variable *current;
74 
75 	if (!space)
76 		return NULL;
77 
78 	for (current = space->next; current; current = current->next)
79 	{
80 		int			cmp = strcmp(current->name, name);
81 
82 		if (cmp == 0)
83 		{
84 			/* this is correct answer when value is NULL, too */
85 			return current->value;
86 		}
87 		if (cmp > 0)
88 			break;				/* it's not there */
89 	}
90 
91 	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
ParseVariableBool(const char * value,const char * name,bool * result)107 ParseVariableBool(const char *value, const char *name, bool *result)
108 {
109 	size_t		len;
110 	bool		valid = true;
111 
112 	/* Treat "unset" as an empty string, which will lead to error below */
113 	if (value == NULL)
114 		value = "";
115 
116 	len = strlen(value);
117 
118 	if (len > 0 && pg_strncasecmp(value, "true", len) == 0)
119 		*result = true;
120 	else if (len > 0 && pg_strncasecmp(value, "false", len) == 0)
121 		*result = false;
122 	else if (len > 0 && pg_strncasecmp(value, "yes", len) == 0)
123 		*result = true;
124 	else if (len > 0 && pg_strncasecmp(value, "no", len) == 0)
125 		*result = false;
126 	/* 'o' is not unique enough */
127 	else if (pg_strncasecmp(value, "on", (len > 2 ? len : 2)) == 0)
128 		*result = true;
129 	else if (pg_strncasecmp(value, "off", (len > 2 ? len : 2)) == 0)
130 		*result = false;
131 	else if (pg_strcasecmp(value, "1") == 0)
132 		*result = true;
133 	else if (pg_strcasecmp(value, "0") == 0)
134 		*result = false;
135 	else
136 	{
137 		/* string is not recognized; don't clobber *result */
138 		if (name)
139 			pg_log_error("unrecognized value \"%s\" for \"%s\": Boolean expected",
140 						 value, name);
141 		valid = false;
142 	}
143 	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
ParseVariableNum(const char * value,const char * name,int * result)156 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 	if (value == NULL)
163 		value = "";
164 
165 	errno = 0;
166 	numval = strtol(value, &end, 0);
167 	if (errno == 0 && *end == '\0' && end != value && numval == (int) numval)
168 	{
169 		*result = (int) numval;
170 		return true;
171 	}
172 	else
173 	{
174 		/* string is not recognized; don't clobber *result */
175 		if (name)
176 			pg_log_error("invalid value \"%s\" for \"%s\": integer expected",
177 						 value, name);
178 		return false;
179 	}
180 }
181 
182 /*
183  * Print values of all variables.
184  */
185 void
PrintVariables(VariableSpace space)186 PrintVariables(VariableSpace space)
187 {
188 	struct _variable *ptr;
189 
190 	if (!space)
191 		return;
192 
193 	for (ptr = space->next; ptr; ptr = ptr->next)
194 	{
195 		if (ptr->value)
196 			printf("%s = '%s'\n", ptr->name, ptr->value);
197 		if (cancel_pressed)
198 			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
SetVariable(VariableSpace space,const char * name,const char * value)211 SetVariable(VariableSpace space, const char *name, const char *value)
212 {
213 	struct _variable *current,
214 			   *previous;
215 
216 	if (!space || !name)
217 		return false;
218 
219 	if (!valid_variable_name(name))
220 	{
221 		/* Deletion of non-existent variable is not an error */
222 		if (!value)
223 			return true;
224 		pg_log_error("invalid variable name: \"%s\"", name);
225 		return false;
226 	}
227 
228 	for (previous = space, current = space->next;
229 		 current;
230 		 previous = current, current = current->next)
231 	{
232 		int			cmp = strcmp(current->name, name);
233 
234 		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 			char	   *new_value = value ? pg_strdup(value) : NULL;
246 			bool		confirmed;
247 
248 			if (current->substitute_hook)
249 				new_value = current->substitute_hook(new_value);
250 
251 			if (current->assign_hook)
252 				confirmed = current->assign_hook(new_value);
253 			else
254 				confirmed = true;
255 
256 			if (confirmed)
257 			{
258 				if (current->value)
259 					pg_free(current->value);
260 				current->value = new_value;
261 
262 				/*
263 				 * If we deleted the value, and there are no hooks to
264 				 * remember, we can discard the variable altogether.
265 				 */
266 				if (new_value == NULL &&
267 					current->substitute_hook == NULL &&
268 					current->assign_hook == NULL)
269 				{
270 					previous->next = current->next;
271 					free(current->name);
272 					free(current);
273 				}
274 			}
275 			else if (new_value)
276 				pg_free(new_value); /* current->value is left unchanged */
277 
278 			return confirmed;
279 		}
280 		if (cmp > 0)
281 			break;				/* it's not there */
282 	}
283 
284 	/* not present, make new entry ... unless we were asked to delete */
285 	if (value)
286 	{
287 		current = pg_malloc(sizeof *current);
288 		current->name = pg_strdup(name);
289 		current->value = pg_strdup(value);
290 		current->substitute_hook = NULL;
291 		current->assign_hook = NULL;
292 		current->next = previous->next;
293 		previous->next = current;
294 	}
295 	return true;
296 }
297 
298 /*
299  * Attach substitute and/or assign hook functions to the named variable.
300  * If you need only one hook, pass NULL for the other.
301  *
302  * If the variable doesn't already exist, create it with value NULL, just so
303  * we have a place to store the hook function(s).  (The substitute hook might
304  * immediately change the NULL to something else; if not, this state is
305  * externally the same as the variable not being defined.)
306  *
307  * The substitute hook, if given, is immediately called on the variable's
308  * value.  Then the assign hook, if given, is called on the variable's value.
309  * This is meant to let it update any derived psql state.  If the assign hook
310  * doesn't like the current value, it will print a message to that effect,
311  * but we'll ignore it.  Generally we do not expect any such failure here,
312  * because this should get called before any user-supplied value is assigned.
313  */
314 void
SetVariableHooks(VariableSpace space,const char * name,VariableSubstituteHook shook,VariableAssignHook ahook)315 SetVariableHooks(VariableSpace space, const char *name,
316 				 VariableSubstituteHook shook,
317 				 VariableAssignHook ahook)
318 {
319 	struct _variable *current,
320 			   *previous;
321 
322 	if (!space || !name)
323 		return;
324 
325 	if (!valid_variable_name(name))
326 		return;
327 
328 	for (previous = space, current = space->next;
329 		 current;
330 		 previous = current, current = current->next)
331 	{
332 		int			cmp = strcmp(current->name, name);
333 
334 		if (cmp == 0)
335 		{
336 			/* found entry, so update */
337 			current->substitute_hook = shook;
338 			current->assign_hook = ahook;
339 			if (shook)
340 				current->value = (*shook) (current->value);
341 			if (ahook)
342 				(void) (*ahook) (current->value);
343 			return;
344 		}
345 		if (cmp > 0)
346 			break;				/* it's not there */
347 	}
348 
349 	/* not present, make new entry */
350 	current = pg_malloc(sizeof *current);
351 	current->name = pg_strdup(name);
352 	current->value = NULL;
353 	current->substitute_hook = shook;
354 	current->assign_hook = ahook;
355 	current->next = previous->next;
356 	previous->next = current;
357 	if (shook)
358 		current->value = (*shook) (current->value);
359 	if (ahook)
360 		(void) (*ahook) (current->value);
361 }
362 
363 /*
364  * Return true iff the named variable has substitute and/or assign hook
365  * functions.
366  */
367 bool
VariableHasHook(VariableSpace space,const char * name)368 VariableHasHook(VariableSpace space, const char *name)
369 {
370 	struct _variable *current;
371 
372 	Assert(space);
373 	Assert(name);
374 
375 	for (current = space->next; current; current = current->next)
376 	{
377 		int			cmp = strcmp(current->name, name);
378 
379 		if (cmp == 0)
380 			return (current->substitute_hook != NULL ||
381 					current->assign_hook != NULL);
382 		if (cmp > 0)
383 			break;				/* it's not there */
384 	}
385 
386 	return false;
387 }
388 
389 /*
390  * Convenience function to set a variable's value to "on".
391  */
392 bool
SetVariableBool(VariableSpace space,const char * name)393 SetVariableBool(VariableSpace space, const char *name)
394 {
395 	return SetVariable(space, name, "on");
396 }
397 
398 /*
399  * Attempt to delete variable.
400  *
401  * If unsuccessful, print a message and return "false".
402  * Deleting a nonexistent variable is not an error.
403  */
404 bool
DeleteVariable(VariableSpace space,const char * name)405 DeleteVariable(VariableSpace space, const char *name)
406 {
407 	return SetVariable(space, name, NULL);
408 }
409 
410 /*
411  * Emit error with suggestions for variables or commands
412  * accepting enum-style arguments.
413  * This function just exists to standardize the wording.
414  * suggestions should follow the format "fee, fi, fo, fum".
415  */
416 void
PsqlVarEnumError(const char * name,const char * value,const char * suggestions)417 PsqlVarEnumError(const char *name, const char *value, const char *suggestions)
418 {
419 	pg_log_error("unrecognized value \"%s\" for \"%s\"\n"
420 				 "Available values are: %s.",
421 				 value, name, suggestions);
422 }
423