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