TLA 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-2023, 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/builtins.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 GIC 4655427 : charin(PG_FUNCTION_ARGS)
42 ECB : {
43 GIC 4655427 : char *ch = PG_GETARG_CSTRING(0);
44 ECB :
45 GIC 4655427 : if (strlen(ch) == 4 && ch[0] == '\\' &&
46 CBC 12 : ISOCTAL(ch[1]) && ISOCTAL(ch[2]) && ISOCTAL(ch[3]))
47 12 : PG_RETURN_CHAR((FROMOCTAL(ch[1]) << 6) +
48 ECB : (FROMOCTAL(ch[2]) << 3) +
49 : FROMOCTAL(ch[3]));
50 : /* This will do the right thing for a zero-length input string */
51 GIC 4655415 : PG_RETURN_CHAR(ch[0]);
52 ECB : }
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 GIC 860899 : charout(PG_FUNCTION_ARGS)
65 ECB : {
66 GIC 860899 : char ch = PG_GETARG_CHAR(0);
67 CBC 860899 : char *result = (char *) palloc(5);
68 ECB :
69 GIC 860899 : if (IS_HIGHBIT_SET(ch))
70 ECB : {
71 GIC 6 : result[0] = '\\';
72 CBC 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 ECB : }
77 : else
78 : {
79 : /* This produces acceptable results for 0x00 as well */
80 GIC 860893 : result[0] = ch;
81 CBC 860893 : result[1] = '\0';
82 ECB : }
83 GIC 860899 : PG_RETURN_CSTRING(result);
84 ECB : }
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 UIC 0 : charrecv(PG_FUNCTION_ARGS)
95 EUB : {
96 UIC 0 : StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
97 EUB :
98 UIC 0 : PG_RETURN_CHAR(pq_getmsgbyte(buf));
99 EUB : }
100 :
101 : /*
102 : * charsend - converts char to binary format
103 : */
104 : Datum
105 UIC 0 : charsend(PG_FUNCTION_ARGS)
106 EUB : {
107 UIC 0 : char arg1 = PG_GETARG_CHAR(0);
108 EUB : StringInfoData buf;
109 :
110 UIC 0 : pq_begintypsend(&buf);
111 UBC 0 : pq_sendbyte(&buf, arg1);
112 0 : PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
113 EUB : }
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 GIC 2804213 : chareq(PG_FUNCTION_ARGS)
128 ECB : {
129 GIC 2804213 : char arg1 = PG_GETARG_CHAR(0);
130 CBC 2804213 : char arg2 = PG_GETARG_CHAR(1);
131 ECB :
132 GIC 2804213 : PG_RETURN_BOOL(arg1 == arg2);
133 ECB : }
134 :
135 : Datum
136 GIC 1477017 : charne(PG_FUNCTION_ARGS)
137 ECB : {
138 GIC 1477017 : char arg1 = PG_GETARG_CHAR(0);
139 CBC 1477017 : char arg2 = PG_GETARG_CHAR(1);
140 ECB :
141 GIC 1477017 : PG_RETURN_BOOL(arg1 != arg2);
142 ECB : }
143 :
144 : Datum
145 GIC 1548 : charlt(PG_FUNCTION_ARGS)
146 ECB : {
147 GIC 1548 : char arg1 = PG_GETARG_CHAR(0);
148 CBC 1548 : char arg2 = PG_GETARG_CHAR(1);
149 ECB :
150 GIC 1548 : PG_RETURN_BOOL((uint8) arg1 < (uint8) arg2);
151 ECB : }
152 :
153 : Datum
154 GIC 1212 : charle(PG_FUNCTION_ARGS)
155 ECB : {
156 GIC 1212 : char arg1 = PG_GETARG_CHAR(0);
157 CBC 1212 : char arg2 = PG_GETARG_CHAR(1);
158 ECB :
159 GIC 1212 : PG_RETURN_BOOL((uint8) arg1 <= (uint8) arg2);
160 ECB : }
161 :
162 : Datum
163 GIC 1521 : chargt(PG_FUNCTION_ARGS)
164 ECB : {
165 GIC 1521 : char arg1 = PG_GETARG_CHAR(0);
166 CBC 1521 : char arg2 = PG_GETARG_CHAR(1);
167 ECB :
168 GIC 1521 : PG_RETURN_BOOL((uint8) arg1 > (uint8) arg2);
169 ECB : }
170 :
171 : Datum
172 GIC 1065 : charge(PG_FUNCTION_ARGS)
173 ECB : {
174 GIC 1065 : char arg1 = PG_GETARG_CHAR(0);
175 CBC 1065 : char arg2 = PG_GETARG_CHAR(1);
176 ECB :
177 GIC 1065 : PG_RETURN_BOOL((uint8) arg1 >= (uint8) arg2);
178 ECB : }
179 :
180 :
181 : Datum
182 UIC 0 : chartoi4(PG_FUNCTION_ARGS)
183 EUB : {
184 UIC 0 : char arg1 = PG_GETARG_CHAR(0);
185 EUB :
186 UIC 0 : PG_RETURN_INT32((int32) ((int8) arg1));
187 EUB : }
188 :
189 : Datum
190 UIC 0 : i4tochar(PG_FUNCTION_ARGS)
191 EUB : {
192 UIC 0 : int32 arg1 = PG_GETARG_INT32(0);
193 EUB :
194 UIC 0 : if (arg1 < SCHAR_MIN || arg1 > SCHAR_MAX)
195 UBC 0 : ereport(ERROR,
196 EUB : (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
197 : errmsg("\"char\" out of range")));
198 :
199 UIC 0 : PG_RETURN_CHAR((int8) arg1);
200 EUB : }
201 :
202 :
203 : Datum
204 GIC 38849 : text_char(PG_FUNCTION_ARGS)
205 ECB : {
206 GIC 38849 : text *arg1 = PG_GETARG_TEXT_PP(0);
207 CBC 38849 : char *ch = VARDATA_ANY(arg1);
208 ECB : 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 GIC 38849 : if (VARSIZE_ANY_EXHDR(arg1) == 4 && ch[0] == '\\' &&
215 CBC 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 38846 : else if (VARSIZE_ANY_EXHDR(arg1) > 0)
220 38843 : result = ch[0];
221 ECB : else
222 GIC 3 : result = '\0';
223 ECB :
224 GIC 38849 : PG_RETURN_CHAR(result);
225 ECB : }
226 :
227 : Datum
228 GIC 15422 : char_text(PG_FUNCTION_ARGS)
229 ECB : {
230 GIC 15422 : char arg1 = PG_GETARG_CHAR(0);
231 CBC 15422 : text *result = palloc(VARHDRSZ + 4);
232 ECB :
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 GIC 15422 : if (IS_HIGHBIT_SET(arg1))
238 ECB : {
239 GIC 3 : SET_VARSIZE(result, VARHDRSZ + 4);
240 CBC 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 ECB : }
245 GIC 15419 : else if (arg1 != '\0')
246 ECB : {
247 GIC 15416 : SET_VARSIZE(result, VARHDRSZ + 1);
248 CBC 15416 : *(VARDATA(result)) = arg1;
249 ECB : }
250 : else
251 GIC 3 : SET_VARSIZE(result, VARHDRSZ);
252 ECB :
253 GIC 15422 : PG_RETURN_TEXT_P(result);
254 ECB : }
|