From 224774ff2fbe8449ce8590d5b936aab7fd052fe3 Mon Sep 17 00:00:00 2001 From: Muhammad Taha Naveed Date: Wed, 13 Aug 2025 19:51:08 +0500 Subject: [PATCH 1/3] Add missing dependency in jdbc driver (#2206) - This was also failing CI. The issue is described here https://github.com/gradle/gradle/issues/33950 --- drivers/jdbc/lib/build.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/jdbc/lib/build.gradle.kts b/drivers/jdbc/lib/build.gradle.kts index 2ba529ec1..0b63bc5a6 100644 --- a/drivers/jdbc/lib/build.gradle.kts +++ b/drivers/jdbc/lib/build.gradle.kts @@ -36,6 +36,7 @@ dependencies { testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.3") testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine") + testRuntimeOnly("org.junit.platform:junit-platform-launcher") testImplementation("org.testcontainers:testcontainers:1.18.0") testImplementation("org.postgresql:postgresql:42.6.0") From 29bd979205edc165bc1e91dcfc0c364806d9710a Mon Sep 17 00:00:00 2001 From: John Gemignani Date: Wed, 13 Aug 2025 08:13:29 -0700 Subject: [PATCH 2/3] 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 76eecbe0c..980172da1 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 a0cf1b024..16987b817 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 d26929d33..17e083534 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; From d2ceae3f6215c62ea389e7bd372ab1a8bd3c7428 Mon Sep 17 00:00:00 2001 From: John Gemignani Date: Mon, 18 Aug 2025 07:50:17 -0700 Subject: [PATCH 3/3] 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 980172da1..513ea142f 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 16987b817..83f21856e 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 17e083534..be838cbd0 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);