This is an automated email from the ASF dual-hosted git repository. mtaha pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/age.git
The following commit(s) were added to refs/heads/master by this push: new 7aa2d885 fix issue 2205: left doesn't catch overflow (#2207) 7aa2d885 is described below commit 7aa2d885d74cb59985bf51648e25e95506e2f612 Author: John Gemignani <jrgemign...@gmail.com> AuthorDate: Mon Aug 18 07:50:17 2025 -0700 fix issue 2205: left doesn't catch overflow (#2207) Fixed issue 2205 where large int values aren't detected. The following functions were fixed - left, right, and substring modified: regress/expected/expr.out modified: regress/sql/expr.sql modified: src/backend/utils/adt/agtype.c Added regression tests. --- regress/expected/expr.out | 48 ++++++++++++++ regress/sql/expr.sql | 36 +++++++++++ src/backend/utils/adt/agtype.c | 143 +++++++++++++++++++++++++++++++++++++---- 3 files changed, 216 insertions(+), 11 deletions(-) diff --git a/regress/expected/expr.out b/regress/expected/expr.out index 980172da..513ea142 100644 --- a/regress/expected/expr.out +++ b/regress/expected/expr.out @@ -4556,6 +4556,18 @@ ERROR: function age_left() does not exist LINE 1: SELECT * FROM age_left(); ^ HINT: No function matches the given name and argument types. You might need to add explicit type casts. +SELECT * FROM cypher('expr', $$ + RETURN left('abcdef', -2147483648) +$$) AS (result agtype); +ERROR: left() negative values are not supported for length +SELECT * FROM cypher('expr', $$ + RETURN left('abcdef', -2147483649) +$$) AS (result agtype); +ERROR: left() length value is out of INT range +SELECT * FROM cypher('expr', $$ + RETURN left('abcdef', 2147483649) +$$) AS (result agtype); +ERROR: left() length value is out of INT range --right() SELECT * FROM cypher('expr', $$ RETURN right("123456789", 1) @@ -4636,6 +4648,18 @@ ERROR: function age_right() does not exist LINE 1: SELECT * FROM age_right(); ^ HINT: No function matches the given name and argument types. You might need to add explicit type casts. +SELECT * FROM cypher('expr', $$ + RETURN right('abcdef', -2147483648) +$$) AS (result agtype); +ERROR: right() negative values are not supported for length +SELECT * FROM cypher('expr', $$ + RETURN right('abcdef', -2147483649) +$$) AS (result agtype); +ERROR: right() length value is out of INT range +SELECT * FROM cypher('expr', $$ + RETURN right('abcdef', 2147483649) +$$) AS (result agtype); +ERROR: right() length value is out of INT range -- substring() SELECT * FROM cypher('expr', $$ RETURN substring("0123456789", 0, 1) @@ -4731,6 +4755,30 @@ SELECT * FROM age_substring(null, 1); (1 row) +SELECT * FROM cypher('expr', $$ + RETURN substring('abcdef', -2147483648, 0) +$$) AS (result agtype); +ERROR: substring() negative values are not supported for offset or length +SELECT * FROM cypher('expr', $$ + RETURN substring('abcdef', -2147483649, 0) +$$) AS (result agtype); +ERROR: substring() parameter value is out of INT range +SELECT * FROM cypher('expr', $$ + RETURN substring('abcdef', 2147483649, 0) +$$) AS (result agtype); +ERROR: substring() parameter value is out of INT range +SELECT * FROM cypher('expr', $$ + RETURN substring('abcdef', 0, -2147483648) +$$) AS (result agtype); +ERROR: substring() negative values are not supported for offset or length +SELECT * FROM cypher('expr', $$ + RETURN substring('abcdef', 0, -2147483649) +$$) AS (result agtype); +ERROR: substring() parameter value is out of INT range +SELECT * FROM cypher('expr', $$ + RETURN substring('abcdef', 0, 2147483649) +$$) AS (result agtype); +ERROR: substring() parameter value is out of INT range -- should fail SELECT * FROM cypher('expr', $$ RETURN substring("123456789", null) diff --git a/regress/sql/expr.sql b/regress/sql/expr.sql index 16987b81..83f21856 100644 --- a/regress/sql/expr.sql +++ b/regress/sql/expr.sql @@ -1906,6 +1906,15 @@ $$) AS (results agtype); SELECT * FROM age_left('123456789', null); SELECT * FROM age_left('123456789', -1); SELECT * FROM age_left(); +SELECT * FROM cypher('expr', $$ + RETURN left('abcdef', -2147483648) +$$) AS (result agtype); +SELECT * FROM cypher('expr', $$ + RETURN left('abcdef', -2147483649) +$$) AS (result agtype); +SELECT * FROM cypher('expr', $$ + RETURN left('abcdef', 2147483649) +$$) AS (result agtype); --right() SELECT * FROM cypher('expr', $$ RETURN right("123456789", 1) @@ -1939,6 +1948,15 @@ $$) AS (results agtype); SELECT * FROM age_right('123456789', null); SELECT * FROM age_right('123456789', -1); SELECT * FROM age_right(); +SELECT * FROM cypher('expr', $$ + RETURN right('abcdef', -2147483648) +$$) AS (result agtype); +SELECT * FROM cypher('expr', $$ + RETURN right('abcdef', -2147483649) +$$) AS (result agtype); +SELECT * FROM cypher('expr', $$ + RETURN right('abcdef', 2147483649) +$$) AS (result agtype); -- substring() SELECT * FROM cypher('expr', $$ RETURN substring("0123456789", 0, 1) @@ -1969,6 +1987,24 @@ $$) AS (results agtype); SELECT * FROM age_substring(null, null, null); SELECT * FROM age_substring(null, null); SELECT * FROM age_substring(null, 1); +SELECT * FROM cypher('expr', $$ + RETURN substring('abcdef', -2147483648, 0) +$$) AS (result agtype); +SELECT * FROM cypher('expr', $$ + RETURN substring('abcdef', -2147483649, 0) +$$) AS (result agtype); +SELECT * FROM cypher('expr', $$ + RETURN substring('abcdef', 2147483649, 0) +$$) AS (result agtype); +SELECT * FROM cypher('expr', $$ + RETURN substring('abcdef', 0, -2147483648) +$$) AS (result agtype); +SELECT * FROM cypher('expr', $$ + RETURN substring('abcdef', 0, -2147483649) +$$) AS (result agtype); +SELECT * FROM cypher('expr', $$ + RETURN substring('abcdef', 0, 2147483649) +$$) AS (result agtype); -- should fail SELECT * FROM cypher('expr', $$ RETURN substring("123456789", null) diff --git a/src/backend/utils/adt/agtype.c b/src/backend/utils/adt/agtype.c index 17e08353..be838cbd 100644 --- a/src/backend/utils/adt/agtype.c +++ b/src/backend/utils/adt/agtype.c @@ -8144,7 +8144,7 @@ Datum age_right(PG_FUNCTION_ARGS) agtype_value agtv_result; text *text_string = NULL; char *string = NULL; - int string_len; + int64 string_len; Oid type; /* extract argument values */ @@ -8152,18 +8152,21 @@ Datum age_right(PG_FUNCTION_ARGS) /* check number of args */ if (nargs != 2) + { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("right() invalid number of arguments"))); - + } /* check for a null string */ if (nargs < 0 || nulls[0]) + { PG_RETURN_NULL(); - + } /* check for a null length */ if (nulls[1]) + { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("right() length parameter cannot be null"))); - + } /* right() supports text, cstring, or the agtype string input */ arg = args[0]; type = types[0]; @@ -8171,13 +8174,19 @@ Datum age_right(PG_FUNCTION_ARGS) if (type != AGTYPEOID) { if (type == CSTRINGOID) + { text_string = cstring_to_text(DatumGetCString(arg)); + } else if (type == TEXTOID) + { text_string = DatumGetTextPP(arg); + } else + { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("right() unsupported argument type %d", type))); + } } else { @@ -8188,21 +8197,29 @@ Datum age_right(PG_FUNCTION_ARGS) agt_arg = DATUM_GET_AGTYPE_P(arg); if (!AGT_ROOT_IS_SCALAR(agt_arg)) + { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("right() only supports scalar arguments"))); + } agtv_value = get_ith_agtype_value_from_container(&agt_arg->root, 0); /* check for agtype null */ if (agtv_value->type == AGTV_NULL) + { PG_RETURN_NULL(); + } if (agtv_value->type == AGTV_STRING) + { text_string = cstring_to_text_with_len(agtv_value->val.string.val, agtv_value->val.string.len); + } else + { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("right() unsupported argument agtype %d", agtv_value->type))); + } } /* right() only supports integer and agtype integer for the second parameter. */ @@ -8212,14 +8229,22 @@ Datum age_right(PG_FUNCTION_ARGS) if (type != AGTYPEOID) { if (type == INT2OID) + { string_len = (int64) DatumGetInt16(arg); + } else if (type == INT4OID) + { string_len = (int64) DatumGetInt32(arg); + } else if (type == INT8OID) + { string_len = (int64) DatumGetInt64(arg); + } else + { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("right() unsupported argument type %d", type))); + } } else { @@ -8230,21 +8255,30 @@ Datum age_right(PG_FUNCTION_ARGS) agt_arg = DATUM_GET_AGTYPE_P(arg); if (!AGT_ROOT_IS_SCALAR(agt_arg)) + { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("right() only supports scalar arguments"))); + } agtv_value = get_ith_agtype_value_from_container(&agt_arg->root, 0); /* no need to check for agtype null because it is an error if found */ if (agtv_value->type != AGTV_INTEGER) + { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("right() unsupported argument agtype %d", agtv_value->type))); + } string_len = agtv_value->val.int_value; } - /* negative values are not supported in the opencypher spec */ + /* out of range and negative values are not supported in the opencypher spec */ + if (string_len > INT_MAX || string_len < INT_MIN) + { + ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("right() length value is out of INT range"))); + } if (string_len < 0) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("right() negative values are not supported for length"))); @@ -8281,7 +8315,7 @@ Datum age_left(PG_FUNCTION_ARGS) agtype_value agtv_result; text *text_string = NULL; char *string = NULL; - int string_len; + int64 string_len; Oid type; /* extract argument values */ @@ -8289,17 +8323,23 @@ Datum age_left(PG_FUNCTION_ARGS) /* check number of args */ if (nargs != 2) + { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("left() invalid number of arguments"))); + } /* check for a null string */ if (nargs < 0 || nulls[0]) + { PG_RETURN_NULL(); + } /* check for a null length */ if (nulls[1]) + { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("left() length parameter cannot be null"))); + } /* left() supports text, cstring, or the agtype string input */ arg = args[0]; @@ -8308,13 +8348,19 @@ Datum age_left(PG_FUNCTION_ARGS) if (type != AGTYPEOID) { if (type == CSTRINGOID) + { text_string = cstring_to_text(DatumGetCString(arg)); + } else if (type == TEXTOID) + { text_string = DatumGetTextPP(arg); + } else + { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("left() unsupported argument type %d", type))); + } } else { @@ -8325,21 +8371,29 @@ Datum age_left(PG_FUNCTION_ARGS) agt_arg = DATUM_GET_AGTYPE_P(arg); if (!AGT_ROOT_IS_SCALAR(agt_arg)) + { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("left() only supports scalar arguments"))); + } agtv_value = get_ith_agtype_value_from_container(&agt_arg->root, 0); /* check for agtype null */ if (agtv_value->type == AGTV_NULL) + { PG_RETURN_NULL(); + } if (agtv_value->type == AGTV_STRING) + { text_string = cstring_to_text_with_len(agtv_value->val.string.val, agtv_value->val.string.len); + } else + { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("left() unsupported argument agtype %d", agtv_value->type))); + } } /* left() only supports integer and agtype integer for the second parameter. */ @@ -8349,14 +8403,22 @@ Datum age_left(PG_FUNCTION_ARGS) if (type != AGTYPEOID) { if (type == INT2OID) + { string_len = (int64) DatumGetInt16(arg); + } else if (type == INT4OID) + { string_len = (int64) DatumGetInt32(arg); + } else if (type == INT8OID) + { string_len = (int64) DatumGetInt64(arg); + } else + { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("left() unsupported argument type %d", type))); + } } else { @@ -8367,24 +8429,37 @@ Datum age_left(PG_FUNCTION_ARGS) agt_arg = DATUM_GET_AGTYPE_P(arg); if (!AGT_ROOT_IS_SCALAR(agt_arg)) + { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("left() only supports scalar arguments"))); + } agtv_value = get_ith_agtype_value_from_container(&agt_arg->root, 0); /* no need to check for agtype null because it is an error if found */ if (agtv_value->type != AGTV_INTEGER) + { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("left() unsupported argument agtype %d", agtv_value->type))); + } string_len = agtv_value->val.int_value; } - /* negative values are not supported in the opencypher spec */ + /* out of range and negative values are not supported in the opencypher spec */ + if (string_len > INT_MAX || string_len < INT_MIN) + { + ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("left() length value is out of INT range"))); + } + if (string_len < 0) + { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("left() negative values are not supported for length"))); + } + /* * We need the string as a text string so that we can let PG deal with @@ -8418,7 +8493,7 @@ Datum age_substring(PG_FUNCTION_ARGS) agtype_value agtv_result; text *text_string = NULL; char *string = NULL; - int param; + int64 param; int string_start = 0; int string_len = 0; int i; @@ -8429,19 +8504,24 @@ Datum age_substring(PG_FUNCTION_ARGS) /* check number of args */ if (nargs < 2 || nargs > 3) + { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("substring() invalid number of arguments"))); + } /* check for null */ if (nargs < 0 || nulls[0]) + { PG_RETURN_NULL(); + } /* neither offset or length can be null if there is a valid string */ if ((nargs == 2 && nulls[1]) || (nargs == 3 && nulls[2])) + { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("substring() offset or length cannot be null"))); - + } /* substring() supports text, cstring, or the agtype string input */ arg = args[0]; type = types[0]; @@ -8449,13 +8529,19 @@ Datum age_substring(PG_FUNCTION_ARGS) if (type != AGTYPEOID) { if (type == CSTRINGOID) + { text_string = cstring_to_text(DatumGetCString(arg)); + } else if (type == TEXTOID) + { text_string = DatumGetTextPP(arg); + } else + { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("substring() unsupported argument type %d", type))); + } } else { @@ -8466,21 +8552,29 @@ Datum age_substring(PG_FUNCTION_ARGS) agt_arg = DATUM_GET_AGTYPE_P(arg); if (!AGT_ROOT_IS_SCALAR(agt_arg)) + { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("substring() only supports scalar arguments"))); + } agtv_value = get_ith_agtype_value_from_container(&agt_arg->root, 0); /* check for agtype null */ if (agtv_value->type == AGTV_NULL) + { PG_RETURN_NULL(); + } if (agtv_value->type == AGTV_STRING) + { text_string = cstring_to_text_with_len(agtv_value->val.string.val, agtv_value->val.string.len); + } else + { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("substring() unsupported argument agtype %d", agtv_value->type))); + } } /* @@ -8495,15 +8589,23 @@ Datum age_substring(PG_FUNCTION_ARGS) if (type != AGTYPEOID) { if (type == INT2OID) + { param = (int64) DatumGetInt16(arg); + } else if (type == INT4OID) + { param = (int64) DatumGetInt32(arg); + } else if (type == INT8OID) + { param = (int64) DatumGetInt64(arg); + } else + { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("substring() unsupported argument type %d", type))); + } } else { @@ -8514,30 +8616,46 @@ Datum age_substring(PG_FUNCTION_ARGS) agt_arg = DATUM_GET_AGTYPE_P(arg); if (!AGT_ROOT_IS_SCALAR(agt_arg)) + { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("substring() only supports scalar arguments"))); - + } agtv_value = get_ith_agtype_value_from_container(&agt_arg->root, 0); /* no need to check for agtype null because it is an error if found */ if (agtv_value->type != AGTV_INTEGER) + { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("substring() unsupported argument agtype %d", agtv_value->type))); + } param = agtv_value->val.int_value; } + /* out of range values are not supported in the opencypher spec */ + if (param > INT_MAX || param < INT_MIN) + { + ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("substring() parameter value is out of INT range"))); + } + if (i == 1) + { string_start = param; + } if (i == 2) + { string_len = param; + } } /* negative values are not supported in the opencypher spec */ if (string_start < 0 || string_len < 0) + { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("substring() negative values are not supported for offset or length"))); + } /* cypher substring is 0 based while PG's is 1 based */ string_start += 1; @@ -8549,16 +8667,19 @@ Datum age_substring(PG_FUNCTION_ARGS) /* if optional length is left out */ if (nargs == 2) + { text_string = DatumGetTextPP(DirectFunctionCall2(text_substr_no_len, PointerGetDatum(text_string), Int64GetDatum(string_start))); + } /* if length is given */ else + { text_string = DatumGetTextPP(DirectFunctionCall3(text_substr, PointerGetDatum(text_string), Int64GetDatum(string_start), Int64GetDatum(string_len))); - + } /* convert it back to a cstring */ string = text_to_cstring(text_string); string_len = strlen(string);