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

dehowef 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 52bfe9b0 Remove implicit casting from int to bool (#923)
52bfe9b0 is described below

commit 52bfe9b026528872c5ddc3c5581f15f3fd14ff62
Author: Zainab Saad <[email protected]>
AuthorDate: Tue May 16 04:40:02 2023 +0500

    Remove implicit casting from int to bool (#923)
    
    - Fixed&Added code to deal an edge case where integers could be
      used as operands for logical operations due to implicit casting
    - Fixed&Added relevant regression tests
---
 age--1.2.0.sql                   |  7 +++
 regress/expected/agtype.out      |  8 +---
 regress/expected/expr.out        | 94 ++++++++++++++++++++++++++++++++++------
 regress/sql/agtype.sql           |  2 +-
 regress/sql/expr.sql             | 71 +++++++++++++++++++++++++++---
 src/backend/parser/cypher_expr.c |  2 +-
 src/backend/utils/adt/agtype.c   | 64 ++++++++++++++++++++++++++-
 7 files changed, 218 insertions(+), 30 deletions(-)

diff --git a/age--1.2.0.sql b/age--1.2.0.sql
index 72e96e97..6d8730f9 100644
--- a/age--1.2.0.sql
+++ b/age--1.2.0.sql
@@ -4039,6 +4039,13 @@ IMMUTABLE
 PARALLEL SAFE
 AS 'MODULE_PATHNAME';
 
+CREATE FUNCTION ag_catalog.agtype_typecast_bool(variadic "any")
+RETURNS agtype
+LANGUAGE c
+IMMUTABLE
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
 CREATE FUNCTION ag_catalog.agtype_typecast_vertex(variadic "any")
 RETURNS agtype
 LANGUAGE c
diff --git a/regress/expected/agtype.out b/regress/expected/agtype.out
index 236e610b..b368141a 100644
--- a/regress/expected/agtype.out
+++ b/regress/expected/agtype.out
@@ -2077,13 +2077,9 @@ 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('1'));
+ERROR:  cannot cast agtype integer to type boolean
 SELECT agtype_to_bool(agtype_in('null'));
 ERROR:  cannot cast agtype null to type boolean
 SELECT agtype_to_bool(agtype_in('1.0'));
diff --git a/regress/expected/expr.out b/regress/expected/expr.out
index 9d0f9e06..91bc70fe 100644
--- a/regress/expected/expr.out
+++ b/regress/expected/expr.out
@@ -898,6 +898,79 @@ $$) AS r(result boolean);
  f
 (1 row)
 
+SELECT * FROM cypher('expr', $$
+RETURN false OR 1::bool    
+$$) AS (result boolean);
+ result 
+--------
+ t
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+RETURN false AND NOT 1::bool
+$$) AS (result boolean);
+ result 
+--------
+ f
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+RETURN NOT 1::bool::int::bool    
+$$) AS (result boolean);
+ result 
+--------
+ f
+(1 row)
+
+-- Invalid operands for AND, OR, NOT, XOR
+SELECT * FROM cypher('expr', $$
+RETURN 1 AND true
+$$) AS r(result boolean);
+ERROR:  cannot cast agtype integer to type boolean
+SELECT * FROM cypher('expr', $$
+RETURN true AND 0
+$$) AS r(result boolean);
+ERROR:  cannot cast agtype integer to type boolean
+SELECT * FROM cypher('expr', $$
+RETURN false OR 1
+$$) AS r(result boolean);
+ERROR:  cannot cast agtype integer to type boolean
+SELECT * FROM cypher('expr', $$
+RETURN 0 OR true 
+$$) AS r(result boolean);
+ERROR:  cannot cast agtype integer to type boolean
+SELECT * FROM cypher('expr', $$
+RETURN NOT 1    
+$$) AS r(result boolean);
+ERROR:  cannot cast agtype integer to type boolean
+SELECT * FROM cypher('expr', $$
+RETURN true XOR 0
+$$) AS r(result boolean);
+ERROR:  cannot cast agtype integer to type boolean
+SELECT * FROM cypher('expr', $$
+RETURN 1 XOR 0
+$$) AS r(result boolean);
+ERROR:  cannot cast agtype integer to type boolean
+SELECT * FROM cypher('expr', $$
+RETURN NOT ((1 OR 0) AND (0 OR 1))
+$$) AS r(result boolean);
+ERROR:  cannot cast agtype integer to type boolean
+SELECT * FROM cypher('expr', $$
+RETURN 1.0 AND true
+$$) AS (result agtype);
+ERROR:  cannot cast agtype float to type boolean
+SELECT * FROM cypher('expr', $$
+RETURN false OR 'string'
+$$) AS (result agtype);
+ERROR:  cannot cast agtype string to type boolean
+SELECT * FROM cypher('expr', $$
+RETURN false XOR 1::numeric
+$$) AS (result agtype);
+ERROR:  cannot cast agtype numeric to type boolean
+SELECT * FROM cypher('expr', $$
+RETURN false OR 1::bool::int    
+$$) AS (result boolean);
+ERROR:  cannot cast agtype integer to type boolean
 --
 -- Test indirection transform logic for object.property, object["property"],
 -- and array[element]
@@ -1155,17 +1228,6 @@ $$) AS (i int);
  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'
@@ -1189,6 +1251,10 @@ SELECT * FROM cypher('type_coercion', $$
        RETURN [1]
 $$) AS (i bigint);
 ERROR:  cannot cast agtype array to type int
+SELECT * FROM cypher('type_coercion', $$
+    RETURN 1
+$$) AS (i bool);
+ERROR:  cannot cast agtype integer to type boolean
 SELECT * FROM cypher('type_coercion', $$CREATE ()-[:edge]->()$$) AS (result 
agtype);
  result 
 --------
@@ -1393,15 +1459,15 @@ $$) AS r(result agtype);
 SELECT * FROM cypher('expr', $$
 RETURN 1.23::bool
 $$) AS r(result agtype);
-ERROR:  cannot cast agtype float to type boolean
+ERROR:  typecast expression must be an integer or a boolean
 SELECT * FROM cypher('expr', $$
 RETURN ''::bool
 $$) AS r(result agtype);
-ERROR:  cannot cast agtype string to type boolean
+ERROR:  typecast expression must be an integer or a boolean
 SELECT * FROM cypher('expr', $$
 RETURN 'falze'::bool
 $$) AS r(result agtype);
-ERROR:  cannot cast agtype string to type boolean
+ERROR:  typecast expression must be an integer or a 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 16e14f65..d772cde1 100644
--- a/regress/sql/agtype.sql
+++ b/regress/sql/agtype.sql
@@ -523,8 +523,8 @@ 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('1'));
 SELECT agtype_to_bool(agtype_in('null'));
 SELECT agtype_to_bool(agtype_in('1.0'));
 SELECT agtype_to_bool(agtype_in('"string"'));
diff --git a/regress/sql/expr.sql b/regress/sql/expr.sql
index 0559b0da..456f38c8 100644
--- a/regress/sql/expr.sql
+++ b/regress/sql/expr.sql
@@ -393,6 +393,66 @@ SELECT * FROM cypher('expr', $$
 RETURN false XOR false
 $$) AS r(result boolean);
 
+SELECT * FROM cypher('expr', $$
+RETURN false OR 1::bool    
+$$) AS (result boolean);
+
+SELECT * FROM cypher('expr', $$
+RETURN false AND NOT 1::bool
+$$) AS (result boolean);
+
+SELECT * FROM cypher('expr', $$
+RETURN NOT 1::bool::int::bool    
+$$) AS (result boolean);
+
+-- Invalid operands for AND, OR, NOT, XOR
+SELECT * FROM cypher('expr', $$
+RETURN 1 AND true
+$$) AS r(result boolean);
+
+SELECT * FROM cypher('expr', $$
+RETURN true AND 0
+$$) AS r(result boolean);
+
+SELECT * FROM cypher('expr', $$
+RETURN false OR 1
+$$) AS r(result boolean);
+
+SELECT * FROM cypher('expr', $$
+RETURN 0 OR true 
+$$) AS r(result boolean);
+
+SELECT * FROM cypher('expr', $$
+RETURN NOT 1    
+$$) AS r(result boolean);
+
+SELECT * FROM cypher('expr', $$
+RETURN true XOR 0
+$$) AS r(result boolean);
+
+SELECT * FROM cypher('expr', $$
+RETURN 1 XOR 0
+$$) AS r(result boolean);
+
+SELECT * FROM cypher('expr', $$
+RETURN NOT ((1 OR 0) AND (0 OR 1))
+$$) AS r(result boolean);
+
+SELECT * FROM cypher('expr', $$
+RETURN 1.0 AND true
+$$) AS (result agtype);
+
+SELECT * FROM cypher('expr', $$
+RETURN false OR 'string'
+$$) AS (result agtype);
+
+SELECT * FROM cypher('expr', $$
+RETURN false XOR 1::numeric
+$$) AS (result agtype);
+
+SELECT * FROM cypher('expr', $$
+RETURN false OR 1::bool::int    
+$$) AS (result boolean);
 --
 -- Test indirection transform logic for object.property, object["property"],
 -- and array[element]
@@ -522,13 +582,6 @@ 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'
@@ -553,6 +606,10 @@ SELECT * FROM cypher('type_coercion', $$
        RETURN [1]
 $$) AS (i bigint);
 
+SELECT * FROM cypher('type_coercion', $$
+    RETURN 1
+$$) AS (i bool);
+
 SELECT * FROM cypher('type_coercion', $$CREATE ()-[:edge]->()$$) AS (result 
agtype);
 SELECT * FROM cypher('type_coercion', $$
        MATCH (v)
diff --git a/src/backend/parser/cypher_expr.c b/src/backend/parser/cypher_expr.c
index dcda2251..afb695a3 100644
--- a/src/backend/parser/cypher_expr.c
+++ b/src/backend/parser/cypher_expr.c
@@ -62,7 +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"
+#define FUNC_AGTYPE_TYPECAST_BOOL "agtype_typecast_bool"
 
 static Node *transform_cypher_expr_recurse(cypher_parsestate *cpstate,
                                            Node *expr);
diff --git a/src/backend/utils/adt/agtype.c b/src/backend/utils/adt/agtype.c
index 9f234fd2..898f3d8a 100644
--- a/src/backend/utils/adt/agtype.c
+++ b/src/backend/utils/adt/agtype.c
@@ -2575,7 +2575,7 @@ 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_INTEGER))
+        agtv.type != AGTV_BOOL)
     {
         cannot_cast_agtype_value(agtv.type, "boolean");
     }
@@ -4105,6 +4105,68 @@ Datum agtype_typecast_int(PG_FUNCTION_ARGS)
     PG_RETURN_POINTER(agtype_value_to_agtype(&result_value));
 }
 
+PG_FUNCTION_INFO_V1(agtype_typecast_bool);
+/*
+ * Execute function to typecast an agtype to an agtype bool
+ */
+Datum agtype_typecast_bool(PG_FUNCTION_ARGS)
+{
+    agtype *arg_agt;
+    agtype_value *arg_value;
+    agtype_value result_value;
+    Datum d;
+
+    /* get the agtype equivalence of any convertable input type */
+    arg_agt = get_one_agtype_from_variadic_args(fcinfo, 0, 1);
+
+    /* 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)
+    {
+    case AGTV_BOOL:
+        PG_RETURN_POINTER(agtype_value_to_agtype(arg_value));
+        break;
+    case AGTV_INTEGER:
+        d = DirectFunctionCall1(int4_bool,
+                                Int64GetDatum(arg_value->val.int_value));
+        break;
+    /* what was given doesn't cast to a bool */
+    default:
+        ereport(ERROR,
+                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                 errmsg("typecast expression must be an integer or a 
boolean")));
+        break;
+    }
+
+    /* set the result type and return our result */
+    result_value.type = AGTV_BOOL;
+    result_value.val.boolean = DatumGetBool(d);
+
+    PG_RETURN_POINTER(agtype_value_to_agtype(&result_value));
+}
+
 PG_FUNCTION_INFO_V1(agtype_typecast_float);
 /*
  * Execute function to typecast an agtype to an agtype float

Reply via email to