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-2024, 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 1470832 : charin(PG_FUNCTION_ARGS) 42 : { 43 1470832 : char *ch = PG_GETARG_CSTRING(0); 44 : 45 1470832 : if (strlen(ch) == 4 && ch[0] == '\\' && 46 24 : ISOCTAL(ch[1]) && ISOCTAL(ch[2]) && ISOCTAL(ch[3])) 47 24 : 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 1470808 : 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 2814512 : charout(PG_FUNCTION_ARGS) 65 : { 66 2814512 : char ch = PG_GETARG_CHAR(0); 67 2814512 : char *result = (char *) palloc(5); 68 : 69 2814512 : if (IS_HIGHBIT_SET(ch)) 70 : { 71 12 : result[0] = '\\'; 72 12 : result[1] = TOOCTAL(((unsigned char) ch) >> 6); 73 12 : result[2] = TOOCTAL((((unsigned char) ch) >> 3) & 07); 74 12 : result[3] = TOOCTAL(((unsigned char) ch) & 07); 75 12 : result[4] = '\0'; 76 : } 77 : else 78 : { 79 : /* This produces acceptable results for 0x00 as well */ 80 2814500 : result[0] = ch; 81 2814500 : result[1] = '\0'; 82 : } 83 2814512 : 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 6202770 : chareq(PG_FUNCTION_ARGS) 128 : { 129 6202770 : char arg1 = PG_GETARG_CHAR(0); 130 6202770 : char arg2 = PG_GETARG_CHAR(1); 131 : 132 6202770 : PG_RETURN_BOOL(arg1 == arg2); 133 : } 134 : 135 : Datum 136 3602070 : charne(PG_FUNCTION_ARGS) 137 : { 138 3602070 : char arg1 = PG_GETARG_CHAR(0); 139 3602070 : char arg2 = PG_GETARG_CHAR(1); 140 : 141 3602070 : PG_RETURN_BOOL(arg1 != arg2); 142 : } 143 : 144 : Datum 145 3096 : charlt(PG_FUNCTION_ARGS) 146 : { 147 3096 : char arg1 = PG_GETARG_CHAR(0); 148 3096 : char arg2 = PG_GETARG_CHAR(1); 149 : 150 3096 : PG_RETURN_BOOL((uint8) arg1 < (uint8) arg2); 151 : } 152 : 153 : Datum 154 2424 : charle(PG_FUNCTION_ARGS) 155 : { 156 2424 : char arg1 = PG_GETARG_CHAR(0); 157 2424 : char arg2 = PG_GETARG_CHAR(1); 158 : 159 2424 : PG_RETURN_BOOL((uint8) arg1 <= (uint8) arg2); 160 : } 161 : 162 : Datum 163 3042 : chargt(PG_FUNCTION_ARGS) 164 : { 165 3042 : char arg1 = PG_GETARG_CHAR(0); 166 3042 : char arg2 = PG_GETARG_CHAR(1); 167 : 168 3042 : PG_RETURN_BOOL((uint8) arg1 > (uint8) arg2); 169 : } 170 : 171 : Datum 172 2130 : charge(PG_FUNCTION_ARGS) 173 : { 174 2130 : char arg1 = PG_GETARG_CHAR(0); 175 2130 : char arg2 = PG_GETARG_CHAR(1); 176 : 177 2130 : 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 12506 : text_char(PG_FUNCTION_ARGS) 205 : { 206 12506 : text *arg1 = PG_GETARG_TEXT_PP(0); 207 12506 : 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 12506 : if (VARSIZE_ANY_EXHDR(arg1) == 4 && ch[0] == '\\' && 215 6 : ISOCTAL(ch[1]) && ISOCTAL(ch[2]) && ISOCTAL(ch[3])) 216 6 : result = (FROMOCTAL(ch[1]) << 6) + 217 6 : (FROMOCTAL(ch[2]) << 3) + 218 6 : FROMOCTAL(ch[3]); 219 12500 : else if (VARSIZE_ANY_EXHDR(arg1) > 0) 220 12494 : result = ch[0]; 221 : else 222 6 : result = '\0'; 223 : 224 12506 : PG_RETURN_CHAR(result); 225 : } 226 : 227 : Datum 228 46086 : char_text(PG_FUNCTION_ARGS) 229 : { 230 46086 : char arg1 = PG_GETARG_CHAR(0); 231 46086 : 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 46086 : if (IS_HIGHBIT_SET(arg1)) 238 : { 239 6 : SET_VARSIZE(result, VARHDRSZ + 4); 240 6 : (VARDATA(result))[0] = '\\'; 241 6 : (VARDATA(result))[1] = TOOCTAL(((unsigned char) arg1) >> 6); 242 6 : (VARDATA(result))[2] = TOOCTAL((((unsigned char) arg1) >> 3) & 07); 243 6 : (VARDATA(result))[3] = TOOCTAL(((unsigned char) arg1) & 07); 244 : } 245 46080 : else if (arg1 != '\0') 246 : { 247 46074 : SET_VARSIZE(result, VARHDRSZ + 1); 248 46074 : *(VARDATA(result)) = arg1; 249 : } 250 : else 251 6 : SET_VARSIZE(result, VARHDRSZ); 252 : 253 46086 : PG_RETURN_TEXT_P(result); 254 : }