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);

Reply via email to