diff --git a/src/backend/utils/adt/oracle_compat.c b/src/backend/utils/adt/oracle_compat.c index 471c6967be0..87deeda7b23 100644 --- a/src/backend/utils/adt/oracle_compat.c +++ b/src/backend/utils/adt/oracle_compat.c @@ -932,8 +932,10 @@ ascii(PG_FUNCTION_ARGS) text *string = PG_GETARG_TEXT_PP(0); int encoding = GetDatabaseEncoding(); unsigned char *data; + int len; - if (VARSIZE_ANY_EXHDR(string) <= 0) + len = VARSIZE_ANY_EXHDR(string); + if (len <= 0) PG_RETURN_INT32(0); data = (unsigned char *) VARDATA_ANY(string); @@ -956,18 +958,31 @@ ascii(PG_FUNCTION_ARGS) result = *data & 0x0F; tbytes = 2; } - else + else if (*data > 0xC0) { - Assert(*data > 0xC0); result = *data & 0x1f; tbytes = 1; } + else + ereport(ERROR, + (errcode(ERRCODE_CHARACTER_NOT_IN_REPERTOIRE), + errmsg("invalid byte sequence for encoding \"%s\"", + GetDatabaseEncodingName()))); - Assert(tbytes > 0); + /* All continuation bytes are present in the input */ + if (tbytes >= len) + ereport(ERROR, + (errcode(ERRCODE_CHARACTER_NOT_IN_REPERTOIRE), + errmsg("invalid byte sequence for encoding \"%s\"", + GetDatabaseEncodingName()))); for (i = 1; i <= tbytes; i++) { - Assert((data[i] & 0xC0) == 0x80); + if (unlikely((data[i] & 0xC0) != 0x80)) + ereport(ERROR, + (errcode(ERRCODE_CHARACTER_NOT_IN_REPERTOIRE), + errmsg("invalid byte sequence for encoding \"%s\"", + GetDatabaseEncodingName()))); result = (result << 6) + (data[i] & 0x3f); } diff --git a/src/test/regress/input/encoding.source b/src/test/regress/input/encoding.source index d080bef2f80..72652f5494c 100644 --- a/src/test/regress/input/encoding.source +++ b/src/test/regress/input/encoding.source @@ -223,6 +223,18 @@ ALTER TABLE toast_3b_utf8 RENAME TO toast_4b_utf8; UPDATE toast_4b_utf8 SET c = repeat(U&'\+01F680', 3000); SELECT SUBSTRING(c FROM 3000 FOR 1) FROM toast_4b_utf8; +-- ascii() and multibyte characters +SELECT ascii(test_bytea_to_text('\xc3')); +SELECT ascii(test_bytea_to_text('\xe2')); +SELECT ascii(test_bytea_to_text('\xe282')); +SELECT ascii(test_bytea_to_text('\xf0')); +SELECT ascii(test_bytea_to_text('\xf09f')); +SELECT ascii(test_bytea_to_text('\xf09f98')); +-- invalid continuation byte +SELECT ascii(test_bytea_to_text('\xc3ff')); +-- invalid leading byte +SELECT ascii(test_bytea_to_text('\x80')); + DROP TABLE encoding_tests; DROP TABLE toast_4b_utf8; DROP FUNCTION test_encoding; diff --git a/src/test/regress/output/encoding.source b/src/test/regress/output/encoding.source index 6e730e8737e..af5c21cb59a 100644 --- a/src/test/regress/output/encoding.source +++ b/src/test/regress/output/encoding.source @@ -413,6 +413,25 @@ SELECT SUBSTRING(c FROM 3000 FOR 1) FROM toast_4b_utf8; 🚀 (1 row) +-- ascii() and multibyte characters +SELECT ascii(test_bytea_to_text('\xc3')); +ERROR: invalid byte sequence for encoding "UTF8" +SELECT ascii(test_bytea_to_text('\xe2')); +ERROR: invalid byte sequence for encoding "UTF8" +SELECT ascii(test_bytea_to_text('\xe282')); +ERROR: invalid byte sequence for encoding "UTF8" +SELECT ascii(test_bytea_to_text('\xf0')); +ERROR: invalid byte sequence for encoding "UTF8" +SELECT ascii(test_bytea_to_text('\xf09f')); +ERROR: invalid byte sequence for encoding "UTF8" +SELECT ascii(test_bytea_to_text('\xf09f98')); +ERROR: invalid byte sequence for encoding "UTF8" +-- invalid continuation byte +SELECT ascii(test_bytea_to_text('\xc3ff')); +ERROR: invalid byte sequence for encoding "UTF8" +-- invalid leading byte +SELECT ascii(test_bytea_to_text('\x80')); +ERROR: invalid byte sequence for encoding "UTF8" DROP TABLE encoding_tests; DROP TABLE toast_4b_utf8; DROP FUNCTION test_encoding;