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 856127 : charin(PG_FUNCTION_ARGS)
42 : {
43 856127 : char *ch = PG_GETARG_CSTRING(0);
44 :
45 856127 : if (strlen(ch) == 4 && ch[0] == '\\' &&
46 12 : ISOCTAL(ch[1]) && ISOCTAL(ch[2]) && ISOCTAL(ch[3]))
47 12 : 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 856115 : PG_RETURN_CHAR(ch[0]);
52 : }
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 2013929 : charout(PG_FUNCTION_ARGS)
65 : {
66 2013929 : char ch = PG_GETARG_CHAR(0);
67 2013929 : char *result = (char *) palloc(5);
68 :
69 2013929 : if (IS_HIGHBIT_SET(ch))
70 : {
71 6 : result[0] = '\\';
72 6 : result[1] = TOOCTAL(((unsigned char) ch) >> 6);
73 6 : result[2] = TOOCTAL((((unsigned char) ch) >> 3) & 07);
74 6 : result[3] = TOOCTAL(((unsigned char) ch) & 07);
75 6 : result[4] = '\0';
76 : }
77 : else
78 : {
79 : /* This produces acceptable results for 0x00 as well */
80 2013923 : result[0] = ch;
81 2013923 : result[1] = '\0';
82 : }
83 2013929 : PG_RETURN_CSTRING(result);
84 : }
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 : }
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 : StringInfoData buf;
109 :
110 0 : pq_begintypsend(&buf);
111 0 : pq_sendbyte(&buf, arg1);
112 0 : PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
113 : }
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 5799112 : chareq(PG_FUNCTION_ARGS)
128 : {
129 5799112 : char arg1 = PG_GETARG_CHAR(0);
130 5799112 : char arg2 = PG_GETARG_CHAR(1);
131 :
132 5799112 : PG_RETURN_BOOL(arg1 == arg2);
133 : }
134 :
135 : Datum
136 2588858 : charne(PG_FUNCTION_ARGS)
137 : {
138 2588858 : char arg1 = PG_GETARG_CHAR(0);
139 2588858 : char arg2 = PG_GETARG_CHAR(1);
140 :
141 2588858 : PG_RETURN_BOOL(arg1 != arg2);
142 : }
143 :
144 : Datum
145 1548 : charlt(PG_FUNCTION_ARGS)
146 : {
147 1548 : char arg1 = PG_GETARG_CHAR(0);
148 1548 : char arg2 = PG_GETARG_CHAR(1);
149 :
150 1548 : PG_RETURN_BOOL((uint8) arg1 < (uint8) arg2);
151 : }
152 :
153 : Datum
154 1212 : charle(PG_FUNCTION_ARGS)
155 : {
156 1212 : char arg1 = PG_GETARG_CHAR(0);
157 1212 : char arg2 = PG_GETARG_CHAR(1);
158 :
159 1212 : PG_RETURN_BOOL((uint8) arg1 <= (uint8) arg2);
160 : }
161 :
162 : Datum
163 1521 : chargt(PG_FUNCTION_ARGS)
164 : {
165 1521 : char arg1 = PG_GETARG_CHAR(0);
166 1521 : char arg2 = PG_GETARG_CHAR(1);
167 :
168 1521 : PG_RETURN_BOOL((uint8) arg1 > (uint8) arg2);
169 : }
170 :
171 : Datum
172 1065 : charge(PG_FUNCTION_ARGS)
173 : {
174 1065 : char arg1 = PG_GETARG_CHAR(0);
175 1065 : char arg2 = PG_GETARG_CHAR(1);
176 :
177 1065 : PG_RETURN_BOOL((uint8) arg1 >= (uint8) arg2);
178 : }
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 : }
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 : }
201 :
202 :
203 : Datum
204 7075 : text_char(PG_FUNCTION_ARGS)
205 : {
206 7075 : text *arg1 = PG_GETARG_TEXT_PP(0);
207 7075 : char *ch = VARDATA_ANY(arg1);
208 : 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 7075 : if (VARSIZE_ANY_EXHDR(arg1) == 4 && ch[0] == '\\' &&
215 3 : ISOCTAL(ch[1]) && ISOCTAL(ch[2]) && ISOCTAL(ch[3]))
216 3 : result = (FROMOCTAL(ch[1]) << 6) +
217 3 : (FROMOCTAL(ch[2]) << 3) +
218 3 : FROMOCTAL(ch[3]);
219 7072 : else if (VARSIZE_ANY_EXHDR(arg1) > 0)
220 7069 : result = ch[0];
221 : else
222 3 : result = '\0';
223 :
224 7075 : PG_RETURN_CHAR(result);
225 : }
226 :
227 : Datum
228 35531 : char_text(PG_FUNCTION_ARGS)
229 : {
230 35531 : char arg1 = PG_GETARG_CHAR(0);
231 35531 : 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 35531 : if (IS_HIGHBIT_SET(arg1))
238 : {
239 3 : SET_VARSIZE(result, VARHDRSZ + 4);
240 3 : (VARDATA(result))[0] = '\\';
241 3 : (VARDATA(result))[1] = TOOCTAL(((unsigned char) arg1) >> 6);
242 3 : (VARDATA(result))[2] = TOOCTAL((((unsigned char) arg1) >> 3) & 07);
243 3 : (VARDATA(result))[3] = TOOCTAL(((unsigned char) arg1) & 07);
244 : }
245 35528 : else if (arg1 != '\0')
246 : {
247 35525 : SET_VARSIZE(result, VARHDRSZ + 1);
248 35525 : *(VARDATA(result)) = arg1;
249 : }
250 : else
251 3 : SET_VARSIZE(result, VARHDRSZ);
252 :
253 35531 : PG_RETURN_TEXT_P(result);
254 : }
|