This is an automated email from the ASF dual-hosted git repository.
rafsun42 pushed a commit to branch PG13
in repository https://gitbox.apache.org/repos/asf/age.git
The following commit(s) were added to refs/heads/PG13 by this push:
new d6679ed8 Fix Issue 1329 - agtype_to_int4 crash (#1339) (#1343)
d6679ed8 is described below
commit d6679ed8ec3daadaa30967089788baf582defce9
Author: John Gemignani <[email protected]>
AuthorDate: Fri Nov 3 14:30:23 2023 -0700
Fix Issue 1329 - agtype_to_int4 crash (#1339) (#1343)
Fixed issue 1329 where `agtype_to_int`<8,4,2> and `agtype_to_int4_array`
crashed due to not properly checking input.
As these functions take "any" input, the input has to be properly
checked before casting it to a specific type. The input section
assumed it was agtype, which caused crashes for non-agtypes.
The functions `agtype_to_int`<8,4,2> will convert non-agtypes into
agtype. However, there were no regression tests for this.
The functions `agtype_to_int`<8,4,2> will convert non-agtypes to
agtype ints but, did not for their string equivs. Meaning, passing
a ('true') or ('3.14') would fail but, passing a (true) or (3.14)
would not. This has been corrected for all 3 functions.
TODO -
The function `agtype_to_int4_array` only takes agtype, currently,
and we should consider allowing it to take "any" types.
Added regression tests.
Added missing regression tests.
---
regress/expected/agtype.out | 324 ++++++++++++++++++++++++++++++++++++++++-
regress/expected/expr.out | 59 +++++++-
regress/sql/agtype.sql | 82 ++++++++++-
regress/sql/expr.sql | 15 +-
src/backend/utils/adt/agtype.c | 308 ++++++++++++++++++++++++++++++++-------
5 files changed, 721 insertions(+), 67 deletions(-)
diff --git a/regress/expected/agtype.out b/regress/expected/agtype.out
index c418b69d..cb4063b0 100644
--- a/regress/expected/agtype.out
+++ b/regress/expected/agtype.out
@@ -2217,6 +2217,91 @@ SELECT agtype_to_int8(agtype_in('false'));
0
(1 row)
+-- should return SQL NULL
+SELECT agtype_to_int8(agtype_in('null'));
+ agtype_to_int8
+----------------
+
+(1 row)
+
+SELECT agtype_to_int8(NULL);
+ agtype_to_int8
+----------------
+
+(1 row)
+
+-- non agtype input
+SELECT agtype_to_int8(1);
+ agtype_to_int8
+----------------
+ 1
+(1 row)
+
+SELECT agtype_to_int8(3.14);
+ agtype_to_int8
+----------------
+ 3
+(1 row)
+
+SELECT agtype_to_int8(3.14::numeric);
+ agtype_to_int8
+----------------
+ 3
+(1 row)
+
+SELECT agtype_to_int8('3');
+ agtype_to_int8
+----------------
+ 3
+(1 row)
+
+SELECT agtype_to_int8(true);
+ agtype_to_int8
+----------------
+ 1
+(1 row)
+
+SELECT agtype_to_int8(false);
+ agtype_to_int8
+----------------
+ 0
+(1 row)
+
+SELECT agtype_to_int8('3.14');
+ agtype_to_int8
+----------------
+ 3
+(1 row)
+
+SELECT agtype_to_int8('true');
+ agtype_to_int8
+----------------
+ 1
+(1 row)
+
+SELECT agtype_to_int8('false');
+ agtype_to_int8
+----------------
+ 0
+(1 row)
+
+-- should fail
+SELECT agtype_to_int8('neither');
+ERROR: invalid input syntax for type agtype
+DETAIL: Expected agtype value, but found "neither".
+CONTEXT: agtype data, line 1: neither
+SELECT agtype_to_int8('NaN');
+ERROR: bigint out of range
+SELECT agtype_to_int8('Inf');
+ERROR: bigint out of range
+SELECT agtype_to_int8(NaN);
+ERROR: column "nan" does not exist
+LINE 1: SELECT agtype_to_int8(NaN);
+ ^
+SELECT agtype_to_int8(Inf);
+ERROR: column "inf" does not exist
+LINE 1: SELECT agtype_to_int8(Inf);
+ ^
--
-- Test boolean to integer cast
--
@@ -2232,14 +2317,8 @@ SELECT agtype_to_int4(agtype_in('false'));
0
(1 row)
-SELECT agtype_to_int4(agtype_in('null'));
- agtype_to_int4
-----------------
-
-(1 row)
-
--
--- Test agtype to integer cast
+-- Test agtype to integer4 cast
--
SELECT agtype_to_int4(agtype_in('1'));
agtype_to_int4
@@ -2261,11 +2340,228 @@ SELECT agtype_to_int4(agtype_in('1.444::numeric'));
-- These should all fail
SELECT agtype_to_int4(agtype_in('"string"'));
-ERROR: invalid input syntax for type integer: "string"
+ERROR: invalid input syntax for type agtype
+DETAIL: Expected agtype value, but found "string".
+CONTEXT: agtype data, line 1: 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
+-- should return SQL NULL
+SELECT agtype_to_int4(agtype_in('null'));
+ agtype_to_int4
+----------------
+
+(1 row)
+
+SELECT agtype_to_int4(NULL);
+ agtype_to_int4
+----------------
+
+(1 row)
+
+-- non agtype input
+SELECT agtype_to_int4(1);
+ agtype_to_int4
+----------------
+ 1
+(1 row)
+
+SELECT agtype_to_int4(3.14);
+ agtype_to_int4
+----------------
+ 3
+(1 row)
+
+SELECT agtype_to_int4(3.14::numeric);
+ agtype_to_int4
+----------------
+ 3
+(1 row)
+
+SELECT agtype_to_int4('3');
+ agtype_to_int4
+----------------
+ 3
+(1 row)
+
+SELECT agtype_to_int4(true);
+ agtype_to_int4
+----------------
+ 1
+(1 row)
+
+SELECT agtype_to_int4(false);
+ agtype_to_int4
+----------------
+ 0
+(1 row)
+
+SELECT agtype_to_int4('3.14');
+ agtype_to_int4
+----------------
+ 3
+(1 row)
+
+SELECT agtype_to_int4('true');
+ agtype_to_int4
+----------------
+ 1
+(1 row)
+
+SELECT agtype_to_int4('false');
+ agtype_to_int4
+----------------
+ 0
+(1 row)
+
+-- should error
+SELECT agtype_to_int4('neither');
+ERROR: invalid input syntax for type agtype
+DETAIL: Expected agtype value, but found "neither".
+CONTEXT: agtype data, line 1: neither
+SELECT agtype_to_int4('NaN');
+ERROR: integer out of range
+SELECT agtype_to_int4('Inf');
+ERROR: integer out of range
+SELECT agtype_to_int4(NaN);
+ERROR: column "nan" does not exist
+LINE 1: SELECT agtype_to_int4(NaN);
+ ^
+SELECT agtype_to_int4(Inf);
+ERROR: column "inf" does not exist
+LINE 1: SELECT agtype_to_int4(Inf);
+ ^
+--
+-- Test boolean to integer2 cast
+--
+SELECT agtype_to_int2(agtype_in('true'));
+ agtype_to_int2
+----------------
+ 1
+(1 row)
+
+SELECT agtype_to_int2(agtype_in('false'));
+ agtype_to_int2
+----------------
+ 0
+(1 row)
+
+--
+-- Test agtype to integer2 cast
+--
+SELECT agtype_to_int2(agtype_in('1'));
+ agtype_to_int2
+----------------
+ 1
+(1 row)
+
+SELECT agtype_to_int2(agtype_in('1.45'));
+ agtype_to_int2
+----------------
+ 1
+(1 row)
+
+SELECT agtype_to_int2(agtype_in('1.444::numeric'));
+ agtype_to_int2
+----------------
+ 1
+(1 row)
+
+-- These should all fail
+SELECT agtype_to_int2(agtype_in('"string"'));
+ERROR: invalid input syntax for type agtype
+DETAIL: Expected agtype value, but found "string".
+CONTEXT: agtype data, line 1: string
+SELECT agtype_to_int2(agtype_in('[1, 2, 3]'));
+ERROR: cannot cast agtype array to type int
+SELECT agtype_to_int2(agtype_in('{"int":1}'));
+ERROR: cannot cast agtype object to type int
+-- should return SQL NULL
+SELECT agtype_to_int2(agtype_in('null'));
+ agtype_to_int2
+----------------
+
+(1 row)
+
+SELECT agtype_to_int2(NULL);
+ agtype_to_int2
+----------------
+
+(1 row)
+
+-- non agtype input
+SELECT agtype_to_int2(1);
+ agtype_to_int2
+----------------
+ 1
+(1 row)
+
+SELECT agtype_to_int2(3.14);
+ agtype_to_int2
+----------------
+ 3
+(1 row)
+
+SELECT agtype_to_int2(3.14::numeric);
+ agtype_to_int2
+----------------
+ 3
+(1 row)
+
+SELECT agtype_to_int2('3');
+ agtype_to_int2
+----------------
+ 3
+(1 row)
+
+SELECT agtype_to_int2(true);
+ agtype_to_int2
+----------------
+ 1
+(1 row)
+
+SELECT agtype_to_int2(false);
+ agtype_to_int2
+----------------
+ 0
+(1 row)
+
+SELECT agtype_to_int2('3.14');
+ agtype_to_int2
+----------------
+ 3
+(1 row)
+
+SELECT agtype_to_int2('true');
+ agtype_to_int2
+----------------
+ 1
+(1 row)
+
+SELECT agtype_to_int2('false');
+ agtype_to_int2
+----------------
+ 0
+(1 row)
+
+-- should error
+SELECT agtype_to_int2('neither');
+ERROR: invalid input syntax for type agtype
+DETAIL: Expected agtype value, but found "neither".
+CONTEXT: agtype data, line 1: neither
+SELECT agtype_to_int2('NaN');
+ERROR: smallint out of range
+SELECT agtype_to_int2('Inf');
+ERROR: smallint out of range
+SELECT agtype_to_int2(NaN);
+ERROR: column "nan" does not exist
+LINE 1: SELECT agtype_to_int2(NaN);
+ ^
+SELECT agtype_to_int2(Inf);
+ERROR: column "inf" does not exist
+LINE 1: SELECT agtype_to_int2(Inf);
+ ^
--
-- Test agtype to int[]
--
@@ -2287,6 +2583,18 @@ SELECT agtype_to_int4_array(agtype_in('["6","7",3.66]'));
{6,7,4}
(1 row)
+-- should error
+SELECT agtype_to_int4_array(bool('true'));
+ERROR: argument must resolve to agtype
+SELECT agtype_to_int4_array((1,2,3,4,5));
+ERROR: argument must resolve to agtype
+-- should return SQL NULL
+SELECT agtype_to_int4_array(NULL);
+ agtype_to_int4_array
+----------------------
+
+(1 row)
+
--
-- Map Literal
--
diff --git a/regress/expected/expr.out b/regress/expected/expr.out
index f634c932..f34661bd 100644
--- a/regress/expected/expr.out
+++ b/regress/expected/expr.out
@@ -1229,11 +1229,14 @@ $$) AS (i int);
1
(1 row)
---Invalid String Format
SELECT * FROM cypher('type_coercion', $$
RETURN '1.0'
$$) AS (i bigint);
-ERROR: invalid input syntax for type bigint: "1.0"
+ i
+---
+ 1
+(1 row)
+
-- Casting to ints that will cause overflow
SELECT * FROM cypher('type_coercion', $$
RETURN 10000000000000000000
@@ -7381,6 +7384,58 @@ SELECT * FROM bool(true AND false);
f
(1 row)
+-- Issue 1329
+-- returns 1
+SELECT agtype_to_int2(bool('true'));
+ agtype_to_int2
+----------------
+ 1
+(1 row)
+
+SELECT agtype_to_int4(bool('true'));
+ agtype_to_int4
+----------------
+ 1
+(1 row)
+
+SELECT agtype_to_int8(bool('true'));
+ agtype_to_int8
+----------------
+ 1
+(1 row)
+
+-- returns 0
+SELECT agtype_to_int2(bool('false'));
+ agtype_to_int2
+----------------
+ 0
+(1 row)
+
+SELECT agtype_to_int4(bool('false'));
+ agtype_to_int4
+----------------
+ 0
+(1 row)
+
+SELECT agtype_to_int8(bool('false'));
+ agtype_to_int8
+----------------
+ 0
+(1 row)
+
+-- should error
+SELECT agtype_to_int2(bool('neither'));
+ERROR: invalid input syntax for type boolean: "neither"
+LINE 1: SELECT agtype_to_int2(bool('neither'));
+ ^
+SELECT agtype_to_int4(bool('neither'));
+ERROR: invalid input syntax for type boolean: "neither"
+LINE 1: SELECT agtype_to_int4(bool('neither'));
+ ^
+SELECT agtype_to_int8(bool('neither'));
+ERROR: invalid input syntax for type boolean: "neither"
+LINE 1: SELECT agtype_to_int8(bool('neither'));
+ ^
--
-- Cleanup
--
diff --git a/regress/sql/agtype.sql b/regress/sql/agtype.sql
index b8f08d9e..74eb4f3a 100644
--- a/regress/sql/agtype.sql
+++ b/regress/sql/agtype.sql
@@ -555,16 +555,33 @@ SELECT bool_to_agtype(true) <> bool_to_agtype(false);
--
SELECT agtype_to_int8(agtype_in('true'));
SELECT agtype_to_int8(agtype_in('false'));
+-- should return SQL NULL
+SELECT agtype_to_int8(agtype_in('null'));
+SELECT agtype_to_int8(NULL);
+-- non agtype input
+SELECT agtype_to_int8(1);
+SELECT agtype_to_int8(3.14);
+SELECT agtype_to_int8(3.14::numeric);
+SELECT agtype_to_int8('3');
+SELECT agtype_to_int8(true);
+SELECT agtype_to_int8(false);
+SELECT agtype_to_int8('3.14');
+SELECT agtype_to_int8('true');
+SELECT agtype_to_int8('false');
+-- should fail
+SELECT agtype_to_int8('neither');
+SELECT agtype_to_int8('NaN');
+SELECT agtype_to_int8('Inf');
+SELECT agtype_to_int8(NaN);
+SELECT agtype_to_int8(Inf);
--
-- 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
+-- Test agtype to integer4 cast
--
SELECT agtype_to_int4(agtype_in('1'));
SELECT agtype_to_int4(agtype_in('1.45'));
@@ -573,6 +590,60 @@ SELECT agtype_to_int4(agtype_in('1.444::numeric'));
SELECT agtype_to_int4(agtype_in('"string"'));
SELECT agtype_to_int4(agtype_in('[1, 2, 3]'));
SELECT agtype_to_int4(agtype_in('{"int":1}'));
+-- should return SQL NULL
+SELECT agtype_to_int4(agtype_in('null'));
+SELECT agtype_to_int4(NULL);
+-- non agtype input
+SELECT agtype_to_int4(1);
+SELECT agtype_to_int4(3.14);
+SELECT agtype_to_int4(3.14::numeric);
+SELECT agtype_to_int4('3');
+SELECT agtype_to_int4(true);
+SELECT agtype_to_int4(false);
+SELECT agtype_to_int4('3.14');
+SELECT agtype_to_int4('true');
+SELECT agtype_to_int4('false');
+-- should error
+SELECT agtype_to_int4('neither');
+SELECT agtype_to_int4('NaN');
+SELECT agtype_to_int4('Inf');
+SELECT agtype_to_int4(NaN);
+SELECT agtype_to_int4(Inf);
+
+--
+-- Test boolean to integer2 cast
+--
+SELECT agtype_to_int2(agtype_in('true'));
+SELECT agtype_to_int2(agtype_in('false'));
+--
+-- Test agtype to integer2 cast
+--
+SELECT agtype_to_int2(agtype_in('1'));
+SELECT agtype_to_int2(agtype_in('1.45'));
+SELECT agtype_to_int2(agtype_in('1.444::numeric'));
+-- These should all fail
+SELECT agtype_to_int2(agtype_in('"string"'));
+SELECT agtype_to_int2(agtype_in('[1, 2, 3]'));
+SELECT agtype_to_int2(agtype_in('{"int":1}'));
+-- should return SQL NULL
+SELECT agtype_to_int2(agtype_in('null'));
+SELECT agtype_to_int2(NULL);
+-- non agtype input
+SELECT agtype_to_int2(1);
+SELECT agtype_to_int2(3.14);
+SELECT agtype_to_int2(3.14::numeric);
+SELECT agtype_to_int2('3');
+SELECT agtype_to_int2(true);
+SELECT agtype_to_int2(false);
+SELECT agtype_to_int2('3.14');
+SELECT agtype_to_int2('true');
+SELECT agtype_to_int2('false');
+-- should error
+SELECT agtype_to_int2('neither');
+SELECT agtype_to_int2('NaN');
+SELECT agtype_to_int2('Inf');
+SELECT agtype_to_int2(NaN);
+SELECT agtype_to_int2(Inf);
--
-- Test agtype to int[]
@@ -580,6 +651,11 @@ SELECT agtype_to_int4(agtype_in('{"int":1}'));
SELECT agtype_to_int4_array(agtype_in('[1,2,3]'));
SELECT agtype_to_int4_array(agtype_in('[1.6,2.3,3.66]'));
SELECT agtype_to_int4_array(agtype_in('["6","7",3.66]'));
+-- should error
+SELECT agtype_to_int4_array(bool('true'));
+SELECT agtype_to_int4_array((1,2,3,4,5));
+-- should return SQL NULL
+SELECT agtype_to_int4_array(NULL);
--
-- Map Literal
diff --git a/regress/sql/expr.sql b/regress/sql/expr.sql
index 5f754e0d..cf0b149d 100644
--- a/regress/sql/expr.sql
+++ b/regress/sql/expr.sql
@@ -583,7 +583,6 @@ SELECT * FROM cypher('type_coercion', $$
RETURN true
$$) AS (i int);
---Invalid String Format
SELECT * FROM cypher('type_coercion', $$
RETURN '1.0'
$$) AS (i bigint);
@@ -3009,6 +3008,20 @@ SELECT * FROM agtype('{"a": 1, "b": 2}'::agtype ->
'a'::text);
-- Text BoolExpr expression node types
SELECT * FROM bool(true AND false);
+-- Issue 1329
+-- returns 1
+SELECT agtype_to_int2(bool('true'));
+SELECT agtype_to_int4(bool('true'));
+SELECT agtype_to_int8(bool('true'));
+-- returns 0
+SELECT agtype_to_int2(bool('false'));
+SELECT agtype_to_int4(bool('false'));
+SELECT agtype_to_int8(bool('false'));
+-- should error
+SELECT agtype_to_int2(bool('neither'));
+SELECT agtype_to_int4(bool('neither'));
+SELECT agtype_to_int8(bool('neither'));
+
--
-- Cleanup
--
diff --git a/src/backend/utils/adt/agtype.c b/src/backend/utils/adt/agtype.c
index ff7ddabe..dba2c138 100644
--- a/src/backend/utils/adt/agtype.c
+++ b/src/backend/utils/adt/agtype.c
@@ -104,6 +104,7 @@ typedef enum /* type categories for datum_to_agtype */
} agt_type_category;
static inline Datum agtype_from_cstring(char *str, int len);
+static inline agtype_value *agtype_value_from_cstring(char *str, int len);
size_t check_string_length(size_t len);
static void agtype_in_agtype_annotation(void *pstate, char *annotation);
static void agtype_in_object_start(void *pstate);
@@ -352,13 +353,14 @@ Datum agtype_out(PG_FUNCTION_ARGS)
}
/*
- * agtype_from_cstring
+ * agtype_value_from_cstring
*
- * Turns agtype string into an agtype Datum.
+ * Helper function to turn an agtype string into an agtype_value.
*
* Uses the agtype parser (with hooks) to construct an agtype.
*/
-static inline Datum agtype_from_cstring(char *str, int len)
+
+static inline agtype_value *agtype_value_from_cstring(char *str, int len)
{
agtype_lex_context *lex;
agtype_in_state state;
@@ -382,7 +384,21 @@ static inline Datum agtype_from_cstring(char *str, int len)
parse_agtype(lex, &sem);
/* after parsing, the item member has the composed agtype structure */
- PG_RETURN_POINTER(agtype_value_to_agtype(state.res));
+ return state.res;
+}
+
+/*
+ * agtype_from_cstring
+ *
+ * Turns agtype string into a Datum of agtype.
+ *
+ * Calls helper function
+ */
+static inline Datum agtype_from_cstring(char *str, int len)
+{
+ agtype_value *agtv = agtype_value_from_cstring(str, len);
+
+ PG_RETURN_POINTER(agtype_value_to_agtype(agtv));
}
size_t check_string_length(size_t len)
@@ -2583,17 +2599,20 @@ PG_FUNCTION_INFO_V1(agtype_to_int8);
*/
Datum agtype_to_int8(PG_FUNCTION_ARGS)
{
- agtype *agtype_in = AG_GET_ARG_AGTYPE_P(0);
agtype_value agtv;
+ agtype_value *agtv_p = NULL;
+ agtype_value *container = NULL;
int64 result = 0x0;
- agtype *arg_agt;
+ agtype *arg_agt = NULL;
/* 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();
+ }
if (!agtype_extract_scalar(&arg_agt->root, &agtv) ||
(agtv.type != AGTV_FLOAT &&
@@ -2601,26 +2620,78 @@ Datum agtype_to_int8(PG_FUNCTION_ARGS)
agtv.type != AGTV_NUMERIC &&
agtv.type != AGTV_STRING &&
agtv.type != AGTV_BOOL))
+ {
cannot_cast_agtype_value(agtv.type, "int");
+ }
- PG_FREE_IF_COPY(agtype_in, 0);
+ agtv_p = &agtv;
+
+ /*
+ * If it is an agtype string, we need to convert the string component
first.
+ * We need to do this because the string could be any type of value. Fx,
+ * integer, float, boolean, numeric, object, or array. Once converted, we
+ * need to remember scalar values are returned as a scalar array. We only
+ * care about scalar arrays.
+ */
+ if (agtv_p->type == AGTV_STRING)
+ {
+ agtype_value *temp = NULL;
+
+ /* convert the string to an agtype_value */
+ temp = agtype_value_from_cstring(agtv_p->val.string.val,
+ agtv_p->val.string.len);
+
+ if (temp->type != AGTV_ARRAY ||
+ !temp->val.array.raw_scalar)
+ {
+ elog(ERROR, "invalid agtype type: %d", (int)agtv_p->type);
+ }
+
+ /* save the top agtype_value */
+ container = temp;
+ /* get the wrapped agtype_value */
+ temp = &temp->val.array.elems[0];
+
+ if (temp->type == AGTV_FLOAT ||
+ temp->type == AGTV_INTEGER ||
+ temp->type == AGTV_NUMERIC ||
+ temp->type == AGTV_BOOL)
+ {
+ agtv_p = temp;
+ }
+ }
- if (agtv.type == AGTV_INTEGER)
- result = agtv.val.int_value;
- else if (agtv.type == AGTV_FLOAT)
+ /* now check the rest */
+ if (agtv_p->type == AGTV_INTEGER)
+ {
+ result = agtv_p->val.int_value;
+ }
+ else if (agtv_p->type == AGTV_FLOAT)
+ {
result = DatumGetInt64(DirectFunctionCall1(dtoi8,
- Float8GetDatum(agtv.val.float_value)));
- else if (agtv.type == AGTV_NUMERIC)
+ Float8GetDatum(agtv_p->val.float_value)));
+ }
+ else if (agtv_p->type == AGTV_NUMERIC)
+ {
result = DatumGetInt64(DirectFunctionCall1(numeric_int8,
- NumericGetDatum(agtv.val.numeric)));
- else if (agtv.type == AGTV_STRING)
- result = DatumGetInt64(DirectFunctionCall1(int8in,
- CStringGetDatum(agtv.val.string.val)));
- else if(agtv.type == AGTV_BOOL)
- result = DatumGetInt64(DirectFunctionCall1(bool_int4,
- BoolGetDatum(agtv.val.boolean)));
+ NumericGetDatum(agtv_p->val.numeric)));
+ }
+ else if(agtv_p->type == AGTV_BOOL)
+ {
+ result = (agtv_p->val.boolean) ? 1 : 0;
+ }
else
- elog(ERROR, "invalid agtype type: %d", (int)agtv.type);
+ {
+ elog(ERROR, "invalid agtype type: %d", (int)agtv_p->type);
+ }
+
+ /* free the container, if it was used */
+ if (container)
+ {
+ pfree(container);
+ }
+
+ PG_FREE_IF_COPY(arg_agt, 0);
PG_RETURN_INT64(result);
}
@@ -2632,10 +2703,11 @@ PG_FUNCTION_INFO_V1(agtype_to_int4);
*/
Datum agtype_to_int4(PG_FUNCTION_ARGS)
{
- agtype *agtype_in = AG_GET_ARG_AGTYPE_P(0);
agtype_value agtv;
+ agtype_value *agtv_p = NULL;
+ agtype_value *container = NULL;
int32 result = 0x0;
- agtype *arg_agt;
+ agtype *arg_agt = NULL;
/* get the agtype equivalence of any convertable input type */
arg_agt = get_one_agtype_from_variadic_args(fcinfo, 0, 1);
@@ -2656,38 +2728,81 @@ Datum agtype_to_int4(PG_FUNCTION_ARGS)
cannot_cast_agtype_value(agtv.type, "int");
}
- PG_FREE_IF_COPY(agtype_in, 0);
+ agtv_p = &agtv;
+
+ /*
+ * If it is an agtype string, we need to convert the string component
first.
+ * We need to do this because the string could be any type of value. Fx,
+ * integer, float, boolean, numeric, object, or array. Once converted, we
+ * need to remember scalar values are returned as a scalar array. We only
+ * care about scalar arrays.
+ */
+ if (agtv_p->type == AGTV_STRING)
+ {
+ agtype_value *temp = NULL;
+
+ /* convert the string to an agtype_value */
+ temp = agtype_value_from_cstring(agtv_p->val.string.val,
+ agtv_p->val.string.len);
+
+ if (temp->type != AGTV_ARRAY ||
+ !temp->val.array.raw_scalar)
+ {
+ elog(ERROR, "invalid agtype type: %d", (int)agtv_p->type);
+ }
+
+ /* save the top agtype_value */
+ container = temp;
+ /* get the wrapped agtype_value */
+ temp = &temp->val.array.elems[0];
+
+ if (temp->type == AGTV_FLOAT ||
+ temp->type == AGTV_INTEGER ||
+ temp->type == AGTV_NUMERIC ||
+ temp->type == AGTV_BOOL)
+ {
+ agtv_p = temp;
+ }
+ }
- if (agtv.type == AGTV_INTEGER)
+ /* now check the rest */
+ if (agtv_p->type == AGTV_INTEGER)
{
result = DatumGetInt32(DirectFunctionCall1(int84,
- Int64GetDatum(agtv.val.int_value)));
+ Int64GetDatum(agtv_p->val.int_value)));
}
- else if (agtv.type == AGTV_FLOAT)
+ else if (agtv_p->type == AGTV_FLOAT)
{
result = DatumGetInt32(DirectFunctionCall1(dtoi4,
- Float8GetDatum(agtv.val.float_value)));
+ Float8GetDatum(agtv_p->val.float_value)));
}
- else if (agtv.type == AGTV_NUMERIC)
+ else if (agtv_p->type == AGTV_NUMERIC)
{
result = DatumGetInt32(DirectFunctionCall1(numeric_int4,
- NumericGetDatum(agtv.val.numeric)));
+ NumericGetDatum(agtv_p->val.numeric)));
}
- else if (agtv.type == AGTV_STRING)
+ else if (agtv_p->type == AGTV_STRING)
{
result = DatumGetInt32(DirectFunctionCall1(int4in,
- CStringGetDatum(agtv.val.string.val)));
+ CStringGetDatum(agtv_p->val.string.val)));
}
- else if (agtv.type == AGTV_BOOL)
+ else if (agtv_p->type == AGTV_BOOL)
{
- result = DatumGetInt64(DirectFunctionCall1(bool_int4,
- BoolGetDatum(agtv.val.boolean)));
+ result = (agtv_p->val.boolean) ? 1 : 0;
}
else
{
- elog(ERROR, "invalid agtype type: %d", (int)agtv.type);
+ elog(ERROR, "invalid agtype type: %d", (int)agtv_p->type);
+ }
+
+ /* free the container, if it was used */
+ if (container)
+ {
+ pfree(container);
}
+ PG_FREE_IF_COPY(arg_agt, 0);
+
PG_RETURN_INT32(result);
}
@@ -2698,41 +2813,105 @@ PG_FUNCTION_INFO_V1(agtype_to_int2);
*/
Datum agtype_to_int2(PG_FUNCTION_ARGS)
{
- agtype *agtype_in = AG_GET_ARG_AGTYPE_P(0);
agtype_value agtv;
+ agtype_value *agtv_p = NULL;
+ agtype_value *container = NULL;
int16 result = 0x0;
- agtype *arg_agt;
+ agtype *arg_agt = NULL;
/* 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();
+ }
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);
+ agtv_p = &agtv;
+
+ /*
+ * If it is an agtype string, we need to convert the string component
first.
+ * We need to do this because the string could be any type of value. Fx,
+ * integer, float, boolean, numeric, object, or array. Once converted, we
+ * need to remember scalar values are returned as a scalar array. We only
+ * care about scalar arrays.
+ */
+ if (agtv_p->type == AGTV_STRING)
+ {
+ agtype_value *temp = NULL;
+
+ /* convert the string to an agtype_value */
+ temp = agtype_value_from_cstring(agtv_p->val.string.val,
+ agtv_p->val.string.len);
+
+ if (temp->type != AGTV_ARRAY ||
+ !temp->val.array.raw_scalar)
+ {
+ elog(ERROR, "invalid agtype type: %d", (int)agtv_p->type);
+ }
+
+ /* save the top agtype_value */
+ container = temp;
+ /* get the wrapped agtype_value */
+ temp = &temp->val.array.elems[0];
+
+ if (temp->type == AGTV_FLOAT ||
+ temp->type == AGTV_INTEGER ||
+ temp->type == AGTV_NUMERIC ||
+ temp->type == AGTV_BOOL)
+ {
+ agtv_p = temp;
+ }
+ }
- if (agtv.type == AGTV_INTEGER)
+ /* now check the rest */
+ if (agtv_p->type == AGTV_INTEGER)
+ {
result = DatumGetInt16(DirectFunctionCall1(int82,
- Int64GetDatum(agtv.val.int_value)));
- else if (agtv.type == AGTV_FLOAT)
- result = DatumGetInt32(DirectFunctionCall1(dtoi2,
- Float8GetDatum(agtv.val.float_value)));
- else if (agtv.type == AGTV_NUMERIC)
+ Int64GetDatum(agtv_p->val.int_value)));
+ }
+ else if (agtv_p->type == AGTV_FLOAT)
+ {
+ result = DatumGetInt16(DirectFunctionCall1(dtoi2,
+ Float8GetDatum(agtv_p->val.float_value)));
+ }
+ else if (agtv_p->type == AGTV_NUMERIC)
+ {
result = DatumGetInt16(DirectFunctionCall1(numeric_int2,
- NumericGetDatum(agtv.val.numeric)));
- else if (agtv.type == AGTV_STRING)
+ NumericGetDatum(agtv_p->val.numeric)));
+ }
+ else if (agtv_p->type == AGTV_STRING)
+ {
result = DatumGetInt16(DirectFunctionCall1(int2in,
- CStringGetDatum(agtv.val.string.val)));
+ CStringGetDatum(agtv_p->val.string.val)));
+ }
+ else if (agtv_p->type == AGTV_BOOL)
+ {
+ result = (agtv_p->val.boolean) ? 1 : 0;
+ }
else
- elog(ERROR, "invalid agtype type: %d", (int)agtv.type);
+ {
+ elog(ERROR, "invalid agtype type: %d", (int)agtv_p->type);
+ }
+
+ /* free the container, if it was used */
+ if (container)
+ {
+ pfree(container);
+ }
+
+ PG_FREE_IF_COPY(arg_agt, 0);
PG_RETURN_INT16(result);
}
@@ -2870,18 +3049,41 @@ PG_FUNCTION_INFO_V1(agtype_to_int4_array);
/*
* Cast agtype to int4[].
+ *
+ * TODO:
+ *
+ * We either need to change the function definition in age--x.x.x.sql
+ * to something like agtype[] or we need to make this function work
+ * for "any" type input. Right now it only works for an agtype array but
+ * it takes "any" input. Hence the additional code added to block anything
+ * other than agtype.
*/
Datum agtype_to_int4_array(PG_FUNCTION_ARGS)
{
- agtype *agtype_in = AG_GET_ARG_AGTYPE_P(0);
+ agtype_iterator *agtype_iterator = NULL;
+ agtype *agtype_in = NULL;
agtype_value agtv;
agtype_iterator_token agtv_token;
Datum *array_value;
ArrayType *result;
+ Oid arg_type = InvalidOid;
int element_size;
int i;
- agtype_iterator *agtype_iterator = agtype_iterator_init(&agtype_in->root);
+ /* get the input data type */
+ arg_type = get_fn_expr_argtype(fcinfo->flinfo, 0);
+
+ /* verify the input is agtype */
+ if (arg_type != AGTYPEOID)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("argument must resolve to agtype")));
+ }
+
+ agtype_in = AG_GET_ARG_AGTYPE_P(0);
+
+ agtype_iterator = agtype_iterator_init(&agtype_in->root);
agtv_token = agtype_iterator_next(&agtype_iterator, &agtv, false);
if (agtv.type != AGTV_ARRAY)
@@ -9170,8 +9372,8 @@ agtype_value *alter_properties(agtype_value
*original_properties,
* extract_variadic_args.
*/
agtype *get_one_agtype_from_variadic_args(FunctionCallInfo fcinfo,
- int variadic_offset,
- int expected_nargs)
+ int variadic_offset,
+ int expected_nargs)
{
int nargs;
Datum *args = NULL;