diff options
Diffstat (limited to 'src/backend/utils/adt/char.c')
-rw-r--r-- | src/backend/utils/adt/char.c | 253 |
1 files changed, 253 insertions, 0 deletions
diff --git a/src/backend/utils/adt/char.c b/src/backend/utils/adt/char.c new file mode 100644 index 0000000..e50293b --- /dev/null +++ b/src/backend/utils/adt/char.c @@ -0,0 +1,253 @@ +/*------------------------------------------------------------------------- + * + * char.c + * Functions for the built-in type "char" (not to be confused with + * bpchar, which is the SQL CHAR(n) type). + * + * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/utils/adt/char.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include <limits.h> + +#include "libpq/pqformat.h" +#include "utils/builtins.h" + +#define ISOCTAL(c) (((c) >= '0') && ((c) <= '7')) +#define TOOCTAL(c) ((c) + '0') +#define FROMOCTAL(c) ((unsigned char) (c) - '0') + + +/***************************************************************************** + * USER I/O ROUTINES * + *****************************************************************************/ + +/* + * charin - converts "x" to 'x' + * + * This accepts the formats charout produces. If we have multibyte input + * that is not in the form '\ooo', then we take its first byte as the value + * and silently discard the rest; this is a backwards-compatibility provision. + */ +Datum +charin(PG_FUNCTION_ARGS) +{ + char *ch = PG_GETARG_CSTRING(0); + + if (strlen(ch) == 4 && ch[0] == '\\' && + ISOCTAL(ch[1]) && ISOCTAL(ch[2]) && ISOCTAL(ch[3])) + PG_RETURN_CHAR((FROMOCTAL(ch[1]) << 6) + + (FROMOCTAL(ch[2]) << 3) + + FROMOCTAL(ch[3])); + /* This will do the right thing for a zero-length input string */ + PG_RETURN_CHAR(ch[0]); +} + +/* + * charout - converts 'x' to "x" + * + * The possible output formats are: + * 1. 0x00 is represented as an empty string. + * 2. 0x01..0x7F are represented as a single ASCII byte. + * 3. 0x80..0xFF are represented as \ooo (backslash and 3 octal digits). + * Case 3 is meant to match the traditional "escape" format of bytea. + */ +Datum +charout(PG_FUNCTION_ARGS) +{ + char ch = PG_GETARG_CHAR(0); + char *result = (char *) palloc(5); + + if (IS_HIGHBIT_SET(ch)) + { + result[0] = '\\'; + result[1] = TOOCTAL(((unsigned char) ch) >> 6); + result[2] = TOOCTAL((((unsigned char) ch) >> 3) & 07); + result[3] = TOOCTAL(((unsigned char) ch) & 07); + result[4] = '\0'; + } + else + { + /* This produces acceptable results for 0x00 as well */ + result[0] = ch; + result[1] = '\0'; + } + PG_RETURN_CSTRING(result); +} + +/* + * charrecv - converts external binary format to char + * + * The external representation is one byte, with no character set + * conversion. This is somewhat dubious, perhaps, but in many + * cases people use char for a 1-byte binary type. + */ +Datum +charrecv(PG_FUNCTION_ARGS) +{ + StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); + + PG_RETURN_CHAR(pq_getmsgbyte(buf)); +} + +/* + * charsend - converts char to binary format + */ +Datum +charsend(PG_FUNCTION_ARGS) +{ + char arg1 = PG_GETARG_CHAR(0); + StringInfoData buf; + + pq_begintypsend(&buf); + pq_sendbyte(&buf, arg1); + PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); +} + +/***************************************************************************** + * PUBLIC ROUTINES * + *****************************************************************************/ + +/* + * NOTE: comparisons are done as though char is unsigned (uint8). + * Conversions to and from integer are done as though char is signed (int8). + * + * You wanted consistency? + */ + +Datum +chareq(PG_FUNCTION_ARGS) +{ + char arg1 = PG_GETARG_CHAR(0); + char arg2 = PG_GETARG_CHAR(1); + + PG_RETURN_BOOL(arg1 == arg2); +} + +Datum +charne(PG_FUNCTION_ARGS) +{ + char arg1 = PG_GETARG_CHAR(0); + char arg2 = PG_GETARG_CHAR(1); + + PG_RETURN_BOOL(arg1 != arg2); +} + +Datum +charlt(PG_FUNCTION_ARGS) +{ + char arg1 = PG_GETARG_CHAR(0); + char arg2 = PG_GETARG_CHAR(1); + + PG_RETURN_BOOL((uint8) arg1 < (uint8) arg2); +} + +Datum +charle(PG_FUNCTION_ARGS) +{ + char arg1 = PG_GETARG_CHAR(0); + char arg2 = PG_GETARG_CHAR(1); + + PG_RETURN_BOOL((uint8) arg1 <= (uint8) arg2); +} + +Datum +chargt(PG_FUNCTION_ARGS) +{ + char arg1 = PG_GETARG_CHAR(0); + char arg2 = PG_GETARG_CHAR(1); + + PG_RETURN_BOOL((uint8) arg1 > (uint8) arg2); +} + +Datum +charge(PG_FUNCTION_ARGS) +{ + char arg1 = PG_GETARG_CHAR(0); + char arg2 = PG_GETARG_CHAR(1); + + PG_RETURN_BOOL((uint8) arg1 >= (uint8) arg2); +} + + +Datum +chartoi4(PG_FUNCTION_ARGS) +{ + char arg1 = PG_GETARG_CHAR(0); + + PG_RETURN_INT32((int32) ((int8) arg1)); +} + +Datum +i4tochar(PG_FUNCTION_ARGS) +{ + int32 arg1 = PG_GETARG_INT32(0); + + if (arg1 < SCHAR_MIN || arg1 > SCHAR_MAX) + ereport(ERROR, + (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), + errmsg("\"char\" out of range"))); + + PG_RETURN_CHAR((int8) arg1); +} + + +Datum +text_char(PG_FUNCTION_ARGS) +{ + text *arg1 = PG_GETARG_TEXT_PP(0); + char *ch = VARDATA_ANY(arg1); + char result; + + /* + * Conversion rules are the same as in charin(), but here we need to + * handle the empty-string case honestly. + */ + if (VARSIZE_ANY_EXHDR(arg1) == 4 && ch[0] == '\\' && + ISOCTAL(ch[1]) && ISOCTAL(ch[2]) && ISOCTAL(ch[3])) + result = (FROMOCTAL(ch[1]) << 6) + + (FROMOCTAL(ch[2]) << 3) + + FROMOCTAL(ch[3]); + else if (VARSIZE_ANY_EXHDR(arg1) > 0) + result = ch[0]; + else + result = '\0'; + + PG_RETURN_CHAR(result); +} + +Datum +char_text(PG_FUNCTION_ARGS) +{ + char arg1 = PG_GETARG_CHAR(0); + text *result = palloc(VARHDRSZ + 4); + + /* + * Conversion rules are the same as in charout(), but here we need to be + * honest about converting 0x00 to an empty string. + */ + if (IS_HIGHBIT_SET(arg1)) + { + SET_VARSIZE(result, VARHDRSZ + 4); + (VARDATA(result))[0] = '\\'; + (VARDATA(result))[1] = TOOCTAL(((unsigned char) arg1) >> 6); + (VARDATA(result))[2] = TOOCTAL((((unsigned char) arg1) >> 3) & 07); + (VARDATA(result))[3] = TOOCTAL(((unsigned char) arg1) & 07); + } + else if (arg1 != '\0') + { + SET_VARSIZE(result, VARHDRSZ + 1); + *(VARDATA(result)) = arg1; + } + else + SET_VARSIZE(result, VARHDRSZ); + + PG_RETURN_TEXT_P(result); +} |