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 2803ffa7 Fix issue 2201: unexpected empty string behavior (#2203)
2803ffa7 is described below
commit 2803ffa7801f37a0d02a22044150e04b2845d43a
Author: John Gemignani <[email protected]>
AuthorDate: Wed Aug 13 08:13:29 2025 -0700
Fix issue 2201: unexpected empty string behavior (#2203)
This PR fixes the issue of some string functions returning NULL
instead of the empty string.
The string functions affected and corrected are -
reverse, toupper, tolower, rtrim, ltrim, trim, right, left, substring,
and replace.
Added additional regression tests. Corrected previous tests.
modified: regress/expected/expr.out
modified: regress/sql/expr.sql
modified: src/backend/utils/adt/agtype.c
---
regress/expected/expr.out | 245 +++++++++++++++++++++++++++++++++++------
regress/sql/expr.sql | 80 ++++++++++++--
src/backend/utils/adt/agtype.c | 42 -------
3 files changed, 281 insertions(+), 86 deletions(-)
diff --git a/regress/expected/expr.out b/regress/expected/expr.out
index 76eecbe0..980172da 100644
--- a/regress/expected/expr.out
+++ b/regress/expected/expr.out
@@ -3927,6 +3927,41 @@ SELECT * FROM age_reverse('gnirts a si siht'::cstring);
"this is a string"
(1 row)
+-- should return empty string
+SELECT * FROM age_reverse('');
+ age_reverse
+-------------
+ ""
+(1 row)
+
+SELECT * FROM age_reverse(''::text);
+ age_reverse
+-------------
+ ""
+(1 row)
+
+SELECT * FROM age_reverse(''::cstring);
+ age_reverse
+-------------
+ ""
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+ RETURN reverse('')
+$$) AS (result agtype);
+ result
+--------
+ ""
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+ RETURN reverse("")
+$$) AS (result agtype);
+ result
+--------
+ ""
+(1 row)
+
-- should return null
SELECT * FROM cypher('expr', $$
RETURN reverse(null)
@@ -4104,6 +4139,75 @@ SELECT * FROM age_tolower('CSTRING'::cstring);
"cstring"
(1 row)
+-- should return empty string
+SELECT * FROM age_toupper('');
+ age_toupper
+-------------
+ ""
+(1 row)
+
+SELECT * FROM age_toupper(''::text);
+ age_toupper
+-------------
+ ""
+(1 row)
+
+SELECT * FROM age_toupper(''::cstring);
+ age_toupper
+-------------
+ ""
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+ RETURN toupper('')
+$$) AS (result agtype);
+ result
+--------
+ ""
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+ RETURN toupper("")
+$$) AS (result agtype);
+ result
+--------
+ ""
+(1 row)
+
+SELECT * FROM age_tolower('');
+ age_tolower
+-------------
+ ""
+(1 row)
+
+SELECT * FROM age_tolower(''::text);
+ age_tolower
+-------------
+ ""
+(1 row)
+
+SELECT * FROM age_tolower(''::cstring);
+ age_tolower
+-------------
+ ""
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+ RETURN tolower('')
+$$) AS (result agtype);
+ result
+--------
+ ""
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+ RETURN tolower("")
+$$) AS (result agtype);
+ result
+--------
+ ""
+(1 row)
+
-- should return null
SELECT * FROM cypher('expr', $$
RETURN toUpper(null)
@@ -4211,6 +4315,73 @@ SELECT * FROM age_trim(' string ');
"string"
(1 row)
+-- should return empty string
+SELECT * FROM cypher('expr', $$
+ RETURN lTrim('')
+$$) AS (results agtype);
+ results
+---------
+ ""
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+ RETURN rTrim('')
+$$) AS (results agtype);
+ results
+---------
+ ""
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+ RETURN trim('')
+$$) AS (results agtype);
+ results
+---------
+ ""
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+ RETURN lTrim("")
+$$) AS (results agtype);
+ results
+---------
+ ""
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+ RETURN rTrim("")
+$$) AS (results agtype);
+ results
+---------
+ ""
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+ RETURN trim("")
+$$) AS (results agtype);
+ results
+---------
+ ""
+(1 row)
+
+SELECT * FROM age_ltrim('');
+ age_ltrim
+-----------
+ ""
+(1 row)
+
+SELECT * FROM age_rtrim('');
+ age_rtrim
+-----------
+ ""
+(1 row)
+
+SELECT * FROM age_trim('');
+ age_trim
+----------
+ ""
+(1 row)
+
-- should return null
SELECT * FROM cypher('expr', $$
RETURN lTrim(null)
@@ -4322,15 +4493,16 @@ $$) AS (results agtype);
"123"
(1 row)
--- should return null
+-- should return empty string
SELECT * FROM cypher('expr', $$
RETURN left("123456789", 0)
$$) AS (results agtype);
results
---------
-
+ ""
(1 row)
+-- should return null
SELECT * FROM cypher('expr', $$
RETURN left(null, 1)
$$) AS (results agtype);
@@ -4401,15 +4573,16 @@ $$) AS (results agtype);
"789"
(1 row)
--- should return null
+-- should return empty string
SELECT * FROM cypher('expr', $$
RETURN right("123456789", 0)
$$) AS (results agtype);
results
---------
-
+ ""
(1 row)
+-- should return null
SELECT * FROM cypher('expr', $$
RETURN right(null, 1)
$$) AS (results agtype);
@@ -4508,6 +4681,13 @@ SELECT * FROM age_substring('0123456789', 1);
"123456789"
(1 row)
+-- should return empty string
+SELECT * FROM age_substring('0123456789', 0, 0);
+ age_substring
+---------------
+ ""
+(1 row)
+
-- should return null
SELECT * FROM cypher('expr', $$
RETURN substring(null, null, null)
@@ -4747,33 +4927,52 @@ $$) AS (results agtype);
"ababab"
(1 row)
--- should return null
+-- should return empty string
SELECT * FROM cypher('expr', $$
- RETURN replace(null, null, null)
+ RETURN replace("", "", "")
$$) AS (results agtype);
results
---------
-
+ ""
(1 row)
SELECT * FROM cypher('expr', $$
- RETURN replace("Hello", null, null)
+ RETURN replace("Hello", "Hello", "")
$$) AS (results agtype);
results
---------
-
+ ""
(1 row)
SELECT * FROM cypher('expr', $$
- RETURN replace("Hello", "", null)
+ RETURN replace("", "Hello", "Mellow")
$$) AS (results agtype);
results
---------
-
+ ""
+(1 row)
+
+SELECT * FROM age_replace('', '', '');
+ age_replace
+-------------
+ ""
(1 row)
+SELECT * FROM age_replace('Hello', 'Hello', '');
+ age_replace
+-------------
+ ""
+(1 row)
+
+SELECT * FROM age_replace('', 'Hello', 'Mellow');
+ age_replace
+-------------
+ ""
+(1 row)
+
+-- should return null
SELECT * FROM cypher('expr', $$
- RETURN replace("", "", "")
+ RETURN replace(null, null, null)
$$) AS (results agtype);
results
---------
@@ -4781,7 +4980,7 @@ $$) AS (results agtype);
(1 row)
SELECT * FROM cypher('expr', $$
- RETURN replace("Hello", "Hello", "")
+ RETURN replace("Hello", null, null)
$$) AS (results agtype);
results
---------
@@ -4789,7 +4988,7 @@ $$) AS (results agtype);
(1 row)
SELECT * FROM cypher('expr', $$
- RETURN replace("", "Hello", "Mellow")
+ RETURN replace("Hello", "", null)
$$) AS (results agtype);
results
---------
@@ -4814,24 +5013,6 @@ SELECT * FROM age_replace('Hello', '', null);
(1 row)
-SELECT * FROM age_replace('', '', '');
- age_replace
--------------
-
-(1 row)
-
-SELECT * FROM age_replace('Hello', 'Hello', '');
- age_replace
--------------
-
-(1 row)
-
-SELECT * FROM age_replace('', 'Hello', 'Mellow');
- age_replace
--------------
-
-(1 row)
-
-- should fail
SELECT * FROM cypher('expr', $$
RETURN replace()
diff --git a/regress/sql/expr.sql b/regress/sql/expr.sql
index a0cf1b02..16987b81 100644
--- a/regress/sql/expr.sql
+++ b/regress/sql/expr.sql
@@ -1669,6 +1669,16 @@ $$) AS (results agtype);
SELECT * FROM age_reverse('gnirts a si siht');
SELECT * FROM age_reverse('gnirts a si siht'::text);
SELECT * FROM age_reverse('gnirts a si siht'::cstring);
+-- should return empty string
+SELECT * FROM age_reverse('');
+SELECT * FROM age_reverse(''::text);
+SELECT * FROM age_reverse(''::cstring);
+SELECT * FROM cypher('expr', $$
+ RETURN reverse('')
+$$) AS (result agtype);
+SELECT * FROM cypher('expr', $$
+ RETURN reverse("")
+$$) AS (result agtype);
-- should return null
SELECT * FROM cypher('expr', $$
RETURN reverse(null)
@@ -1742,6 +1752,25 @@ SELECT * FROM age_toupper('text'::text);
SELECT * FROM age_toupper('cstring'::cstring);
SELECT * FROM age_tolower('TEXT'::text);
SELECT * FROM age_tolower('CSTRING'::cstring);
+-- should return empty string
+SELECT * FROM age_toupper('');
+SELECT * FROM age_toupper(''::text);
+SELECT * FROM age_toupper(''::cstring);
+SELECT * FROM cypher('expr', $$
+ RETURN toupper('')
+$$) AS (result agtype);
+SELECT * FROM cypher('expr', $$
+ RETURN toupper("")
+$$) AS (result agtype);
+SELECT * FROM age_tolower('');
+SELECT * FROM age_tolower(''::text);
+SELECT * FROM age_tolower(''::cstring);
+SELECT * FROM cypher('expr', $$
+ RETURN tolower('')
+$$) AS (result agtype);
+SELECT * FROM cypher('expr', $$
+ RETURN tolower("")
+$$) AS (result agtype);
-- should return null
SELECT * FROM cypher('expr', $$
RETURN toUpper(null)
@@ -1783,6 +1812,28 @@ $$) AS (results agtype);
SELECT * FROM age_ltrim(' string ');
SELECT * FROM age_rtrim(' string ');
SELECT * FROM age_trim(' string ');
+-- should return empty string
+SELECT * FROM cypher('expr', $$
+ RETURN lTrim('')
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+ RETURN rTrim('')
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+ RETURN trim('')
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+ RETURN lTrim("")
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+ RETURN rTrim("")
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+ RETURN trim("")
+$$) AS (results agtype);
+SELECT * FROM age_ltrim('');
+SELECT * FROM age_rtrim('');
+SELECT * FROM age_trim('');
-- should return null
SELECT * FROM cypher('expr', $$
RETURN lTrim(null)
@@ -1829,10 +1880,11 @@ $$) AS (results agtype);
SELECT * FROM cypher('expr', $$
RETURN left("123456789", 3)
$$) AS (results agtype);
--- should return null
+-- should return empty string
SELECT * FROM cypher('expr', $$
RETURN left("123456789", 0)
$$) AS (results agtype);
+-- should return null
SELECT * FROM cypher('expr', $$
RETURN left(null, 1)
$$) AS (results agtype);
@@ -1861,10 +1913,11 @@ $$) AS (results agtype);
SELECT * FROM cypher('expr', $$
RETURN right("123456789", 3)
$$) AS (results agtype);
--- should return null
+-- should return empty string
SELECT * FROM cypher('expr', $$
RETURN right("123456789", 0)
$$) AS (results agtype);
+-- should return null
SELECT * FROM cypher('expr', $$
RETURN right(null, 1)
$$) AS (results agtype);
@@ -1901,6 +1954,8 @@ SELECT * FROM cypher('expr', $$
$$) AS (results agtype);
SELECT * FROM age_substring('0123456789', 3, 2);
SELECT * FROM age_substring('0123456789', 1);
+-- should return empty string
+SELECT * FROM age_substring('0123456789', 0, 0);
-- should return null
SELECT * FROM cypher('expr', $$
RETURN substring(null, null, null)
@@ -2002,31 +2057,32 @@ $$) AS (results agtype);
SELECT * FROM cypher('expr', $$
RETURN replace("ababab", "ab", "ab")
$$) AS (results agtype);
--- should return null
+-- should return empty string
SELECT * FROM cypher('expr', $$
- RETURN replace(null, null, null)
+ RETURN replace("", "", "")
$$) AS (results agtype);
SELECT * FROM cypher('expr', $$
- RETURN replace("Hello", null, null)
+ RETURN replace("Hello", "Hello", "")
$$) AS (results agtype);
SELECT * FROM cypher('expr', $$
- RETURN replace("Hello", "", null)
+ RETURN replace("", "Hello", "Mellow")
$$) AS (results agtype);
+SELECT * FROM age_replace('', '', '');
+SELECT * FROM age_replace('Hello', 'Hello', '');
+SELECT * FROM age_replace('', 'Hello', 'Mellow');
+-- should return null
SELECT * FROM cypher('expr', $$
- RETURN replace("", "", "")
+ RETURN replace(null, null, null)
$$) AS (results agtype);
SELECT * FROM cypher('expr', $$
- RETURN replace("Hello", "Hello", "")
+ RETURN replace("Hello", null, null)
$$) AS (results agtype);
SELECT * FROM cypher('expr', $$
- RETURN replace("", "Hello", "Mellow")
+ RETURN replace("Hello", "", null)
$$) AS (results agtype);
SELECT * FROM age_replace(null, null, null);
SELECT * FROM age_replace('Hello', null, null);
SELECT * FROM age_replace('Hello', '', null);
-SELECT * FROM age_replace('', '', '');
-SELECT * FROM age_replace('Hello', 'Hello', '');
-SELECT * FROM age_replace('', 'Hello', 'Mellow');
-- should fail
SELECT * FROM cypher('expr', $$
RETURN replace()
diff --git a/src/backend/utils/adt/agtype.c b/src/backend/utils/adt/agtype.c
index d26929d3..17e08353 100644
--- a/src/backend/utils/adt/agtype.c
+++ b/src/backend/utils/adt/agtype.c
@@ -7691,12 +7691,6 @@ Datum age_reverse(PG_FUNCTION_ARGS)
string = text_to_cstring(text_string);
string_len = strlen(string);
- /* if we have an empty string, return null */
- if (string_len == 0)
- {
- PG_RETURN_NULL();
- }
-
/* build the result */
agtv_result.type = AGTV_STRING;
agtv_result.val.string.val = string;
@@ -7776,10 +7770,6 @@ Datum age_toupper(PG_FUNCTION_ARGS)
agtv_value->type)));
}
- /* if we have an empty string, return null */
- if (string_len == 0)
- PG_RETURN_NULL();
-
/* allocate the new string */
result = palloc0(string_len);
@@ -7866,10 +7856,6 @@ Datum age_tolower(PG_FUNCTION_ARGS)
agtv_value->type)));
}
- /* if we have an empty string, return null */
- if (string_len == 0)
- PG_RETURN_NULL();
-
/* allocate the new string */
result = palloc0(string_len);
@@ -7964,10 +7950,6 @@ Datum age_rtrim(PG_FUNCTION_ARGS)
string = text_to_cstring(text_string);
string_len = strlen(string);
- /* if we have an empty string, return null */
- if (string_len == 0)
- PG_RETURN_NULL();
-
/* build the result */
agtv_result.type = AGTV_STRING;
agtv_result.val.string.val = string;
@@ -8055,10 +8037,6 @@ Datum age_ltrim(PG_FUNCTION_ARGS)
string = text_to_cstring(text_string);
string_len = strlen(string);
- /* if we have an empty string, return null */
- if (string_len == 0)
- PG_RETURN_NULL();
-
/* build the result */
agtv_result.type = AGTV_STRING;
agtv_result.val.string.val = string;
@@ -8146,10 +8124,6 @@ Datum age_trim(PG_FUNCTION_ARGS)
string = text_to_cstring(text_string);
string_len = strlen(string);
- /* if we have an empty string, return null */
- if (string_len == 0)
- PG_RETURN_NULL();
-
/* build the result */
agtv_result.type = AGTV_STRING;
agtv_result.val.string.val = string;
@@ -8287,10 +8261,6 @@ Datum age_right(PG_FUNCTION_ARGS)
string = text_to_cstring(text_string);
string_len = strlen(string);
- /* if we have an empty string, return null */
- if (string_len == 0)
- PG_RETURN_NULL();
-
/* build the result */
agtv_result.type = AGTV_STRING;
agtv_result.val.string.val = string;
@@ -8428,10 +8398,6 @@ Datum age_left(PG_FUNCTION_ARGS)
string = text_to_cstring(text_string);
string_len = strlen(string);
- /* if we have an empty string, return null */
- if (string_len == 0)
- PG_RETURN_NULL();
-
/* build the result */
agtv_result.type = AGTV_STRING;
agtv_result.val.string.val = string;
@@ -8597,10 +8563,6 @@ Datum age_substring(PG_FUNCTION_ARGS)
string = text_to_cstring(text_string);
string_len = strlen(string);
- /* if we have an empty string, return null */
- if (string_len == 0)
- PG_RETURN_NULL();
-
/* build the result */
agtv_result.type = AGTV_STRING;
agtv_result.val.string.val = string;
@@ -8855,10 +8817,6 @@ Datum age_replace(PG_FUNCTION_ARGS)
string = text_to_cstring(text_result);
string_len = strlen(string);
- /* if we have an empty string, return null */
- if (string_len == 0)
- PG_RETURN_NULL();
-
/* build the result */
agtv_result.type = AGTV_STRING;
agtv_result.val.string.val = string;