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);