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 <[email protected]>
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);