summaryrefslogtreecommitdiffstats
path: root/src/backend/utils/adt/oracle_compat.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils/adt/oracle_compat.c')
-rw-r--r--src/backend/utils/adt/oracle_compat.c1143
1 files changed, 1143 insertions, 0 deletions
diff --git a/src/backend/utils/adt/oracle_compat.c b/src/backend/utils/adt/oracle_compat.c
new file mode 100644
index 0000000..f737aa6
--- /dev/null
+++ b/src/backend/utils/adt/oracle_compat.c
@@ -0,0 +1,1143 @@
+/*-------------------------------------------------------------------------
+ * oracle_compat.c
+ * Oracle compatible functions.
+ *
+ * Copyright (c) 1996-2021, PostgreSQL Global Development Group
+ *
+ * Author: Edmund Mergl <E.Mergl@bawue.de>
+ * Multibyte enhancement: Tatsuo Ishii <ishii@postgresql.org>
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/utils/adt/oracle_compat.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "common/int.h"
+#include "mb/pg_wchar.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/formatting.h"
+
+static text *dotrim(const char *string, int stringlen,
+ const char *set, int setlen,
+ bool doltrim, bool dortrim);
+static bytea *dobyteatrim(bytea *string, bytea *set,
+ bool doltrim, bool dortrim);
+
+
+/********************************************************************
+ *
+ * lower
+ *
+ * Syntax:
+ *
+ * text lower(text string)
+ *
+ * Purpose:
+ *
+ * Returns string, with all letters forced to lowercase.
+ *
+ ********************************************************************/
+
+Datum
+lower(PG_FUNCTION_ARGS)
+{
+ text *in_string = PG_GETARG_TEXT_PP(0);
+ char *out_string;
+ text *result;
+
+ out_string = str_tolower(VARDATA_ANY(in_string),
+ VARSIZE_ANY_EXHDR(in_string),
+ PG_GET_COLLATION());
+ result = cstring_to_text(out_string);
+ pfree(out_string);
+
+ PG_RETURN_TEXT_P(result);
+}
+
+
+/********************************************************************
+ *
+ * upper
+ *
+ * Syntax:
+ *
+ * text upper(text string)
+ *
+ * Purpose:
+ *
+ * Returns string, with all letters forced to uppercase.
+ *
+ ********************************************************************/
+
+Datum
+upper(PG_FUNCTION_ARGS)
+{
+ text *in_string = PG_GETARG_TEXT_PP(0);
+ char *out_string;
+ text *result;
+
+ out_string = str_toupper(VARDATA_ANY(in_string),
+ VARSIZE_ANY_EXHDR(in_string),
+ PG_GET_COLLATION());
+ result = cstring_to_text(out_string);
+ pfree(out_string);
+
+ PG_RETURN_TEXT_P(result);
+}
+
+
+/********************************************************************
+ *
+ * initcap
+ *
+ * Syntax:
+ *
+ * text initcap(text string)
+ *
+ * Purpose:
+ *
+ * Returns string, with first letter of each word in uppercase, all
+ * other letters in lowercase. A word is defined as a sequence of
+ * alphanumeric characters, delimited by non-alphanumeric
+ * characters.
+ *
+ ********************************************************************/
+
+Datum
+initcap(PG_FUNCTION_ARGS)
+{
+ text *in_string = PG_GETARG_TEXT_PP(0);
+ char *out_string;
+ text *result;
+
+ out_string = str_initcap(VARDATA_ANY(in_string),
+ VARSIZE_ANY_EXHDR(in_string),
+ PG_GET_COLLATION());
+ result = cstring_to_text(out_string);
+ pfree(out_string);
+
+ PG_RETURN_TEXT_P(result);
+}
+
+
+/********************************************************************
+ *
+ * lpad
+ *
+ * Syntax:
+ *
+ * text lpad(text string1, int4 len, text string2)
+ *
+ * Purpose:
+ *
+ * Returns string1, left-padded to length len with the sequence of
+ * characters in string2. If len is less than the length of string1,
+ * instead truncate (on the right) to len.
+ *
+ ********************************************************************/
+
+Datum
+lpad(PG_FUNCTION_ARGS)
+{
+ text *string1 = PG_GETARG_TEXT_PP(0);
+ int32 len = PG_GETARG_INT32(1);
+ text *string2 = PG_GETARG_TEXT_PP(2);
+ text *ret;
+ char *ptr1,
+ *ptr2,
+ *ptr2start,
+ *ptr2end,
+ *ptr_ret;
+ int m,
+ s1len,
+ s2len;
+
+ int bytelen;
+
+ /* Negative len is silently taken as zero */
+ if (len < 0)
+ len = 0;
+
+ s1len = VARSIZE_ANY_EXHDR(string1);
+ if (s1len < 0)
+ s1len = 0; /* shouldn't happen */
+
+ s2len = VARSIZE_ANY_EXHDR(string2);
+ if (s2len < 0)
+ s2len = 0; /* shouldn't happen */
+
+ s1len = pg_mbstrlen_with_len(VARDATA_ANY(string1), s1len);
+
+ if (s1len > len)
+ s1len = len; /* truncate string1 to len chars */
+
+ if (s2len <= 0)
+ len = s1len; /* nothing to pad with, so don't pad */
+
+ bytelen = pg_database_encoding_max_length() * len;
+
+ /* check for integer overflow */
+ if (len != 0 && bytelen / pg_database_encoding_max_length() != len)
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("requested length too large")));
+
+ ret = (text *) palloc(VARHDRSZ + bytelen);
+
+ m = len - s1len;
+
+ ptr2 = ptr2start = VARDATA_ANY(string2);
+ ptr2end = ptr2 + s2len;
+ ptr_ret = VARDATA(ret);
+
+ while (m--)
+ {
+ int mlen = pg_mblen(ptr2);
+
+ memcpy(ptr_ret, ptr2, mlen);
+ ptr_ret += mlen;
+ ptr2 += mlen;
+ if (ptr2 == ptr2end) /* wrap around at end of s2 */
+ ptr2 = ptr2start;
+ }
+
+ ptr1 = VARDATA_ANY(string1);
+
+ while (s1len--)
+ {
+ int mlen = pg_mblen(ptr1);
+
+ memcpy(ptr_ret, ptr1, mlen);
+ ptr_ret += mlen;
+ ptr1 += mlen;
+ }
+
+ SET_VARSIZE(ret, ptr_ret - (char *) ret);
+
+ PG_RETURN_TEXT_P(ret);
+}
+
+
+/********************************************************************
+ *
+ * rpad
+ *
+ * Syntax:
+ *
+ * text rpad(text string1, int4 len, text string2)
+ *
+ * Purpose:
+ *
+ * Returns string1, right-padded to length len with the sequence of
+ * characters in string2. If len is less than the length of string1,
+ * instead truncate (on the right) to len.
+ *
+ ********************************************************************/
+
+Datum
+rpad(PG_FUNCTION_ARGS)
+{
+ text *string1 = PG_GETARG_TEXT_PP(0);
+ int32 len = PG_GETARG_INT32(1);
+ text *string2 = PG_GETARG_TEXT_PP(2);
+ text *ret;
+ char *ptr1,
+ *ptr2,
+ *ptr2start,
+ *ptr2end,
+ *ptr_ret;
+ int m,
+ s1len,
+ s2len;
+
+ int bytelen;
+
+ /* Negative len is silently taken as zero */
+ if (len < 0)
+ len = 0;
+
+ s1len = VARSIZE_ANY_EXHDR(string1);
+ if (s1len < 0)
+ s1len = 0; /* shouldn't happen */
+
+ s2len = VARSIZE_ANY_EXHDR(string2);
+ if (s2len < 0)
+ s2len = 0; /* shouldn't happen */
+
+ s1len = pg_mbstrlen_with_len(VARDATA_ANY(string1), s1len);
+
+ if (s1len > len)
+ s1len = len; /* truncate string1 to len chars */
+
+ if (s2len <= 0)
+ len = s1len; /* nothing to pad with, so don't pad */
+
+ bytelen = pg_database_encoding_max_length() * len;
+
+ /* Check for integer overflow */
+ if (len != 0 && bytelen / pg_database_encoding_max_length() != len)
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("requested length too large")));
+
+ ret = (text *) palloc(VARHDRSZ + bytelen);
+ m = len - s1len;
+
+ ptr1 = VARDATA_ANY(string1);
+ ptr_ret = VARDATA(ret);
+
+ while (s1len--)
+ {
+ int mlen = pg_mblen(ptr1);
+
+ memcpy(ptr_ret, ptr1, mlen);
+ ptr_ret += mlen;
+ ptr1 += mlen;
+ }
+
+ ptr2 = ptr2start = VARDATA_ANY(string2);
+ ptr2end = ptr2 + s2len;
+
+ while (m--)
+ {
+ int mlen = pg_mblen(ptr2);
+
+ memcpy(ptr_ret, ptr2, mlen);
+ ptr_ret += mlen;
+ ptr2 += mlen;
+ if (ptr2 == ptr2end) /* wrap around at end of s2 */
+ ptr2 = ptr2start;
+ }
+
+ SET_VARSIZE(ret, ptr_ret - (char *) ret);
+
+ PG_RETURN_TEXT_P(ret);
+}
+
+
+/********************************************************************
+ *
+ * btrim
+ *
+ * Syntax:
+ *
+ * text btrim(text string, text set)
+ *
+ * Purpose:
+ *
+ * Returns string with characters removed from the front and back
+ * up to the first character not in set.
+ *
+ ********************************************************************/
+
+Datum
+btrim(PG_FUNCTION_ARGS)
+{
+ text *string = PG_GETARG_TEXT_PP(0);
+ text *set = PG_GETARG_TEXT_PP(1);
+ text *ret;
+
+ ret = dotrim(VARDATA_ANY(string), VARSIZE_ANY_EXHDR(string),
+ VARDATA_ANY(set), VARSIZE_ANY_EXHDR(set),
+ true, true);
+
+ PG_RETURN_TEXT_P(ret);
+}
+
+/********************************************************************
+ *
+ * btrim1 --- btrim with set fixed as ' '
+ *
+ ********************************************************************/
+
+Datum
+btrim1(PG_FUNCTION_ARGS)
+{
+ text *string = PG_GETARG_TEXT_PP(0);
+ text *ret;
+
+ ret = dotrim(VARDATA_ANY(string), VARSIZE_ANY_EXHDR(string),
+ " ", 1,
+ true, true);
+
+ PG_RETURN_TEXT_P(ret);
+}
+
+/*
+ * Common implementation for btrim, ltrim, rtrim
+ */
+static text *
+dotrim(const char *string, int stringlen,
+ const char *set, int setlen,
+ bool doltrim, bool dortrim)
+{
+ int i;
+
+ /* Nothing to do if either string or set is empty */
+ if (stringlen > 0 && setlen > 0)
+ {
+ if (pg_database_encoding_max_length() > 1)
+ {
+ /*
+ * In the multibyte-encoding case, build arrays of pointers to
+ * character starts, so that we can avoid inefficient checks in
+ * the inner loops.
+ */
+ const char **stringchars;
+ const char **setchars;
+ int *stringmblen;
+ int *setmblen;
+ int stringnchars;
+ int setnchars;
+ int resultndx;
+ int resultnchars;
+ const char *p;
+ int len;
+ int mblen;
+ const char *str_pos;
+ int str_len;
+
+ stringchars = (const char **) palloc(stringlen * sizeof(char *));
+ stringmblen = (int *) palloc(stringlen * sizeof(int));
+ stringnchars = 0;
+ p = string;
+ len = stringlen;
+ while (len > 0)
+ {
+ stringchars[stringnchars] = p;
+ stringmblen[stringnchars] = mblen = pg_mblen(p);
+ stringnchars++;
+ p += mblen;
+ len -= mblen;
+ }
+
+ setchars = (const char **) palloc(setlen * sizeof(char *));
+ setmblen = (int *) palloc(setlen * sizeof(int));
+ setnchars = 0;
+ p = set;
+ len = setlen;
+ while (len > 0)
+ {
+ setchars[setnchars] = p;
+ setmblen[setnchars] = mblen = pg_mblen(p);
+ setnchars++;
+ p += mblen;
+ len -= mblen;
+ }
+
+ resultndx = 0; /* index in stringchars[] */
+ resultnchars = stringnchars;
+
+ if (doltrim)
+ {
+ while (resultnchars > 0)
+ {
+ str_pos = stringchars[resultndx];
+ str_len = stringmblen[resultndx];
+ for (i = 0; i < setnchars; i++)
+ {
+ if (str_len == setmblen[i] &&
+ memcmp(str_pos, setchars[i], str_len) == 0)
+ break;
+ }
+ if (i >= setnchars)
+ break; /* no match here */
+ string += str_len;
+ stringlen -= str_len;
+ resultndx++;
+ resultnchars--;
+ }
+ }
+
+ if (dortrim)
+ {
+ while (resultnchars > 0)
+ {
+ str_pos = stringchars[resultndx + resultnchars - 1];
+ str_len = stringmblen[resultndx + resultnchars - 1];
+ for (i = 0; i < setnchars; i++)
+ {
+ if (str_len == setmblen[i] &&
+ memcmp(str_pos, setchars[i], str_len) == 0)
+ break;
+ }
+ if (i >= setnchars)
+ break; /* no match here */
+ stringlen -= str_len;
+ resultnchars--;
+ }
+ }
+
+ pfree(stringchars);
+ pfree(stringmblen);
+ pfree(setchars);
+ pfree(setmblen);
+ }
+ else
+ {
+ /*
+ * In the single-byte-encoding case, we don't need such overhead.
+ */
+ if (doltrim)
+ {
+ while (stringlen > 0)
+ {
+ char str_ch = *string;
+
+ for (i = 0; i < setlen; i++)
+ {
+ if (str_ch == set[i])
+ break;
+ }
+ if (i >= setlen)
+ break; /* no match here */
+ string++;
+ stringlen--;
+ }
+ }
+
+ if (dortrim)
+ {
+ while (stringlen > 0)
+ {
+ char str_ch = string[stringlen - 1];
+
+ for (i = 0; i < setlen; i++)
+ {
+ if (str_ch == set[i])
+ break;
+ }
+ if (i >= setlen)
+ break; /* no match here */
+ stringlen--;
+ }
+ }
+ }
+ }
+
+ /* Return selected portion of string */
+ return cstring_to_text_with_len(string, stringlen);
+}
+
+/*
+ * Common implementation for bytea versions of btrim, ltrim, rtrim
+ */
+bytea *
+dobyteatrim(bytea *string, bytea *set, bool doltrim, bool dortrim)
+{
+ bytea *ret;
+ char *ptr,
+ *end,
+ *ptr2,
+ *ptr2start,
+ *end2;
+ int m,
+ stringlen,
+ setlen;
+
+ stringlen = VARSIZE_ANY_EXHDR(string);
+ setlen = VARSIZE_ANY_EXHDR(set);
+
+ if (stringlen <= 0 || setlen <= 0)
+ return string;
+
+ m = stringlen;
+ ptr = VARDATA_ANY(string);
+ end = ptr + stringlen - 1;
+ ptr2start = VARDATA_ANY(set);
+ end2 = ptr2start + setlen - 1;
+
+ if (doltrim)
+ {
+ while (m > 0)
+ {
+ ptr2 = ptr2start;
+ while (ptr2 <= end2)
+ {
+ if (*ptr == *ptr2)
+ break;
+ ++ptr2;
+ }
+ if (ptr2 > end2)
+ break;
+ ptr++;
+ m--;
+ }
+ }
+
+ if (dortrim)
+ {
+ while (m > 0)
+ {
+ ptr2 = ptr2start;
+ while (ptr2 <= end2)
+ {
+ if (*end == *ptr2)
+ break;
+ ++ptr2;
+ }
+ if (ptr2 > end2)
+ break;
+ end--;
+ m--;
+ }
+ }
+
+ ret = (bytea *) palloc(VARHDRSZ + m);
+ SET_VARSIZE(ret, VARHDRSZ + m);
+ memcpy(VARDATA(ret), ptr, m);
+ return ret;
+}
+
+/********************************************************************
+ *
+ * byteatrim
+ *
+ * Syntax:
+ *
+ * bytea byteatrim(bytea string, bytea set)
+ *
+ * Purpose:
+ *
+ * Returns string with characters removed from the front and back
+ * up to the first character not in set.
+ *
+ * Cloned from btrim and modified as required.
+ ********************************************************************/
+
+Datum
+byteatrim(PG_FUNCTION_ARGS)
+{
+ bytea *string = PG_GETARG_BYTEA_PP(0);
+ bytea *set = PG_GETARG_BYTEA_PP(1);
+ bytea *ret;
+
+ ret = dobyteatrim(string, set, true, true);
+
+ PG_RETURN_BYTEA_P(ret);
+}
+
+/********************************************************************
+ *
+ * bytealtrim
+ *
+ * Syntax:
+ *
+ * bytea bytealtrim(bytea string, bytea set)
+ *
+ * Purpose:
+ *
+ * Returns string with initial characters removed up to the first
+ * character not in set.
+ *
+ ********************************************************************/
+
+Datum
+bytealtrim(PG_FUNCTION_ARGS)
+{
+ bytea *string = PG_GETARG_BYTEA_PP(0);
+ bytea *set = PG_GETARG_BYTEA_PP(1);
+ bytea *ret;
+
+ ret = dobyteatrim(string, set, true, false);
+
+ PG_RETURN_BYTEA_P(ret);
+}
+
+/********************************************************************
+ *
+ * byteartrim
+ *
+ * Syntax:
+ *
+ * bytea byteartrim(bytea string, bytea set)
+ *
+ * Purpose:
+ *
+ * Returns string with final characters removed after the last
+ * character not in set.
+ *
+ ********************************************************************/
+
+Datum
+byteartrim(PG_FUNCTION_ARGS)
+{
+ bytea *string = PG_GETARG_BYTEA_PP(0);
+ bytea *set = PG_GETARG_BYTEA_PP(1);
+ bytea *ret;
+
+ ret = dobyteatrim(string, set, false, true);
+
+ PG_RETURN_BYTEA_P(ret);
+}
+
+/********************************************************************
+ *
+ * ltrim
+ *
+ * Syntax:
+ *
+ * text ltrim(text string, text set)
+ *
+ * Purpose:
+ *
+ * Returns string with initial characters removed up to the first
+ * character not in set.
+ *
+ ********************************************************************/
+
+Datum
+ltrim(PG_FUNCTION_ARGS)
+{
+ text *string = PG_GETARG_TEXT_PP(0);
+ text *set = PG_GETARG_TEXT_PP(1);
+ text *ret;
+
+ ret = dotrim(VARDATA_ANY(string), VARSIZE_ANY_EXHDR(string),
+ VARDATA_ANY(set), VARSIZE_ANY_EXHDR(set),
+ true, false);
+
+ PG_RETURN_TEXT_P(ret);
+}
+
+/********************************************************************
+ *
+ * ltrim1 --- ltrim with set fixed as ' '
+ *
+ ********************************************************************/
+
+Datum
+ltrim1(PG_FUNCTION_ARGS)
+{
+ text *string = PG_GETARG_TEXT_PP(0);
+ text *ret;
+
+ ret = dotrim(VARDATA_ANY(string), VARSIZE_ANY_EXHDR(string),
+ " ", 1,
+ true, false);
+
+ PG_RETURN_TEXT_P(ret);
+}
+
+/********************************************************************
+ *
+ * rtrim
+ *
+ * Syntax:
+ *
+ * text rtrim(text string, text set)
+ *
+ * Purpose:
+ *
+ * Returns string with final characters removed after the last
+ * character not in set.
+ *
+ ********************************************************************/
+
+Datum
+rtrim(PG_FUNCTION_ARGS)
+{
+ text *string = PG_GETARG_TEXT_PP(0);
+ text *set = PG_GETARG_TEXT_PP(1);
+ text *ret;
+
+ ret = dotrim(VARDATA_ANY(string), VARSIZE_ANY_EXHDR(string),
+ VARDATA_ANY(set), VARSIZE_ANY_EXHDR(set),
+ false, true);
+
+ PG_RETURN_TEXT_P(ret);
+}
+
+/********************************************************************
+ *
+ * rtrim1 --- rtrim with set fixed as ' '
+ *
+ ********************************************************************/
+
+Datum
+rtrim1(PG_FUNCTION_ARGS)
+{
+ text *string = PG_GETARG_TEXT_PP(0);
+ text *ret;
+
+ ret = dotrim(VARDATA_ANY(string), VARSIZE_ANY_EXHDR(string),
+ " ", 1,
+ false, true);
+
+ PG_RETURN_TEXT_P(ret);
+}
+
+
+/********************************************************************
+ *
+ * translate
+ *
+ * Syntax:
+ *
+ * text translate(text string, text from, text to)
+ *
+ * Purpose:
+ *
+ * Returns string after replacing all occurrences of characters in from
+ * with the corresponding character in to. If from is longer than to,
+ * occurrences of the extra characters in from are deleted.
+ * Improved by Edwin Ramirez <ramirez@doc.mssm.edu>.
+ *
+ ********************************************************************/
+
+Datum
+translate(PG_FUNCTION_ARGS)
+{
+ text *string = PG_GETARG_TEXT_PP(0);
+ text *from = PG_GETARG_TEXT_PP(1);
+ text *to = PG_GETARG_TEXT_PP(2);
+ text *result;
+ char *from_ptr,
+ *to_ptr;
+ char *source,
+ *target;
+ int m,
+ fromlen,
+ tolen,
+ retlen,
+ i;
+ int worst_len;
+ int len;
+ int source_len;
+ int from_index;
+
+ m = VARSIZE_ANY_EXHDR(string);
+ if (m <= 0)
+ PG_RETURN_TEXT_P(string);
+ source = VARDATA_ANY(string);
+
+ fromlen = VARSIZE_ANY_EXHDR(from);
+ from_ptr = VARDATA_ANY(from);
+ tolen = VARSIZE_ANY_EXHDR(to);
+ to_ptr = VARDATA_ANY(to);
+
+ /*
+ * The worst-case expansion is to substitute a max-length character for a
+ * single-byte character at each position of the string.
+ */
+ worst_len = pg_database_encoding_max_length() * m;
+
+ /* check for integer overflow */
+ if (worst_len / pg_database_encoding_max_length() != m)
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("requested length too large")));
+
+ result = (text *) palloc(worst_len + VARHDRSZ);
+ target = VARDATA(result);
+ retlen = 0;
+
+ while (m > 0)
+ {
+ source_len = pg_mblen(source);
+ from_index = 0;
+
+ for (i = 0; i < fromlen; i += len)
+ {
+ len = pg_mblen(&from_ptr[i]);
+ if (len == source_len &&
+ memcmp(source, &from_ptr[i], len) == 0)
+ break;
+
+ from_index++;
+ }
+ if (i < fromlen)
+ {
+ /* substitute */
+ char *p = to_ptr;
+
+ for (i = 0; i < from_index; i++)
+ {
+ p += pg_mblen(p);
+ if (p >= (to_ptr + tolen))
+ break;
+ }
+ if (p < (to_ptr + tolen))
+ {
+ len = pg_mblen(p);
+ memcpy(target, p, len);
+ target += len;
+ retlen += len;
+ }
+
+ }
+ else
+ {
+ /* no match, so copy */
+ memcpy(target, source, source_len);
+ target += source_len;
+ retlen += source_len;
+ }
+
+ source += source_len;
+ m -= source_len;
+ }
+
+ SET_VARSIZE(result, retlen + VARHDRSZ);
+
+ /*
+ * The function result is probably much bigger than needed, if we're using
+ * a multibyte encoding, but it's not worth reallocating it; the result
+ * probably won't live long anyway.
+ */
+
+ PG_RETURN_TEXT_P(result);
+}
+
+/********************************************************************
+ *
+ * ascii
+ *
+ * Syntax:
+ *
+ * int ascii(text string)
+ *
+ * Purpose:
+ *
+ * Returns the decimal representation of the first character from
+ * string.
+ * If the string is empty we return 0.
+ * If the database encoding is UTF8, we return the Unicode codepoint.
+ * If the database encoding is any other multi-byte encoding, we
+ * return the value of the first byte if it is an ASCII character
+ * (range 1 .. 127), or raise an error.
+ * For all other encodings we return the value of the first byte,
+ * (range 1..255).
+ *
+ ********************************************************************/
+
+Datum
+ascii(PG_FUNCTION_ARGS)
+{
+ text *string = PG_GETARG_TEXT_PP(0);
+ int encoding = GetDatabaseEncoding();
+ unsigned char *data;
+
+ if (VARSIZE_ANY_EXHDR(string) <= 0)
+ PG_RETURN_INT32(0);
+
+ data = (unsigned char *) VARDATA_ANY(string);
+
+ if (encoding == PG_UTF8 && *data > 127)
+ {
+ /* return the code point for Unicode */
+
+ int result = 0,
+ tbytes = 0,
+ i;
+
+ if (*data >= 0xF0)
+ {
+ result = *data & 0x07;
+ tbytes = 3;
+ }
+ else if (*data >= 0xE0)
+ {
+ result = *data & 0x0F;
+ tbytes = 2;
+ }
+ else
+ {
+ Assert(*data > 0xC0);
+ result = *data & 0x1f;
+ tbytes = 1;
+ }
+
+ Assert(tbytes > 0);
+
+ for (i = 1; i <= tbytes; i++)
+ {
+ Assert((data[i] & 0xC0) == 0x80);
+ result = (result << 6) + (data[i] & 0x3f);
+ }
+
+ PG_RETURN_INT32(result);
+ }
+ else
+ {
+ if (pg_encoding_max_length(encoding) > 1 && *data > 127)
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("requested character too large")));
+
+
+ PG_RETURN_INT32((int32) *data);
+ }
+}
+
+/********************************************************************
+ *
+ * chr
+ *
+ * Syntax:
+ *
+ * text chr(int val)
+ *
+ * Purpose:
+ *
+ * Returns the character having the binary equivalent to val.
+ *
+ * For UTF8 we treat the argument as a Unicode code point.
+ * For other multi-byte encodings we raise an error for arguments
+ * outside the strict ASCII range (1..127).
+ *
+ * It's important that we don't ever return a value that is not valid
+ * in the database encoding, so that this doesn't become a way for
+ * invalid data to enter the database.
+ *
+ ********************************************************************/
+
+Datum
+chr (PG_FUNCTION_ARGS)
+{
+ uint32 cvalue = PG_GETARG_UINT32(0);
+ text *result;
+ int encoding = GetDatabaseEncoding();
+
+ if (encoding == PG_UTF8 && cvalue > 127)
+ {
+ /* for Unicode we treat the argument as a code point */
+ int bytes;
+ unsigned char *wch;
+
+ /*
+ * We only allow valid Unicode code points; per RFC3629 that stops at
+ * U+10FFFF, even though 4-byte UTF8 sequences can hold values up to
+ * U+1FFFFF.
+ */
+ if (cvalue > 0x0010ffff)
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("requested character too large for encoding: %d",
+ cvalue)));
+
+ if (cvalue > 0xffff)
+ bytes = 4;
+ else if (cvalue > 0x07ff)
+ bytes = 3;
+ else
+ bytes = 2;
+
+ result = (text *) palloc(VARHDRSZ + bytes);
+ SET_VARSIZE(result, VARHDRSZ + bytes);
+ wch = (unsigned char *) VARDATA(result);
+
+ if (bytes == 2)
+ {
+ wch[0] = 0xC0 | ((cvalue >> 6) & 0x1F);
+ wch[1] = 0x80 | (cvalue & 0x3F);
+ }
+ else if (bytes == 3)
+ {
+ wch[0] = 0xE0 | ((cvalue >> 12) & 0x0F);
+ wch[1] = 0x80 | ((cvalue >> 6) & 0x3F);
+ wch[2] = 0x80 | (cvalue & 0x3F);
+ }
+ else
+ {
+ wch[0] = 0xF0 | ((cvalue >> 18) & 0x07);
+ wch[1] = 0x80 | ((cvalue >> 12) & 0x3F);
+ wch[2] = 0x80 | ((cvalue >> 6) & 0x3F);
+ wch[3] = 0x80 | (cvalue & 0x3F);
+ }
+
+ /*
+ * The preceding range check isn't sufficient, because UTF8 excludes
+ * Unicode "surrogate pair" codes. Make sure what we created is valid
+ * UTF8.
+ */
+ if (!pg_utf8_islegal(wch, bytes))
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("requested character not valid for encoding: %d",
+ cvalue)));
+ }
+ else
+ {
+ bool is_mb;
+
+ /*
+ * Error out on arguments that make no sense or that we can't validly
+ * represent in the encoding.
+ */
+ if (cvalue == 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("null character not permitted")));
+
+ is_mb = pg_encoding_max_length(encoding) > 1;
+
+ if ((is_mb && (cvalue > 127)) || (!is_mb && (cvalue > 255)))
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("requested character too large for encoding: %d",
+ cvalue)));
+
+ result = (text *) palloc(VARHDRSZ + 1);
+ SET_VARSIZE(result, VARHDRSZ + 1);
+ *VARDATA(result) = (char) cvalue;
+ }
+
+ PG_RETURN_TEXT_P(result);
+}
+
+/********************************************************************
+ *
+ * repeat
+ *
+ * Syntax:
+ *
+ * text repeat(text string, int val)
+ *
+ * Purpose:
+ *
+ * Repeat string by val.
+ *
+ ********************************************************************/
+
+Datum
+repeat(PG_FUNCTION_ARGS)
+{
+ text *string = PG_GETARG_TEXT_PP(0);
+ int32 count = PG_GETARG_INT32(1);
+ text *result;
+ int slen,
+ tlen;
+ int i;
+ char *cp,
+ *sp;
+
+ if (count < 0)
+ count = 0;
+
+ slen = VARSIZE_ANY_EXHDR(string);
+
+ if (unlikely(pg_mul_s32_overflow(count, slen, &tlen)) ||
+ unlikely(pg_add_s32_overflow(tlen, VARHDRSZ, &tlen)))
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("requested length too large")));
+
+ result = (text *) palloc(tlen);
+
+ SET_VARSIZE(result, tlen);
+ cp = VARDATA(result);
+ sp = VARDATA_ANY(string);
+ for (i = 0; i < count; i++)
+ {
+ memcpy(cp, sp, slen);
+ cp += slen;
+ CHECK_FOR_INTERRUPTS();
+ }
+
+ PG_RETURN_TEXT_P(result);
+}