This is an automated email from the ASF dual-hosted git repository.

jgemignani 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 87f86015 Typecast bool to integer and vice versa (#905)
87f86015 is described below

commit 87f860151fc17f7d124ddf3f409f2b24acd928de
Author: Zainab Saad <[email protected]>
AuthorDate: Wed May 10 22:33:42 2023 +0500

    Typecast bool to integer and vice versa (#905)
    
    * Typecast boolean to integer
    
    - Modify agtype_typecast_int and agtype_to_int4 in agtype.c
    
    * Add regression tests for typecast boolean to integer
    
    - Add regression tests: sql script, expected results in agtype.sql, 
agtype.out
    - Add regression tests: sql script, expected results in expr.sql, expr.out
    
    * Typecast integer to boolean
    
    - Modify transform_cypher_typecast in cypher_expr.c
    - Modify agtype_to_bool in agtype.c
    
    * Add regression tests for typecast integer to boolean
    
    - Add/Modify regression tests in agtype.sql, agtype.out
    - Add/Modify regression tests in expr.sql, expr.out
    
    * Add regression tests for edge cases
    
    - Add regression tests in expr.sql, expr.out
    - These regression tests check the edge cases for typecasting
    
    * Add regression tests for edge cases - II
    
    - Add regression tests in agtype.sql, agtype.out
    - Add edge cases for agtype_to_int4
    
    * Fix & Add regression tests
    
    - Fix comments in agtype.sql, agtype.out
    - Add regression test for edge case
---
 regress/expected/agtype.out      | 57 ++++++++++++++++++++++++++++++++--
 regress/expected/expr.out        | 67 ++++++++++++++++++++++++++++++++++++++++
 regress/sql/agtype.sql           | 20 +++++++++++-
 regress/sql/expr.sql             | 39 +++++++++++++++++++++++
 src/backend/parser/cypher_expr.c |  6 ++++
 src/backend/utils/adt/agtype.c   | 36 +++++++++++++++++++--
 6 files changed, 220 insertions(+), 5 deletions(-)

diff --git a/regress/expected/agtype.out b/regress/expected/agtype.out
index 03e1a656..236e610b 100644
--- a/regress/expected/agtype.out
+++ b/regress/expected/agtype.out
@@ -2077,11 +2077,15 @@ SELECT agtype_to_bool(agtype_in('false'));
  f
 (1 row)
 
+SELECT agtype_to_bool(agtype_in('1'));
+ agtype_to_bool 
+----------------
+ t
+(1 row)
+
 -- These should all fail
 SELECT agtype_to_bool(agtype_in('null'));
 ERROR:  cannot cast agtype null to type boolean
-SELECT agtype_to_bool(agtype_in('1'));
-ERROR:  cannot cast agtype integer to type boolean
 SELECT agtype_to_bool(agtype_in('1.0'));
 ERROR:  cannot cast agtype float to type boolean
 SELECT agtype_to_bool(agtype_in('"string"'));
@@ -2138,6 +2142,55 @@ SELECT agtype_to_int8(agtype_in('false'));
               0
 (1 row)
 
+--
+-- Test boolean to integer cast
+--
+SELECT agtype_to_int4(agtype_in('true'));
+ agtype_to_int4 
+----------------
+              1
+(1 row)
+
+SELECT agtype_to_int4(agtype_in('false'));
+ agtype_to_int4 
+----------------
+              0
+(1 row)
+
+SELECT agtype_to_int4(agtype_in('null'));
+ agtype_to_int4 
+----------------
+               
+(1 row)
+
+--
+-- Test agtype to integer cast
+--
+SELECT agtype_to_int4(agtype_in('1'));
+ agtype_to_int4 
+----------------
+              1
+(1 row)
+
+SELECT agtype_to_int4(agtype_in('1.45'));
+ agtype_to_int4 
+----------------
+              1
+(1 row)
+
+SELECT agtype_to_int4(agtype_in('1.444::numeric'));
+ agtype_to_int4 
+----------------
+              1
+(1 row)
+
+-- These should all fail
+SELECT agtype_to_int4(agtype_in('"string"'));
+ERROR:  invalid input syntax for integer: "string"
+SELECT agtype_to_int4(agtype_in('[1, 2, 3]'));
+ERROR:  cannot cast agtype array to type int
+SELECT agtype_to_int4(agtype_in('{"int":1}'));
+ERROR:  cannot cast agtype object to type int
 --
 -- Test agtype to int[]
 --
diff --git a/regress/expected/expr.out b/regress/expected/expr.out
index 645ab96a..8ba6e391 100644
--- a/regress/expected/expr.out
+++ b/regress/expected/expr.out
@@ -1147,6 +1147,25 @@ $$) AS (i bigint);
  1
 (1 row)
 
+SELECT * FROM cypher('type_coercion', $$
+       RETURN true
+$$) AS (i int);
+ i 
+---
+ 1
+(1 row)
+
+--
+-- Coerce to Postgres bool/boolean type
+--
+SELECT * FROM cypher('type_coercion', $$
+    RETURN 1
+$$) AS (i bool);
+ i 
+---
+ t
+(1 row)
+
 --Invalid String Format
 SELECT * FROM cypher('type_coercion', $$
        RETURN '1.0'
@@ -1252,6 +1271,22 @@ $$) AS r(result agtype);
  3
 (1 row)
 
+SELECT * FROM cypher('expr', $$
+RETURN true::int
+$$) AS r(result agtype);
+ result 
+--------
+ 1
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+RETURN false::int
+$$) AS r(result agtype);
+ result 
+--------
+ 0
+(1 row)
+
 SELECT * FROM cypher('expr', $$
 RETURN ([0, {one: 1.0, pie: 3.1415927, e: 2::numeric}, 2, null][1].one)::int
 $$) AS r(result agtype);
@@ -1335,6 +1370,38 @@ SELECT * FROM cypher('expr', $$
 RETURN 'infinity'::float::int
 $$) AS r(result agtype);
 ERROR:  bigint out of range
+SELECT * FROM cypher('expr', $$
+RETURN ''::int
+$$) AS r(result agtype);
+ERROR:  invalid input syntax for integer: ""
+SELECT * FROM cypher('expr', $$
+RETURN 'falze'::int
+$$) AS r(result agtype);
+ERROR:  invalid input syntax for integer: "falze"
+--
+-- Test from an agtype value to agtype int
+--
+SELECT * FROM cypher('expr', $$
+RETURN 0::bool
+$$) AS r(result agtype);
+ result 
+--------
+ false
+(1 row)
+
+-- these should fail
+SELECT * FROM cypher('expr', $$
+RETURN 1.23::bool
+$$) AS r(result agtype);
+ERROR:  cannot cast agtype float to type boolean
+SELECT * FROM cypher('expr', $$
+RETURN ''::bool
+$$) AS r(result agtype);
+ERROR:  cannot cast agtype string to type boolean
+SELECT * FROM cypher('expr', $$
+RETURN 'falze'::bool
+$$) AS r(result agtype);
+ERROR:  cannot cast agtype string to type boolean
 -- Test from an agtype value to an agtype numeric
 --
 SELECT * FROM cypher('expr', $$
diff --git a/regress/sql/agtype.sql b/regress/sql/agtype.sql
index 6aa03932..16e14f65 100644
--- a/regress/sql/agtype.sql
+++ b/regress/sql/agtype.sql
@@ -523,9 +523,9 @@ SELECT agtype_in('true') < '1::numeric';
 --
 SELECT agtype_to_bool(agtype_in('true'));
 SELECT agtype_to_bool(agtype_in('false'));
+SELECT agtype_to_bool(agtype_in('1'));
 -- These should all fail
 SELECT agtype_to_bool(agtype_in('null'));
-SELECT agtype_to_bool(agtype_in('1'));
 SELECT agtype_to_bool(agtype_in('1.0'));
 SELECT agtype_to_bool(agtype_in('"string"'));
 SELECT agtype_to_bool(agtype_in('[1,2,3]'));
@@ -546,6 +546,24 @@ SELECT bool_to_agtype(true) <> bool_to_agtype(false);
 SELECT agtype_to_int8(agtype_in('true'));
 SELECT agtype_to_int8(agtype_in('false'));
 
+--
+-- Test boolean to integer cast
+--
+SELECT agtype_to_int4(agtype_in('true'));
+SELECT agtype_to_int4(agtype_in('false'));
+SELECT agtype_to_int4(agtype_in('null'));
+
+--
+-- Test agtype to integer cast
+--
+SELECT agtype_to_int4(agtype_in('1'));
+SELECT agtype_to_int4(agtype_in('1.45'));
+SELECT agtype_to_int4(agtype_in('1.444::numeric'));
+-- These should all fail
+SELECT agtype_to_int4(agtype_in('"string"'));
+SELECT agtype_to_int4(agtype_in('[1, 2, 3]'));
+SELECT agtype_to_int4(agtype_in('{"int":1}'));
+
 --
 -- Test agtype to int[]
 --
diff --git a/regress/sql/expr.sql b/regress/sql/expr.sql
index da116708..f1dc84ee 100644
--- a/regress/sql/expr.sql
+++ b/regress/sql/expr.sql
@@ -518,6 +518,17 @@ SELECT * FROM cypher('type_coercion', $$
        RETURN true
 $$) AS (i bigint);
 
+SELECT * FROM cypher('type_coercion', $$
+       RETURN true
+$$) AS (i int);
+
+--
+-- Coerce to Postgres bool/boolean type
+--
+SELECT * FROM cypher('type_coercion', $$
+    RETURN 1
+$$) AS (i bool);
+
 --Invalid String Format
 SELECT * FROM cypher('type_coercion', $$
        RETURN '1.0'
@@ -585,6 +596,12 @@ SELECT * FROM cypher('expr', $$
 RETURN 2.71::numeric::int
 $$) AS r(result agtype);
 SELECT * FROM cypher('expr', $$
+RETURN true::int
+$$) AS r(result agtype);
+SELECT * FROM cypher('expr', $$
+RETURN false::int
+$$) AS r(result agtype);
+SELECT * FROM cypher('expr', $$
 RETURN ([0, {one: 1.0, pie: 3.1415927, e: 2::numeric}, 2, null][1].one)::int
 $$) AS r(result agtype);
 SELECT * FROM cypher('expr', $$
@@ -620,7 +637,29 @@ $$) AS r(result agtype);
 SELECT * FROM cypher('expr', $$
 RETURN 'infinity'::float::int
 $$) AS r(result agtype);
+SELECT * FROM cypher('expr', $$
+RETURN ''::int
+$$) AS r(result agtype);
+SELECT * FROM cypher('expr', $$
+RETURN 'falze'::int
+$$) AS r(result agtype);
+--
+-- Test from an agtype value to agtype int
+--
+SELECT * FROM cypher('expr', $$
+RETURN 0::bool
+$$) AS r(result agtype);
 
+-- these should fail
+SELECT * FROM cypher('expr', $$
+RETURN 1.23::bool
+$$) AS r(result agtype);
+SELECT * FROM cypher('expr', $$
+RETURN ''::bool
+$$) AS r(result agtype);
+SELECT * FROM cypher('expr', $$
+RETURN 'falze'::bool
+$$) AS r(result agtype);
 -- Test from an agtype value to an agtype numeric
 --
 SELECT * FROM cypher('expr', $$
diff --git a/src/backend/parser/cypher_expr.c b/src/backend/parser/cypher_expr.c
index 50436d75..dcda2251 100644
--- a/src/backend/parser/cypher_expr.c
+++ b/src/backend/parser/cypher_expr.c
@@ -62,6 +62,7 @@
 #define FUNC_AGTYPE_TYPECAST_INT "agtype_typecast_int"
 #define FUNC_AGTYPE_TYPECAST_PG_FLOAT8 "agtype_to_float8"
 #define FUNC_AGTYPE_TYPECAST_PG_BIGINT "agtype_to_int8"
+#define FUNC_AGTYPE_TYPECAST_BOOL "agtype_to_bool"
 
 static Node *transform_cypher_expr_recurse(cypher_parsestate *cpstate,
                                            Node *expr);
@@ -955,6 +956,11 @@ static Node *transform_cypher_typecast(cypher_parsestate 
*cpstate,
     {
         fname = lappend(fname, makeString(FUNC_AGTYPE_TYPECAST_PG_BIGINT));
     }
+    else if ((pg_strcasecmp(ctypecast->typecast, "bool") == 0 || 
+             pg_strcasecmp(ctypecast->typecast, "boolean") == 0))
+    {
+        fname = lappend(fname, makeString(FUNC_AGTYPE_TYPECAST_BOOL));
+    }
     /* if none was found, error out */
     else
     {
diff --git a/src/backend/utils/adt/agtype.c b/src/backend/utils/adt/agtype.c
index d00acc3f..3a5d8ef9 100644
--- a/src/backend/utils/adt/agtype.c
+++ b/src/backend/utils/adt/agtype.c
@@ -2575,8 +2575,10 @@ Datum agtype_to_bool(PG_FUNCTION_ARGS)
     agtype_value agtv;
 
     if (!agtype_extract_scalar(&agtype_in->root, &agtv) ||
-        agtv.type != AGTV_BOOL)
+        (agtv.type != AGTV_BOOL && agtv.type != AGTV_INTEGER))
+    {
         cannot_cast_agtype_value(agtv.type, "boolean");
+    }
 
     PG_FREE_IF_COPY(agtype_in, 0);
 
@@ -2648,31 +2650,51 @@ Datum agtype_to_int4(PG_FUNCTION_ARGS)
 
     /* Return null if arg_agt is null. This covers SQL and Agtype NULLS */
     if (arg_agt == NULL)
+    {    
         PG_RETURN_NULL();
+    }
 
     if (!agtype_extract_scalar(&arg_agt->root, &agtv) ||
         (agtv.type != AGTV_FLOAT &&
          agtv.type != AGTV_INTEGER &&
          agtv.type != AGTV_NUMERIC &&
-         agtv.type != AGTV_STRING))
+         agtv.type != AGTV_STRING &&
+         agtv.type != AGTV_BOOL))
+    {    
         cannot_cast_agtype_value(agtv.type, "int");
+    }
 
     PG_FREE_IF_COPY(agtype_in, 0);
 
     if (agtv.type == AGTV_INTEGER)
+    {    
         result = DatumGetInt32(DirectFunctionCall1(int84,
                     Int64GetDatum(agtv.val.int_value)));
+    }
     else if (agtv.type == AGTV_FLOAT)
+    {    
         result = DatumGetInt32(DirectFunctionCall1(dtoi4,
                                 Float8GetDatum(agtv.val.float_value)));
+    }
     else if (agtv.type == AGTV_NUMERIC)
+    {    
         result = DatumGetInt32(DirectFunctionCall1(numeric_int4,
                      NumericGetDatum(agtv.val.numeric)));
+    }
     else if (agtv.type == AGTV_STRING)
+    {    
         result = DatumGetInt32(DirectFunctionCall1(int4in,
                            CStringGetDatum(agtv.val.string.val)));
+    }
+    else if (agtv.type == AGTV_BOOL)
+    {    
+        result = DatumGetInt64(DirectFunctionCall1(bool_int4, 
+                    BoolGetDatum(agtv.val.boolean)));
+    }
     else
+    {    
         elog(ERROR, "invalid agtype type: %d", (int)agtv.type);
+    }
 
     PG_RETURN_INT32(result);
 }
@@ -4003,20 +4025,26 @@ Datum agtype_typecast_int(PG_FUNCTION_ARGS)
 
     /* Return null if arg_agt is null. This covers SQL and Agtype NULLS */
     if (arg_agt == NULL)
+    {    
         PG_RETURN_NULL();
+    }
 
     /* check that we have a scalar value */
     if (!AGT_ROOT_IS_SCALAR(arg_agt))
+    {    
         ereport(ERROR,
                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                  errmsg("typecast argument must be a scalar value")));
+    }
 
     /* get the arg parameter */
     arg_value = get_ith_agtype_value_from_container(&arg_agt->root, 0);
 
     /* check for agtype null */
     if (arg_value->type == AGTV_NULL)
+    {    
         PG_RETURN_NULL();
+    }
 
     /* the input type drives the casting */
     switch(arg_value->type)
@@ -4032,6 +4060,10 @@ Datum agtype_typecast_int(PG_FUNCTION_ARGS)
         d = DirectFunctionCall1(numeric_int8,
                                 NumericGetDatum(arg_value->val.numeric));
         break;
+    case AGTV_BOOL:
+        d = DirectFunctionCall1(bool_int4, 
+                                BoolGetDatum(arg_value->val.boolean));
+        break;
     case AGTV_STRING:
         /* we need a null terminated string */
         string = (char *) palloc0(sizeof(char)*arg_value->val.string.len + 1);

Reply via email to