</para></entry>
</row>
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <indexterm>
+ <primary>unistr</primary>
+ </indexterm>
+ <function>unistr</function> ( <type>text</type> )
+ <returnvalue>text</returnvalue>
+ </para>
+ <para>
+ Evaluate escaped Unicode characters in argument. Unicode characters
+ can be specified as
+ <literal>\<replaceable>XXXX</replaceable></literal> (4 hexadecimal
+ digits), <literal>\+<replaceable>XXXXXX</replaceable></literal> (6
+ hexadecimal digits),
+ <literal>\u<replaceable>XXXX</replaceable></literal> (4 hexadecimal
+ digits), or <literal>\U<replaceable>XXXXXXXX</replaceable></literal>
+ (8 hexadecimal digits). To specify a backslash, write two
+ backslashes. All other characters are taken literally.
+ </para>
+
+ <para>
+ If the server encoding is not UTF-8, the Unicode code point identified
+ by one of these escape sequences is converted to the actual server
+ encoding; an error is reported if that's not possible.
+ </para>
+
+ <para>
+ This function provides a (non-standard) alternative to string
+ constants with Unicode escapes (see <xref
+ linkend="sql-syntax-strings-uescape"/>).
+ </para>
+
+ <para>
+ <literal>unistr('\0441\043B\043E\043D')</literal>
+ <returnvalue>слон</returnvalue>
+ </para>
+ <para>
+ <literal>unistr('d\0061t\+000061')</literal>
+ <returnvalue>data</returnvalue>
+ </para>
+ <para>
+ <literal>unistr('d\u0061t\U00000061')</literal>
+ <returnvalue>data</returnvalue>
+ </para></entry>
+ </row>
+
</tbody>
</tgroup>
</table>
PG_RETURN_BOOL(result);
}
+
+/*
+ * Check if first n chars are hexadecimal digits
+ */
+static bool
+isxdigits_n(const char *instr, size_t n)
+{
+ for (size_t i = 0; i < n; i++)
+ if (!isxdigit((unsigned char) instr[i]))
+ return false;
+
+ return true;
+}
+
+static unsigned int
+hexval(unsigned char c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ if (c >= 'a' && c <= 'f')
+ return c - 'a' + 0xA;
+ if (c >= 'A' && c <= 'F')
+ return c - 'A' + 0xA;
+ elog(ERROR, "invalid hexadecimal digit");
+ return 0; /* not reached */
+}
+
+/*
+ * Translate string with hexadecimal digits to number
+ */
+static unsigned int
+hexval_n(const char *instr, size_t n)
+{
+ unsigned int result = 0;
+
+ for (size_t i = 0; i < n; i++)
+ result += hexval(instr[i]) << (4 * (n - i - 1));
+
+ return result;
+}
+
+/*
+ * Replaces Unicode escape sequences by Unicode characters
+ */
+Datum
+unistr(PG_FUNCTION_ARGS)
+{
+ text *input_text = PG_GETARG_TEXT_PP(0);
+ char *instr;
+ int len;
+ StringInfoData str;
+ text *result;
+ pg_wchar pair_first = 0;
+ char cbuf[MAX_UNICODE_EQUIVALENT_STRING + 1];
+
+ instr = VARDATA_ANY(input_text);
+ len = VARSIZE_ANY_EXHDR(input_text);
+
+ initStringInfo(&str);
+
+ while (len > 0)
+ {
+ if (instr[0] == '\\')
+ {
+ if (len >= 2 &&
+ instr[1] == '\\')
+ {
+ if (pair_first)
+ goto invalid_pair;
+ appendStringInfoChar(&str, '\\');
+ instr += 2;
+ len -= 2;
+ }
+ else if ((len >= 5 && isxdigits_n(instr + 1, 4)) ||
+ (len >= 6 && instr[1] == 'u' && isxdigits_n(instr + 2, 4)))
+ {
+ pg_wchar unicode;
+ int offset = instr[1] == 'u' ? 2 : 1;
+
+ unicode = hexval_n(instr + offset, 4);
+
+ if (!is_valid_unicode_codepoint(unicode))
+ ereport(ERROR,
+ errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("invalid Unicode code point: %04X", unicode));
+
+ if (pair_first)
+ {
+ if (is_utf16_surrogate_second(unicode))
+ {
+ unicode = surrogate_pair_to_codepoint(pair_first, unicode);
+ pair_first = 0;
+ }
+ else
+ goto invalid_pair;
+ }
+ else if (is_utf16_surrogate_second(unicode))
+ goto invalid_pair;
+
+ if (is_utf16_surrogate_first(unicode))
+ pair_first = unicode;
+ else
+ {
+ pg_unicode_to_server(unicode, (unsigned char *) cbuf);
+ appendStringInfoString(&str, cbuf);
+ }
+
+ instr += 4 + offset;
+ len -= 4 + offset;
+ }
+ else if (len >= 8 && instr[1] == '+' && isxdigits_n(instr + 2, 6))
+ {
+ pg_wchar unicode;
+
+ unicode = hexval_n(instr + 2, 6);
+
+ if (!is_valid_unicode_codepoint(unicode))
+ ereport(ERROR,
+ errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("invalid Unicode code point: %04X", unicode));
+
+ if (pair_first)
+ {
+ if (is_utf16_surrogate_second(unicode))
+ {
+ unicode = surrogate_pair_to_codepoint(pair_first, unicode);
+ pair_first = 0;
+ }
+ else
+ goto invalid_pair;
+ }
+ else if (is_utf16_surrogate_second(unicode))
+ goto invalid_pair;
+
+ if (is_utf16_surrogate_first(unicode))
+ pair_first = unicode;
+ else
+ {
+ pg_unicode_to_server(unicode, (unsigned char *) cbuf);
+ appendStringInfoString(&str, cbuf);
+ }
+
+ instr += 8;
+ len -= 8;
+ }
+ else if (len >= 10 && instr[1] == 'U' && isxdigits_n(instr + 2, 8))
+ {
+ pg_wchar unicode;
+
+ unicode = hexval_n(instr + 2, 8);
+
+ if (!is_valid_unicode_codepoint(unicode))
+ ereport(ERROR,
+ errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("invalid Unicode code point: %04X", unicode));
+
+ if (pair_first)
+ {
+ if (is_utf16_surrogate_second(unicode))
+ {
+ unicode = surrogate_pair_to_codepoint(pair_first, unicode);
+ pair_first = 0;
+ }
+ else
+ goto invalid_pair;
+ }
+ else if (is_utf16_surrogate_second(unicode))
+ goto invalid_pair;
+
+ if (is_utf16_surrogate_first(unicode))
+ pair_first = unicode;
+ else
+ {
+ pg_unicode_to_server(unicode, (unsigned char *) cbuf);
+ appendStringInfoString(&str, cbuf);
+ }
+
+ instr += 10;
+ len -= 10;
+ }
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("invalid Unicode escape"),
+ errhint("Unicode escapes must be \\XXXX, \\+XXXXXX, \\uXXXX, or \\UXXXXXXXX.")));
+ }
+ else
+ {
+ if (pair_first)
+ goto invalid_pair;
+
+ appendStringInfoChar(&str, *instr++);
+ len--;
+ }
+ }
+
+ /* unfinished surrogate pair? */
+ if (pair_first)
+ goto invalid_pair;
+
+ result = cstring_to_text_with_len(str.data, str.len);
+ pfree(str.data);
+
+ PG_RETURN_TEXT_P(result);
+
+invalid_pair:
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("invalid Unicode surrogate pair")));
+}
15
(1 row)
+SELECT unistr('\0064at\+0000610');
+ unistr
+--------
+ data0
+(1 row)
+
+SELECT unistr('d\u0061t\U000000610');
+ unistr
+--------
+ data0
+(1 row)
+
+SELECT unistr('a\\b');
+ unistr
+--------
+ a\b
+(1 row)
+
+-- errors:
+SELECT unistr('wrong: \db99');
+ERROR: invalid Unicode surrogate pair
+SELECT unistr('wrong: \db99\0061');
+ERROR: invalid Unicode surrogate pair
+SELECT unistr('wrong: \+00db99\+000061');
+ERROR: invalid Unicode surrogate pair
+SELECT unistr('wrong: \+2FFFFF');
+ERROR: invalid Unicode code point: 2FFFFF
+SELECT unistr('wrong: \udb99\u0061');
+ERROR: invalid Unicode surrogate pair
+SELECT unistr('wrong: \U0000db99\U00000061');
+ERROR: invalid Unicode surrogate pair
+SELECT unistr('wrong: \U002FFFFF');
+ERROR: invalid Unicode code point: 2FFFFF
+SELECT unistr('wrong: \xyz');
+ERROR: invalid Unicode escape
+HINT: Unicode escapes must be \XXXX, \+XXXXXX, \uXXXX, or \UXXXXXXXX.