diff --git a/regress/expected/agtype.out b/regress/expected/agtype.out index f8860b63a..04ec1f3d6 100644 --- a/regress/expected/agtype.out +++ b/regress/expected/agtype.out @@ -2744,6 +2744,59 @@ 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) + +-- should error +SELECT agtype_to_int8('3.14'); +ERROR: invalid input syntax for type bigint: "3.14" -- -- Test boolean to integer cast -- @@ -2759,14 +2812,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 @@ -2793,6 +2840,155 @@ 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) + +-- should error +SELECT agtype_to_int4('3.14'); +ERROR: invalid input syntax for type integer: "3.14" +-- +-- 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 smallint: "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) + +-- should error +SELECT agtype_to_int2('3.14'); +ERROR: invalid input syntax for type smallint: "3.14" -- -- Test agtype to int[] -- @@ -2814,6 +3010,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 3edb7dd6e..64afa1ca4 100644 --- a/regress/expected/expr.out +++ b/regress/expected/expr.out @@ -7559,6 +7559,70 @@ 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')); + ^ +SELECT agtype_to_int2('true'); +ERROR: invalid input syntax for type smallint: "true" +SELECT agtype_to_int4('true'); +ERROR: invalid input syntax for type integer: "true" +SELECT agtype_to_int8('true'); +ERROR: invalid input syntax for type bigint: "true" +SELECT agtype_to_int2('false'); +ERROR: invalid input syntax for type smallint: "false" +SELECT agtype_to_int4('false'); +ERROR: invalid input syntax for type integer: "false" +SELECT agtype_to_int8('false'); +ERROR: invalid input syntax for type bigint: "false" -- -- Cleanup -- diff --git a/regress/sql/agtype.sql b/regress/sql/agtype.sql index 718e6234a..7d69fb300 100644 --- a/regress/sql/agtype.sql +++ b/regress/sql/agtype.sql @@ -674,16 +674,27 @@ 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); + +-- should error +SELECT agtype_to_int8('3.14'); -- -- 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')); @@ -692,6 +703,46 @@ 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); +-- should error +SELECT agtype_to_int4('3.14'); + +-- +-- 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); +-- should error +SELECT agtype_to_int2('3.14'); -- -- Test agtype to int[] @@ -699,6 +750,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 931159b6b..3171faa2b 100644 --- a/regress/sql/expr.sql +++ b/regress/sql/expr.sql @@ -3078,6 +3078,26 @@ 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')); +SELECT agtype_to_int2('true'); +SELECT agtype_to_int4('true'); +SELECT agtype_to_int8('true'); +SELECT agtype_to_int2('false'); +SELECT agtype_to_int4('false'); +SELECT agtype_to_int8('false'); + -- -- Cleanup -- diff --git a/src/backend/utils/adt/agtype.c b/src/backend/utils/adt/agtype.c index 412351063..09d43ae19 100644 --- a/src/backend/utils/adt/agtype.c +++ b/src/backend/utils/adt/agtype.c @@ -2609,7 +2609,6 @@ 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; int64 result = 0x0; agtype *arg_agt; @@ -2619,7 +2618,9 @@ Datum agtype_to_int8(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 && @@ -2627,26 +2628,39 @@ 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); + } if (agtv.type == AGTV_INTEGER) + { result = agtv.val.int_value; + } else if (agtv.type == AGTV_FLOAT) + { result = DatumGetInt64(DirectFunctionCall1(dtoi8, - Float8GetDatum(agtv.val.float_value))); + Float8GetDatum(agtv.val.float_value))); + } else if (agtv.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))); + CStringGetDatum(agtv.val.string.val))); + } else if(agtv.type == AGTV_BOOL) - result = DatumGetInt64(DirectFunctionCall1(bool_int4, - BoolGetDatum(agtv.val.boolean))); + { + result = (agtv.val.boolean) ? 1 : 0; + } else + { elog(ERROR, "invalid agtype type: %d", (int)agtv.type); + } + + PG_FREE_IF_COPY(arg_agt, 0); PG_RETURN_INT64(result); } @@ -2658,7 +2672,6 @@ 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; int32 result = 0x0; agtype *arg_agt; @@ -2682,17 +2695,15 @@ Datum agtype_to_int4(PG_FUNCTION_ARGS) 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))); + Int64GetDatum(agtv.val.int_value))); } else if (agtv.type == AGTV_FLOAT) { result = DatumGetInt32(DirectFunctionCall1(dtoi4, - Float8GetDatum(agtv.val.float_value))); + Float8GetDatum(agtv.val.float_value))); } else if (agtv.type == AGTV_NUMERIC) { @@ -2702,18 +2713,19 @@ Datum agtype_to_int4(PG_FUNCTION_ARGS) else if (agtv.type == AGTV_STRING) { result = DatumGetInt32(DirectFunctionCall1(int4in, - CStringGetDatum(agtv.val.string.val))); + CStringGetDatum(agtv.val.string.val))); } else if (agtv.type == AGTV_BOOL) { - result = DatumGetInt64(DirectFunctionCall1(bool_int4, - BoolGetDatum(agtv.val.boolean))); + result = (agtv.val.boolean) ? 1 : 0; } else { elog(ERROR, "invalid agtype type: %d", (int)agtv.type); } + PG_FREE_IF_COPY(arg_agt, 0); + PG_RETURN_INT32(result); } @@ -2724,7 +2736,6 @@ 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; int16 result = 0x0; agtype *arg_agt; @@ -2734,31 +2745,50 @@ Datum agtype_to_int2(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 = DatumGetInt16(DirectFunctionCall1(int82, - Int64GetDatum(agtv.val.int_value))); + Int64GetDatum(agtv.val.int_value))); + } else if (agtv.type == AGTV_FLOAT) + { result = DatumGetInt32(DirectFunctionCall1(dtoi2, - Float8GetDatum(agtv.val.float_value))); + Float8GetDatum(agtv.val.float_value))); + } else if (agtv.type == AGTV_NUMERIC) + { result = DatumGetInt16(DirectFunctionCall1(numeric_int2, NumericGetDatum(agtv.val.numeric))); + } else if (agtv.type == AGTV_STRING) + { result = DatumGetInt16(DirectFunctionCall1(int2in, - CStringGetDatum(agtv.val.string.val))); + CStringGetDatum(agtv.val.string.val))); + } + else if (agtv.type == AGTV_BOOL) + { + result = (agtv.val.boolean) ? 1 : 0; + } else + { elog(ERROR, "invalid agtype type: %d", (int)agtv.type); + } + + PG_FREE_IF_COPY(arg_agt, 0); PG_RETURN_INT16(result); } @@ -2896,18 +2926,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) @@ -9450,8 +9503,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;