summaryrefslogtreecommitdiffstats
path: root/src/backend/utils/adt/char.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils/adt/char.c')
-rw-r--r--src/backend/utils/adt/char.c253
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);
+}