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