1 | /*------------------------------------------------------------------------- |
2 | * |
3 | * bool.c |
4 | * Functions for the built-in type "bool". |
5 | * |
6 | * Portions Copyright (c) 1996-2019, 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 |
30 | parse_bool(const char *value, bool *result) |
31 | { |
32 | return parse_bool_with_len(value, strlen(value), result); |
33 | } |
34 | |
35 | bool |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 * |
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 |
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 |
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 |
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 |
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 | |