aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/backend/utils/adt/encode.c35
-rw-r--r--src/backend/utils/adt/varchar.c33
-rw-r--r--src/backend/utils/adt/varlena.c8
-rw-r--r--src/include/utils/builtins.h2
-rw-r--r--src/test/regress/expected/char.out19
-rw-r--r--src/test/regress/expected/char_1.out19
-rw-r--r--src/test/regress/expected/char_2.out19
-rw-r--r--src/test/regress/expected/strings.out25
-rw-r--r--src/test/regress/expected/varchar.out19
-rw-r--r--src/test/regress/expected/varchar_1.out19
-rw-r--r--src/test/regress/expected/varchar_2.out19
-rw-r--r--src/test/regress/sql/char.sql5
-rw-r--r--src/test/regress/sql/strings.sql6
-rw-r--r--src/test/regress/sql/varchar.sql5
14 files changed, 203 insertions, 30 deletions
diff --git a/src/backend/utils/adt/encode.c b/src/backend/utils/adt/encode.c
index feb3e830e4f..f3bb5cca43c 100644
--- a/src/backend/utils/adt/encode.c
+++ b/src/backend/utils/adt/encode.c
@@ -171,8 +171,8 @@ hex_encode(const char *src, size_t len, char *dst)
return (uint64) len * 2;
}
-static inline char
-get_hex(const char *cp)
+static inline bool
+get_hex(const char *cp, char *out)
{
unsigned char c = (unsigned char) *cp;
int res = -1;
@@ -180,18 +180,20 @@ get_hex(const char *cp)
if (c < 127)
res = hexlookup[c];
- if (res < 0)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid hexadecimal digit: \"%.*s\"",
- pg_mblen(cp), cp)));
+ *out = (char) res;
- return (char) res;
+ return (res >= 0);
}
uint64
hex_decode(const char *src, size_t len, char *dst)
{
+ return hex_decode_safe(src, len, dst, NULL);
+}
+
+uint64
+hex_decode_safe(const char *src, size_t len, char *dst, Node *escontext)
+{
const char *s,
*srcend;
char v1,
@@ -208,16 +210,23 @@ hex_decode(const char *src, size_t len, char *dst)
s++;
continue;
}
- v1 = get_hex(s) << 4;
+ if (!get_hex(s, &v1))
+ ereturn(escontext, 0,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("invalid hexadecimal digit: \"%.*s\"",
+ pg_mblen(s), s)));
s++;
if (s >= srcend)
- ereport(ERROR,
+ ereturn(escontext, 0,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid hexadecimal data: odd number of digits")));
-
- v2 = get_hex(s);
+ if (!get_hex(s, &v2))
+ ereturn(escontext, 0,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("invalid hexadecimal digit: \"%.*s\"",
+ pg_mblen(s), s)));
s++;
- *p++ = v1 | v2;
+ *p++ = (v1 << 4) | v2;
}
return p - dst;
diff --git a/src/backend/utils/adt/varchar.c b/src/backend/utils/adt/varchar.c
index a63c498181e..01a2db6b23b 100644
--- a/src/backend/utils/adt/varchar.c
+++ b/src/backend/utils/adt/varchar.c
@@ -122,9 +122,13 @@ anychar_typmodout(int32 typmod)
*
* If the input string is too long, raise an error, unless the extra
* characters are spaces, in which case they're truncated. (per SQL)
+ *
+ * If escontext points to an ErrorSaveContext node, that is filled instead
+ * of throwing an error; the caller must check SOFT_ERROR_OCCURRED()
+ * to detect errors.
*/
static BpChar *
-bpchar_input(const char *s, size_t len, int32 atttypmod)
+bpchar_input(const char *s, size_t len, int32 atttypmod, Node *escontext)
{
BpChar *result;
char *r;
@@ -153,7 +157,7 @@ bpchar_input(const char *s, size_t len, int32 atttypmod)
for (j = mbmaxlen; j < len; j++)
{
if (s[j] != ' ')
- ereport(ERROR,
+ ereturn(escontext, NULL,
(errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION),
errmsg("value too long for type character(%d)",
(int) maxlen)));
@@ -195,14 +199,13 @@ Datum
bpcharin(PG_FUNCTION_ARGS)
{
char *s = PG_GETARG_CSTRING(0);
-
#ifdef NOT_USED
Oid typelem = PG_GETARG_OID(1);
#endif
int32 atttypmod = PG_GETARG_INT32(2);
BpChar *result;
- result = bpchar_input(s, strlen(s), atttypmod);
+ result = bpchar_input(s, strlen(s), atttypmod, fcinfo->context);
PG_RETURN_BPCHAR_P(result);
}
@@ -228,7 +231,6 @@ Datum
bpcharrecv(PG_FUNCTION_ARGS)
{
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
-
#ifdef NOT_USED
Oid typelem = PG_GETARG_OID(1);
#endif
@@ -238,7 +240,7 @@ bpcharrecv(PG_FUNCTION_ARGS)
int nbytes;
str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
- result = bpchar_input(str, nbytes, atttypmod);
+ result = bpchar_input(str, nbytes, atttypmod, NULL);
pfree(str);
PG_RETURN_BPCHAR_P(result);
}
@@ -448,11 +450,12 @@ bpchartypmodout(PG_FUNCTION_ARGS)
* If the input string is too long, raise an error, unless the extra
* characters are spaces, in which case they're truncated. (per SQL)
*
- * Uses the C string to text conversion function, which is only appropriate
- * if VarChar and text are equivalent types.
+ * If escontext points to an ErrorSaveContext node, that is filled instead
+ * of throwing an error; the caller must check SOFT_ERROR_OCCURRED()
+ * to detect errors.
*/
static VarChar *
-varchar_input(const char *s, size_t len, int32 atttypmod)
+varchar_input(const char *s, size_t len, int32 atttypmod, Node *escontext)
{
VarChar *result;
size_t maxlen;
@@ -468,7 +471,7 @@ varchar_input(const char *s, size_t len, int32 atttypmod)
for (j = mbmaxlen; j < len; j++)
{
if (s[j] != ' ')
- ereport(ERROR,
+ ereturn(escontext, NULL,
(errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION),
errmsg("value too long for type character varying(%d)",
(int) maxlen)));
@@ -477,6 +480,10 @@ varchar_input(const char *s, size_t len, int32 atttypmod)
len = mbmaxlen;
}
+ /*
+ * We can use cstring_to_text_with_len because VarChar and text are
+ * binary-compatible types.
+ */
result = (VarChar *) cstring_to_text_with_len(s, len);
return result;
}
@@ -489,14 +496,13 @@ Datum
varcharin(PG_FUNCTION_ARGS)
{
char *s = PG_GETARG_CSTRING(0);
-
#ifdef NOT_USED
Oid typelem = PG_GETARG_OID(1);
#endif
int32 atttypmod = PG_GETARG_INT32(2);
VarChar *result;
- result = varchar_input(s, strlen(s), atttypmod);
+ result = varchar_input(s, strlen(s), atttypmod, fcinfo->context);
PG_RETURN_VARCHAR_P(result);
}
@@ -522,7 +528,6 @@ Datum
varcharrecv(PG_FUNCTION_ARGS)
{
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
-
#ifdef NOT_USED
Oid typelem = PG_GETARG_OID(1);
#endif
@@ -532,7 +537,7 @@ varcharrecv(PG_FUNCTION_ARGS)
int nbytes;
str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
- result = varchar_input(str, nbytes, atttypmod);
+ result = varchar_input(str, nbytes, atttypmod, NULL);
pfree(str);
PG_RETURN_VARCHAR_P(result);
}
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index c5e7ee7ca2d..1c52deec556 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -295,6 +295,7 @@ Datum
byteain(PG_FUNCTION_ARGS)
{
char *inputText = PG_GETARG_CSTRING(0);
+ Node *escontext = fcinfo->context;
char *tp;
char *rp;
int bc;
@@ -307,7 +308,8 @@ byteain(PG_FUNCTION_ARGS)
bc = (len - 2) / 2 + VARHDRSZ; /* maximum possible length */
result = palloc(bc);
- bc = hex_decode(inputText + 2, len - 2, VARDATA(result));
+ bc = hex_decode_safe(inputText + 2, len - 2, VARDATA(result),
+ escontext);
SET_VARSIZE(result, bc + VARHDRSZ); /* actual length */
PG_RETURN_BYTEA_P(result);
@@ -331,7 +333,7 @@ byteain(PG_FUNCTION_ARGS)
/*
* one backslash, not followed by another or ### valid octal
*/
- ereport(ERROR,
+ ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s", "bytea")));
}
@@ -372,7 +374,7 @@ byteain(PG_FUNCTION_ARGS)
/*
* We should never get here. The first pass should not allow it.
*/
- ereport(ERROR,
+ ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s", "bytea")));
}
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 10d13b0f1e9..15373ba68f7 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -34,6 +34,8 @@ extern int errdomainconstraint(Oid datatypeOid, const char *conname);
/* encode.c */
extern uint64 hex_encode(const char *src, size_t len, char *dst);
extern uint64 hex_decode(const char *src, size_t len, char *dst);
+extern uint64 hex_decode_safe(const char *src, size_t len, char *dst,
+ Node *escontext);
/* int.c */
extern int2vector *buildint2vector(const int16 *int2s, int n);
diff --git a/src/test/regress/expected/char.out b/src/test/regress/expected/char.out
index ea9b0b8eeb3..199001b2fed 100644
--- a/src/test/regress/expected/char.out
+++ b/src/test/regress/expected/char.out
@@ -119,6 +119,25 @@ SELECT * FROM CHAR_TBL;
abcd
(4 rows)
+-- Also try it with non-error-throwing API
+SELECT pg_input_is_valid('abcd ', 'char(4)');
+ pg_input_is_valid
+-------------------
+ t
+(1 row)
+
+SELECT pg_input_is_valid('abcde', 'char(4)');
+ pg_input_is_valid
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('abcde', 'char(4)');
+ pg_input_error_message
+--------------------------------------
+ value too long for type character(4)
+(1 row)
+
--
-- Also test "char", which is an ad-hoc one-byte type. It can only
-- really store ASCII characters, but we allow high-bit-set characters
diff --git a/src/test/regress/expected/char_1.out b/src/test/regress/expected/char_1.out
index ffd31551de5..3dcb0daa0d6 100644
--- a/src/test/regress/expected/char_1.out
+++ b/src/test/regress/expected/char_1.out
@@ -119,6 +119,25 @@ SELECT * FROM CHAR_TBL;
abcd
(4 rows)
+-- Also try it with non-error-throwing API
+SELECT pg_input_is_valid('abcd ', 'char(4)');
+ pg_input_is_valid
+-------------------
+ t
+(1 row)
+
+SELECT pg_input_is_valid('abcde', 'char(4)');
+ pg_input_is_valid
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('abcde', 'char(4)');
+ pg_input_error_message
+--------------------------------------
+ value too long for type character(4)
+(1 row)
+
--
-- Also test "char", which is an ad-hoc one-byte type. It can only
-- really store ASCII characters, but we allow high-bit-set characters
diff --git a/src/test/regress/expected/char_2.out b/src/test/regress/expected/char_2.out
index 56818f824b5..dd5d34fe8da 100644
--- a/src/test/regress/expected/char_2.out
+++ b/src/test/regress/expected/char_2.out
@@ -119,6 +119,25 @@ SELECT * FROM CHAR_TBL;
abcd
(4 rows)
+-- Also try it with non-error-throwing API
+SELECT pg_input_is_valid('abcd ', 'char(4)');
+ pg_input_is_valid
+-------------------
+ t
+(1 row)
+
+SELECT pg_input_is_valid('abcde', 'char(4)');
+ pg_input_is_valid
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('abcde', 'char(4)');
+ pg_input_error_message
+--------------------------------------
+ value too long for type character(4)
+(1 row)
+
--
-- Also test "char", which is an ad-hoc one-byte type. It can only
-- really store ASCII characters, but we allow high-bit-set characters
diff --git a/src/test/regress/expected/strings.out b/src/test/regress/expected/strings.out
index 69d7ed4ef1c..f028c1f10f2 100644
--- a/src/test/regress/expected/strings.out
+++ b/src/test/regress/expected/strings.out
@@ -273,6 +273,31 @@ SELECT E'De\\123dBeEf'::bytea;
DeSdBeEf
(1 row)
+-- Test non-error-throwing API too
+SELECT pg_input_is_valid(E'\\xDeAdBeE', 'bytea');
+ pg_input_is_valid
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message(E'\\xDeAdBeE', 'bytea');
+ pg_input_error_message
+------------------------------------------------
+ invalid hexadecimal data: odd number of digits
+(1 row)
+
+SELECT pg_input_error_message(E'\\xDeAdBeEx', 'bytea');
+ pg_input_error_message
+--------------------------------
+ invalid hexadecimal digit: "x"
+(1 row)
+
+SELECT pg_input_error_message(E'foo\\99bar', 'bytea');
+ pg_input_error_message
+-------------------------------------
+ invalid input syntax for type bytea
+(1 row)
+
--
-- test conversions between various string types
-- E021-10 implicit casting among the character data types
diff --git a/src/test/regress/expected/varchar.out b/src/test/regress/expected/varchar.out
index f1a8202d9f9..62b683d86ff 100644
--- a/src/test/regress/expected/varchar.out
+++ b/src/test/regress/expected/varchar.out
@@ -111,3 +111,22 @@ SELECT * FROM VARCHAR_TBL;
abcd
(4 rows)
+-- Also try it with non-error-throwing API
+SELECT pg_input_is_valid('abcd ', 'varchar(4)');
+ pg_input_is_valid
+-------------------
+ t
+(1 row)
+
+SELECT pg_input_is_valid('abcde', 'varchar(4)');
+ pg_input_is_valid
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('abcde', 'varchar(4)');
+ pg_input_error_message
+----------------------------------------------
+ value too long for type character varying(4)
+(1 row)
+
diff --git a/src/test/regress/expected/varchar_1.out b/src/test/regress/expected/varchar_1.out
index 6f01ef969ec..6690f81c0b8 100644
--- a/src/test/regress/expected/varchar_1.out
+++ b/src/test/regress/expected/varchar_1.out
@@ -111,3 +111,22 @@ SELECT * FROM VARCHAR_TBL;
abcd
(4 rows)
+-- Also try it with non-error-throwing API
+SELECT pg_input_is_valid('abcd ', 'varchar(4)');
+ pg_input_is_valid
+-------------------
+ t
+(1 row)
+
+SELECT pg_input_is_valid('abcde', 'varchar(4)');
+ pg_input_is_valid
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('abcde', 'varchar(4)');
+ pg_input_error_message
+----------------------------------------------
+ value too long for type character varying(4)
+(1 row)
+
diff --git a/src/test/regress/expected/varchar_2.out b/src/test/regress/expected/varchar_2.out
index 72e57050ea4..ad8aa7c6933 100644
--- a/src/test/regress/expected/varchar_2.out
+++ b/src/test/regress/expected/varchar_2.out
@@ -111,3 +111,22 @@ SELECT * FROM VARCHAR_TBL;
abcd
(4 rows)
+-- Also try it with non-error-throwing API
+SELECT pg_input_is_valid('abcd ', 'varchar(4)');
+ pg_input_is_valid
+-------------------
+ t
+(1 row)
+
+SELECT pg_input_is_valid('abcde', 'varchar(4)');
+ pg_input_is_valid
+-------------------
+ f
+(1 row)
+
+SELECT pg_input_error_message('abcde', 'varchar(4)');
+ pg_input_error_message
+----------------------------------------------
+ value too long for type character varying(4)
+(1 row)
+
diff --git a/src/test/regress/sql/char.sql b/src/test/regress/sql/char.sql
index 120fed53e5c..8aa43b0fb8e 100644
--- a/src/test/regress/sql/char.sql
+++ b/src/test/regress/sql/char.sql
@@ -72,6 +72,11 @@ INSERT INTO CHAR_TBL (f1) VALUES ('abcde');
SELECT * FROM CHAR_TBL;
+-- Also try it with non-error-throwing API
+SELECT pg_input_is_valid('abcd ', 'char(4)');
+SELECT pg_input_is_valid('abcde', 'char(4)');
+SELECT pg_input_error_message('abcde', 'char(4)');
+
--
-- Also test "char", which is an ad-hoc one-byte type. It can only
-- really store ASCII characters, but we allow high-bit-set characters
diff --git a/src/test/regress/sql/strings.sql b/src/test/regress/sql/strings.sql
index 04109f599dd..932f71cbca4 100644
--- a/src/test/regress/sql/strings.sql
+++ b/src/test/regress/sql/strings.sql
@@ -85,6 +85,12 @@ SELECT E'DeAdBeEf'::bytea;
SELECT E'De\\000dBeEf'::bytea;
SELECT E'De\\123dBeEf'::bytea;
+-- Test non-error-throwing API too
+SELECT pg_input_is_valid(E'\\xDeAdBeE', 'bytea');
+SELECT pg_input_error_message(E'\\xDeAdBeE', 'bytea');
+SELECT pg_input_error_message(E'\\xDeAdBeEx', 'bytea');
+SELECT pg_input_error_message(E'foo\\99bar', 'bytea');
+
--
-- test conversions between various string types
-- E021-10 implicit casting among the character data types
diff --git a/src/test/regress/sql/varchar.sql b/src/test/regress/sql/varchar.sql
index a9708214262..df16da37a73 100644
--- a/src/test/regress/sql/varchar.sql
+++ b/src/test/regress/sql/varchar.sql
@@ -66,3 +66,8 @@ DROP TABLE VARCHAR_TBL;
INSERT INTO VARCHAR_TBL (f1) VALUES ('abcde');
SELECT * FROM VARCHAR_TBL;
+
+-- Also try it with non-error-throwing API
+SELECT pg_input_is_valid('abcd ', 'varchar(4)');
+SELECT pg_input_is_valid('abcde', 'varchar(4)');
+SELECT pg_input_error_message('abcde', 'varchar(4)');