1 /*-------------------------------------------------------------------------
2  *
3  * bool.c
4  *	  Functions for the built-in type "bool".
5  *
6  * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *	  src/backend/utils/adt/bool.c
12  *
13  *-------------------------------------------------------------------------
14  */
15 
16 #include "postgres.h"
17 
18 #include <ctype.h>
19 
20 #include "libpq/pqformat.h"
21 #include "utils/builtins.h"
22 
23 /*
24  * Try to interpret value as boolean value.  Valid values are: true,
25  * false, yes, no, on, off, 1, 0; as well as unique prefixes thereof.
26  * If the string parses okay, return true, else false.
27  * If okay and result is not NULL, return the value in *result.
28  */
29 bool
parse_bool(const char * value,bool * result)30 parse_bool(const char *value, bool *result)
31 {
32 	return parse_bool_with_len(value, strlen(value), result);
33 }
34 
35 bool
parse_bool_with_len(const char * value,size_t len,bool * result)36 parse_bool_with_len(const char *value, size_t len, bool *result)
37 {
38 	switch (*value)
39 	{
40 		case 't':
41 		case 'T':
42 			if (pg_strncasecmp(value, "true", len) == 0)
43 			{
44 				if (result)
45 					*result = true;
46 				return true;
47 			}
48 			break;
49 		case 'f':
50 		case 'F':
51 			if (pg_strncasecmp(value, "false", len) == 0)
52 			{
53 				if (result)
54 					*result = false;
55 				return true;
56 			}
57 			break;
58 		case 'y':
59 		case 'Y':
60 			if (pg_strncasecmp(value, "yes", len) == 0)
61 			{
62 				if (result)
63 					*result = true;
64 				return true;
65 			}
66 			break;
67 		case 'n':
68 		case 'N':
69 			if (pg_strncasecmp(value, "no", len) == 0)
70 			{
71 				if (result)
72 					*result = false;
73 				return true;
74 			}
75 			break;
76 		case 'o':
77 		case 'O':
78 			/* 'o' is not unique enough */
79 			if (pg_strncasecmp(value, "on", (len > 2 ? len : 2)) == 0)
80 			{
81 				if (result)
82 					*result = true;
83 				return true;
84 			}
85 			else if (pg_strncasecmp(value, "off", (len > 2 ? len : 2)) == 0)
86 			{
87 				if (result)
88 					*result = false;
89 				return true;
90 			}
91 			break;
92 		case '1':
93 			if (len == 1)
94 			{
95 				if (result)
96 					*result = true;
97 				return true;
98 			}
99 			break;
100 		case '0':
101 			if (len == 1)
102 			{
103 				if (result)
104 					*result = false;
105 				return true;
106 			}
107 			break;
108 		default:
109 			break;
110 	}
111 
112 	if (result)
113 		*result = false;		/* suppress compiler warning */
114 	return false;
115 }
116 
117 /*****************************************************************************
118  *	 USER I/O ROUTINES														 *
119  *****************************************************************************/
120 
121 /*
122  *		boolin			- converts "t" or "f" to 1 or 0
123  *
124  * Check explicitly for "true/false" and TRUE/FALSE, 1/0, YES/NO, ON/OFF.
125  * Reject other values.
126  *
127  * In the switch statement, check the most-used possibilities first.
128  */
129 Datum
boolin(PG_FUNCTION_ARGS)130 boolin(PG_FUNCTION_ARGS)
131 {
132 	const char *in_str = PG_GETARG_CSTRING(0);
133 	const char *str;
134 	size_t		len;
135 	bool		result;
136 
137 	/*
138 	 * Skip leading and trailing whitespace
139 	 */
140 	str = in_str;
141 	while (isspace((unsigned char) *str))
142 		str++;
143 
144 	len = strlen(str);
145 	while (len > 0 && isspace((unsigned char) str[len - 1]))
146 		len--;
147 
148 	if (parse_bool_with_len(str, len, &result))
149 		PG_RETURN_BOOL(result);
150 
151 	ereport(ERROR,
152 			(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
153 			 errmsg("invalid input syntax for type %s: \"%s\"",
154 					"boolean", in_str)));
155 
156 	/* not reached */
157 	PG_RETURN_BOOL(false);
158 }
159 
160 /*
161  *		boolout			- converts 1 or 0 to "t" or "f"
162  */
163 Datum
boolout(PG_FUNCTION_ARGS)164 boolout(PG_FUNCTION_ARGS)
165 {
166 	bool		b = PG_GETARG_BOOL(0);
167 	char	   *result = (char *) palloc(2);
168 
169 	result[0] = (b) ? 't' : 'f';
170 	result[1] = '\0';
171 	PG_RETURN_CSTRING(result);
172 }
173 
174 /*
175  *		boolrecv			- converts external binary format to bool
176  *
177  * The external representation is one byte.  Any nonzero value is taken
178  * as "true".
179  */
180 Datum
boolrecv(PG_FUNCTION_ARGS)181 boolrecv(PG_FUNCTION_ARGS)
182 {
183 	StringInfo	buf = (StringInfo) PG_GETARG_POINTER(0);
184 	int			ext;
185 
186 	ext = pq_getmsgbyte(buf);
187 	PG_RETURN_BOOL((ext != 0) ? true : false);
188 }
189 
190 /*
191  *		boolsend			- converts bool to binary format
192  */
193 Datum
boolsend(PG_FUNCTION_ARGS)194 boolsend(PG_FUNCTION_ARGS)
195 {
196 	bool		arg1 = PG_GETARG_BOOL(0);
197 	StringInfoData buf;
198 
199 	pq_begintypsend(&buf);
200 	pq_sendbyte(&buf, arg1 ? 1 : 0);
201 	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
202 }
203 
204 /*
205  *		booltext			- cast function for bool => text
206  *
207  * We need this because it's different from the behavior of boolout();
208  * this function follows the SQL-spec result (except for producing lower case)
209  */
210 Datum
booltext(PG_FUNCTION_ARGS)211 booltext(PG_FUNCTION_ARGS)
212 {
213 	bool		arg1 = PG_GETARG_BOOL(0);
214 	const char *str;
215 
216 	if (arg1)
217 		str = "true";
218 	else
219 		str = "false";
220 
221 	PG_RETURN_TEXT_P(cstring_to_text(str));
222 }
223 
224 
225 /*****************************************************************************
226  *	 PUBLIC ROUTINES														 *
227  *****************************************************************************/
228 
229 Datum
booleq(PG_FUNCTION_ARGS)230 booleq(PG_FUNCTION_ARGS)
231 {
232 	bool		arg1 = PG_GETARG_BOOL(0);
233 	bool		arg2 = PG_GETARG_BOOL(1);
234 
235 	PG_RETURN_BOOL(arg1 == arg2);
236 }
237 
238 Datum
boolne(PG_FUNCTION_ARGS)239 boolne(PG_FUNCTION_ARGS)
240 {
241 	bool		arg1 = PG_GETARG_BOOL(0);
242 	bool		arg2 = PG_GETARG_BOOL(1);
243 
244 	PG_RETURN_BOOL(arg1 != arg2);
245 }
246 
247 Datum
boollt(PG_FUNCTION_ARGS)248 boollt(PG_FUNCTION_ARGS)
249 {
250 	bool		arg1 = PG_GETARG_BOOL(0);
251 	bool		arg2 = PG_GETARG_BOOL(1);
252 
253 	PG_RETURN_BOOL(arg1 < arg2);
254 }
255 
256 Datum
boolgt(PG_FUNCTION_ARGS)257 boolgt(PG_FUNCTION_ARGS)
258 {
259 	bool		arg1 = PG_GETARG_BOOL(0);
260 	bool		arg2 = PG_GETARG_BOOL(1);
261 
262 	PG_RETURN_BOOL(arg1 > arg2);
263 }
264 
265 Datum
boolle(PG_FUNCTION_ARGS)266 boolle(PG_FUNCTION_ARGS)
267 {
268 	bool		arg1 = PG_GETARG_BOOL(0);
269 	bool		arg2 = PG_GETARG_BOOL(1);
270 
271 	PG_RETURN_BOOL(arg1 <= arg2);
272 }
273 
274 Datum
boolge(PG_FUNCTION_ARGS)275 boolge(PG_FUNCTION_ARGS)
276 {
277 	bool		arg1 = PG_GETARG_BOOL(0);
278 	bool		arg2 = PG_GETARG_BOOL(1);
279 
280 	PG_RETURN_BOOL(arg1 >= arg2);
281 }
282 
283 /*
284  * boolean-and and boolean-or aggregates.
285  */
286 
287 /*
288  * Function for standard EVERY aggregate conforming to SQL 2003.
289  * The aggregate is also named bool_and for consistency.
290  *
291  * Note: this is only used in plain aggregate mode, not moving-aggregate mode.
292  */
293 Datum
booland_statefunc(PG_FUNCTION_ARGS)294 booland_statefunc(PG_FUNCTION_ARGS)
295 {
296 	PG_RETURN_BOOL(PG_GETARG_BOOL(0) && PG_GETARG_BOOL(1));
297 }
298 
299 /*
300  * Function for standard ANY/SOME aggregate conforming to SQL 2003.
301  * The aggregate is named bool_or, because ANY/SOME have parsing conflicts.
302  *
303  * Note: this is only used in plain aggregate mode, not moving-aggregate mode.
304  */
305 Datum
boolor_statefunc(PG_FUNCTION_ARGS)306 boolor_statefunc(PG_FUNCTION_ARGS)
307 {
308 	PG_RETURN_BOOL(PG_GETARG_BOOL(0) || PG_GETARG_BOOL(1));
309 }
310 
311 typedef struct BoolAggState
312 {
313 	int64		aggcount;		/* number of non-null values aggregated */
314 	int64		aggtrue;		/* number of values aggregated that are true */
315 } BoolAggState;
316 
317 static BoolAggState *
makeBoolAggState(FunctionCallInfo fcinfo)318 makeBoolAggState(FunctionCallInfo fcinfo)
319 {
320 	BoolAggState *state;
321 	MemoryContext agg_context;
322 
323 	if (!AggCheckCallContext(fcinfo, &agg_context))
324 		elog(ERROR, "aggregate function called in non-aggregate context");
325 
326 	state = (BoolAggState *) MemoryContextAlloc(agg_context,
327 												sizeof(BoolAggState));
328 	state->aggcount = 0;
329 	state->aggtrue = 0;
330 
331 	return state;
332 }
333 
334 Datum
bool_accum(PG_FUNCTION_ARGS)335 bool_accum(PG_FUNCTION_ARGS)
336 {
337 	BoolAggState *state;
338 
339 	state = PG_ARGISNULL(0) ? NULL : (BoolAggState *) PG_GETARG_POINTER(0);
340 
341 	/* Create the state data on first call */
342 	if (state == NULL)
343 		state = makeBoolAggState(fcinfo);
344 
345 	if (!PG_ARGISNULL(1))
346 	{
347 		state->aggcount++;
348 		if (PG_GETARG_BOOL(1))
349 			state->aggtrue++;
350 	}
351 
352 	PG_RETURN_POINTER(state);
353 }
354 
355 Datum
bool_accum_inv(PG_FUNCTION_ARGS)356 bool_accum_inv(PG_FUNCTION_ARGS)
357 {
358 	BoolAggState *state;
359 
360 	state = PG_ARGISNULL(0) ? NULL : (BoolAggState *) PG_GETARG_POINTER(0);
361 
362 	/* bool_accum should have created the state data */
363 	if (state == NULL)
364 		elog(ERROR, "bool_accum_inv called with NULL state");
365 
366 	if (!PG_ARGISNULL(1))
367 	{
368 		state->aggcount--;
369 		if (PG_GETARG_BOOL(1))
370 			state->aggtrue--;
371 	}
372 
373 	PG_RETURN_POINTER(state);
374 }
375 
376 Datum
bool_alltrue(PG_FUNCTION_ARGS)377 bool_alltrue(PG_FUNCTION_ARGS)
378 {
379 	BoolAggState *state;
380 
381 	state = PG_ARGISNULL(0) ? NULL : (BoolAggState *) PG_GETARG_POINTER(0);
382 
383 	/* if there were no non-null values, return NULL */
384 	if (state == NULL || state->aggcount == 0)
385 		PG_RETURN_NULL();
386 
387 	/* true if all non-null values are true */
388 	PG_RETURN_BOOL(state->aggtrue == state->aggcount);
389 }
390 
391 Datum
bool_anytrue(PG_FUNCTION_ARGS)392 bool_anytrue(PG_FUNCTION_ARGS)
393 {
394 	BoolAggState *state;
395 
396 	state = PG_ARGISNULL(0) ? NULL : (BoolAggState *) PG_GETARG_POINTER(0);
397 
398 	/* if there were no non-null values, return NULL */
399 	if (state == NULL || state->aggcount == 0)
400 		PG_RETURN_NULL();
401 
402 	/* true if any non-null value is true */
403 	PG_RETURN_BOOL(state->aggtrue > 0);
404 }
405