Branch data Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * char.c
4 : : * Functions for the built-in type "char" (not to be confused with
5 : : * bpchar, which is the SQL CHAR(n) type).
6 : : *
7 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
8 : : * Portions Copyright (c) 1994, Regents of the University of California
9 : : *
10 : : *
11 : : * IDENTIFICATION
12 : : * src/backend/utils/adt/char.c
13 : : *
14 : : *-------------------------------------------------------------------------
15 : : */
16 : : #include "postgres.h"
17 : :
18 : : #include <limits.h>
19 : :
20 : : #include "libpq/pqformat.h"
21 : : #include "utils/fmgrprotos.h"
22 : : #include "varatt.h"
23 : :
24 : : #define ISOCTAL(c) (((c) >= '0') && ((c) <= '7'))
25 : : #define TOOCTAL(c) ((c) + '0')
26 : : #define FROMOCTAL(c) ((unsigned char) (c) - '0')
27 : :
28 : :
29 : : /*****************************************************************************
30 : : * USER I/O ROUTINES *
31 : : *****************************************************************************/
32 : :
33 : : /*
34 : : * charin - converts "x" to 'x'
35 : : *
36 : : * This accepts the formats charout produces. If we have multibyte input
37 : : * that is not in the form '\ooo', then we take its first byte as the value
38 : : * and silently discard the rest; this is a backwards-compatibility provision.
39 : : */
40 : : Datum
41 : 24156 : charin(PG_FUNCTION_ARGS)
42 : : {
43 : 24156 : char *ch = PG_GETARG_CSTRING(0);
44 : :
45 [ + + + - ]: 24156 : if (strlen(ch) == 4 && ch[0] == '\\' &&
46 [ + - + - : 4 : ISOCTAL(ch[1]) && ISOCTAL(ch[2]) && ISOCTAL(ch[3]))
+ - + - +
- - + ]
47 : 4 : PG_RETURN_CHAR((FROMOCTAL(ch[1]) << 6) +
48 : : (FROMOCTAL(ch[2]) << 3) +
49 : : FROMOCTAL(ch[3]));
50 : : /* This will do the right thing for a zero-length input string */
51 : 24152 : PG_RETURN_CHAR(ch[0]);
52 : 24156 : }
53 : :
54 : : /*
55 : : * charout - converts 'x' to "x"
56 : : *
57 : : * The possible output formats are:
58 : : * 1. 0x00 is represented as an empty string.
59 : : * 2. 0x01..0x7F are represented as a single ASCII byte.
60 : : * 3. 0x80..0xFF are represented as \ooo (backslash and 3 octal digits).
61 : : * Case 3 is meant to match the traditional "escape" format of bytea.
62 : : */
63 : : Datum
64 : 6668 : charout(PG_FUNCTION_ARGS)
65 : : {
66 : 6668 : char ch = PG_GETARG_CHAR(0);
67 : 6668 : char *result = (char *) palloc(5);
68 : :
69 [ + + ]: 6668 : if (IS_HIGHBIT_SET(ch))
70 : : {
71 : 2 : result[0] = '\\';
72 : 2 : result[1] = TOOCTAL(((unsigned char) ch) >> 6);
73 : 2 : result[2] = TOOCTAL((((unsigned char) ch) >> 3) & 07);
74 : 2 : result[3] = TOOCTAL(((unsigned char) ch) & 07);
75 : 2 : result[4] = '\0';
76 : 2 : }
77 : : else
78 : : {
79 : : /* This produces acceptable results for 0x00 as well */
80 : 6666 : result[0] = ch;
81 : 6666 : result[1] = '\0';
82 : : }
83 : 13336 : PG_RETURN_CSTRING(result);
84 : 6668 : }
85 : :
86 : : /*
87 : : * charrecv - converts external binary format to char
88 : : *
89 : : * The external representation is one byte, with no character set
90 : : * conversion. This is somewhat dubious, perhaps, but in many
91 : : * cases people use char for a 1-byte binary type.
92 : : */
93 : : Datum
94 : 0 : charrecv(PG_FUNCTION_ARGS)
95 : : {
96 : 0 : StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
97 : :
98 : 0 : PG_RETURN_CHAR(pq_getmsgbyte(buf));
99 : 0 : }
100 : :
101 : : /*
102 : : * charsend - converts char to binary format
103 : : */
104 : : Datum
105 : 0 : charsend(PG_FUNCTION_ARGS)
106 : : {
107 : 0 : char arg1 = PG_GETARG_CHAR(0);
108 : 0 : StringInfoData buf;
109 : :
110 : 0 : pq_begintypsend(&buf);
111 : 0 : pq_sendbyte(&buf, arg1);
112 : 0 : PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
113 : 0 : }
114 : :
115 : : /*****************************************************************************
116 : : * PUBLIC ROUTINES *
117 : : *****************************************************************************/
118 : :
119 : : /*
120 : : * NOTE: comparisons are done as though char is unsigned (uint8).
121 : : * Conversions to and from integer are done as though char is signed (int8).
122 : : *
123 : : * You wanted consistency?
124 : : */
125 : :
126 : : Datum
127 : 478609 : chareq(PG_FUNCTION_ARGS)
128 : : {
129 : 478609 : char arg1 = PG_GETARG_CHAR(0);
130 : 478609 : char arg2 = PG_GETARG_CHAR(1);
131 : :
132 : 957218 : PG_RETURN_BOOL(arg1 == arg2);
133 : 478609 : }
134 : :
135 : : Datum
136 : 187047 : charne(PG_FUNCTION_ARGS)
137 : : {
138 : 187047 : char arg1 = PG_GETARG_CHAR(0);
139 : 187047 : char arg2 = PG_GETARG_CHAR(1);
140 : :
141 : 374094 : PG_RETURN_BOOL(arg1 != arg2);
142 : 187047 : }
143 : :
144 : : Datum
145 : 516 : charlt(PG_FUNCTION_ARGS)
146 : : {
147 : 516 : char arg1 = PG_GETARG_CHAR(0);
148 : 516 : char arg2 = PG_GETARG_CHAR(1);
149 : :
150 : 1032 : PG_RETURN_BOOL((uint8) arg1 < (uint8) arg2);
151 : 516 : }
152 : :
153 : : Datum
154 : 404 : charle(PG_FUNCTION_ARGS)
155 : : {
156 : 404 : char arg1 = PG_GETARG_CHAR(0);
157 : 404 : char arg2 = PG_GETARG_CHAR(1);
158 : :
159 : 808 : PG_RETURN_BOOL((uint8) arg1 <= (uint8) arg2);
160 : 404 : }
161 : :
162 : : Datum
163 : 507 : chargt(PG_FUNCTION_ARGS)
164 : : {
165 : 507 : char arg1 = PG_GETARG_CHAR(0);
166 : 507 : char arg2 = PG_GETARG_CHAR(1);
167 : :
168 : 1014 : PG_RETURN_BOOL((uint8) arg1 > (uint8) arg2);
169 : 507 : }
170 : :
171 : : Datum
172 : 355 : charge(PG_FUNCTION_ARGS)
173 : : {
174 : 355 : char arg1 = PG_GETARG_CHAR(0);
175 : 355 : char arg2 = PG_GETARG_CHAR(1);
176 : :
177 : 710 : PG_RETURN_BOOL((uint8) arg1 >= (uint8) arg2);
178 : 355 : }
179 : :
180 : :
181 : : Datum
182 : 0 : chartoi4(PG_FUNCTION_ARGS)
183 : : {
184 : 0 : char arg1 = PG_GETARG_CHAR(0);
185 : :
186 : 0 : PG_RETURN_INT32((int32) ((int8) arg1));
187 : 0 : }
188 : :
189 : : Datum
190 : 0 : i4tochar(PG_FUNCTION_ARGS)
191 : : {
192 : 0 : int32 arg1 = PG_GETARG_INT32(0);
193 : :
194 [ # # ]: 0 : if (arg1 < SCHAR_MIN || arg1 > SCHAR_MAX)
195 [ # # # # ]: 0 : ereport(ERROR,
196 : : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
197 : : errmsg("\"char\" out of range")));
198 : :
199 : 0 : PG_RETURN_CHAR((int8) arg1);
200 : 0 : }
201 : :
202 : :
203 : : Datum
204 : 352 : text_char(PG_FUNCTION_ARGS)
205 : : {
206 : 352 : text *arg1 = PG_GETARG_TEXT_PP(0);
207 : 352 : char *ch = VARDATA_ANY(arg1);
208 : 352 : char result;
209 : :
210 : : /*
211 : : * Conversion rules are the same as in charin(), but here we need to
212 : : * handle the empty-string case honestly.
213 : : */
214 [ + + + - ]: 352 : if (VARSIZE_ANY_EXHDR(arg1) == 4 && ch[0] == '\\' &&
215 [ + - + - : 1 : ISOCTAL(ch[1]) && ISOCTAL(ch[2]) && ISOCTAL(ch[3]))
+ - + - +
- - + ]
216 : 3 : result = (FROMOCTAL(ch[1]) << 6) +
217 : 2 : (FROMOCTAL(ch[2]) << 3) +
218 : 1 : FROMOCTAL(ch[3]);
219 [ + + ]: 351 : else if (VARSIZE_ANY_EXHDR(arg1) > 0)
220 : 350 : result = ch[0];
221 : : else
222 : 1 : result = '\0';
223 : :
224 : 704 : PG_RETURN_CHAR(result);
225 : 352 : }
226 : :
227 : : Datum
228 : 3 : char_text(PG_FUNCTION_ARGS)
229 : : {
230 : 3 : char arg1 = PG_GETARG_CHAR(0);
231 : 3 : text *result = palloc(VARHDRSZ + 4);
232 : :
233 : : /*
234 : : * Conversion rules are the same as in charout(), but here we need to be
235 : : * honest about converting 0x00 to an empty string.
236 : : */
237 [ + + ]: 3 : if (IS_HIGHBIT_SET(arg1))
238 : : {
239 : 1 : SET_VARSIZE(result, VARHDRSZ + 4);
240 : 1 : (VARDATA(result))[0] = '\\';
241 : 1 : (VARDATA(result))[1] = TOOCTAL(((unsigned char) arg1) >> 6);
242 : 1 : (VARDATA(result))[2] = TOOCTAL((((unsigned char) arg1) >> 3) & 07);
243 : 1 : (VARDATA(result))[3] = TOOCTAL(((unsigned char) arg1) & 07);
244 : 1 : }
245 [ + + ]: 2 : else if (arg1 != '\0')
246 : : {
247 : 1 : SET_VARSIZE(result, VARHDRSZ + 1);
248 : 1 : *(VARDATA(result)) = arg1;
249 : 1 : }
250 : : else
251 : 1 : SET_VARSIZE(result, VARHDRSZ);
252 : :
253 : 6 : PG_RETURN_TEXT_P(result);
254 : 3 : }
|