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/incubator-age.git


The following commit(s) were added to refs/heads/master by this push:
     new c12974c  Add openCypher trig functions sin, cos, tan, and cot.
c12974c is described below

commit c12974c93a988a800c16cb5c730a3023b07c25f7
Author: John Gemignani <[email protected]>
AuthorDate: Tue Aug 25 17:11:22 2020 -0700

    Add openCypher trig functions sin, cos, tan, and cot.
    
    Added the openCypher trig functions sin, cos, tan, and cot.
    
    Added regression tests.
    
    Note: cot(0) returns the float value infinity instead of null per
    the openCypher spec.
    
    The reason for this is partly that neo4j's implementation doesn't
    return null either. And partly that, even though the openCypher spec
    states that the cot(0) should be null, it say nothing about why. One
    has to assume that it is due to the cotangent being undefined at zero.
    However, that isn't the only point where the cotangent is undefined.
    Moreover, there is no such stated condition for the tangent function
    which also has points where it is undefined.
---
 age--0.2.0.sql                     |  35 ++++-
 regress/expected/cypher_create.out |  16 +-
 regress/expected/expr.out          | 185 +++++++++++++++++++++++
 regress/sql/expr.sql               |  70 +++++++++
 src/backend/parser/cypher_expr.c   |   8 +-
 src/backend/utils/adt/agtype.c     | 296 +++++++++++++++++++++++++++++++++++++
 6 files changed, 600 insertions(+), 10 deletions(-)

diff --git a/age--0.2.0.sql b/age--0.2.0.sql
index bce57be..f55161d 100644
--- a/age--0.2.0.sql
+++ b/age--0.2.0.sql
@@ -910,6 +910,9 @@ STABLE
 PARALLEL SAFE
 AS 'MODULE_PATHNAME';
 
+--
+-- String functions
+--
 CREATE FUNCTION reverse(variadic "any")
 RETURNS agtype
 LANGUAGE c
@@ -994,9 +997,39 @@ PARALLEL SAFE
 AS 'MODULE_PATHNAME';
 
 --
--- function for typecasting an agtype value to another agtype value
+-- Trig functions - radian input
 --
+CREATE FUNCTION r_sin(variadic "any")
+RETURNS agtype
+LANGUAGE c
+STABLE
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
 
+CREATE FUNCTION r_cos(variadic "any")
+RETURNS agtype
+LANGUAGE c
+STABLE
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE FUNCTION r_tan(variadic "any")
+RETURNS agtype
+LANGUAGE c
+STABLE
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE FUNCTION r_cot(variadic "any")
+RETURNS agtype
+LANGUAGE c
+STABLE
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+--
+-- function for typecasting an agtype value to another agtype value
+--
 CREATE FUNCTION agtype_typecast_numeric(agtype)
 RETURNS agtype
 LANGUAGE c
diff --git a/regress/expected/cypher_create.out 
b/regress/expected/cypher_create.out
index 47190eb..fd8de17 100644
--- a/regress/expected/cypher_create.out
+++ b/regress/expected/cypher_create.out
@@ -375,14 +375,14 @@ SELECT * FROM cypher_create.e_var;
 SELECT * FROM ag_label;
        name       | graph | id | kind |            relation            
 ------------------+-------+----+------+--------------------------------
- _ag_label_vertex | 17029 |  1 | v    | cypher_create._ag_label_vertex
- _ag_label_edge   | 17029 |  2 | e    | cypher_create._ag_label_edge
- v                | 17029 |  3 | v    | cypher_create.v
- e                | 17029 |  4 | e    | cypher_create.e
- n_var            | 17029 |  5 | v    | cypher_create.n_var
- e_var            | 17029 |  6 | e    | cypher_create.e_var
- n_other_node     | 17029 |  7 | v    | cypher_create.n_other_node
- b_var            | 17029 |  8 | e    | cypher_create.b_var
+ _ag_label_vertex | 17037 |  1 | v    | cypher_create._ag_label_vertex
+ _ag_label_edge   | 17037 |  2 | e    | cypher_create._ag_label_edge
+ v                | 17037 |  3 | v    | cypher_create.v
+ e                | 17037 |  4 | e    | cypher_create.e
+ n_var            | 17037 |  5 | v    | cypher_create.n_var
+ e_var            | 17037 |  6 | e    | cypher_create.e_var
+ n_other_node     | 17037 |  7 | v    | cypher_create.n_other_node
+ b_var            | 17037 |  8 | e    | cypher_create.b_var
 (8 rows)
 
 --Validate every vertex has the correct label
diff --git a/regress/expected/expr.out b/regress/expected/expr.out
index 6c2aacd..6bd7b96 100644
--- a/regress/expected/expr.out
+++ b/regress/expected/expr.out
@@ -2957,6 +2957,191 @@ ERROR:  replace() unsuppoted argument type 23
 SELECT * FROM replace('Hello', 1, 'E');
 ERROR:  replace() unsuppoted argument type 23
 --
+-- sin, cos, tan, cot
+--
+SELECT * FROM cypher('expr', $$
+    RETURN sin(3.1415)
+$$) AS (results agtype), sin(3.1415);
+       results        |         sin          
+----------------------+----------------------
+ 9.26535896604903e-05 | 9.26535896604903e-05
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+    RETURN cos(3.1415)
+$$) AS (results agtype), cos(3.1415);
+      results       |        cos         
+--------------------+--------------------
+ -0.999999995707656 | -0.999999995707656
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+    RETURN tan(3.1415)
+$$) AS (results agtype), tan(3.1415);
+        results        |          tan          
+-----------------------+-----------------------
+ -9.26535900581913e-05 | -9.26535900581913e-05
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+    RETURN cot(3.1415)
+$$) AS (results agtype), cot(3.1415);
+      results      |        cot        
+-------------------+-------------------
+ -10792.8899395258 | -10792.8899395258
+(1 row)
+
+SELECT * FROM sin(3.1415), r_sin(3.1415);
+         sin          |        r_sin         
+----------------------+----------------------
+ 9.26535896604903e-05 | 9.26535896604903e-05
+(1 row)
+
+SELECT * FROM cos(3.1415), r_cos(3.1415);
+        cos         |       r_cos        
+--------------------+--------------------
+ -0.999999995707656 | -0.999999995707656
+(1 row)
+
+SELECT * FROM tan(3.1415), r_tan(3.1415);
+          tan          |         r_tan         
+-----------------------+-----------------------
+ -9.26535900581913e-05 | -9.26535900581913e-05
+(1 row)
+
+SELECT * FROM cot(3.1415), r_cot(3.1415);
+        cot        |       r_cot       
+-------------------+-------------------
+ -10792.8899395258 | -10792.8899395258
+(1 row)
+
+-- should return null
+SELECT * FROM cypher('expr', $$
+    RETURN sin(null)
+$$) AS (results agtype);
+ results 
+---------
+ 
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+    RETURN cos(null)
+$$) AS (results agtype);
+ results 
+---------
+ 
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+    RETURN tan(null)
+$$) AS (results agtype);
+ results 
+---------
+ 
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+    RETURN cot(null)
+$$) AS (results agtype);
+ results 
+---------
+ 
+(1 row)
+
+SELECT * FROM r_sin(null);
+ r_sin 
+-------
+ 
+(1 row)
+
+SELECT * FROM r_cos(null);
+ r_cos 
+-------
+ 
+(1 row)
+
+SELECT * FROM r_tan(null);
+ r_tan 
+-------
+ 
+(1 row)
+
+SELECT * FROM r_cot(null);
+ r_cot 
+-------
+ 
+(1 row)
+
+-- should fail
+SELECT * FROM cypher('expr', $$
+    RETURN sin("0")
+$$) AS (results agtype);
+ERROR:  sin() unsuppoted argument agtype 1
+SELECT * FROM cypher('expr', $$
+    RETURN cos("0")
+$$) AS (results agtype);
+ERROR:  cos() unsuppoted argument agtype 1
+SELECT * FROM cypher('expr', $$
+    RETURN tan("0")
+$$) AS (results agtype);
+ERROR:  tan() unsuppoted argument agtype 1
+SELECT * FROM cypher('expr', $$
+    RETURN cot("0")
+$$) AS (results agtype);
+ERROR:  cot() unsuppoted argument agtype 1
+SELECT * FROM cypher('expr', $$
+    RETURN sin()
+$$) AS (results agtype);
+ERROR:  unrecognized or unsupported function
+LINE 1: SELECT * FROM cypher('expr', $$
+                                      ^
+SELECT * FROM cypher('expr', $$
+    RETURN cos()
+$$) AS (results agtype);
+ERROR:  unrecognized or unsupported function
+LINE 1: SELECT * FROM cypher('expr', $$
+                                      ^
+SELECT * FROM cypher('expr', $$
+    RETURN tan()
+$$) AS (results agtype);
+ERROR:  unrecognized or unsupported function
+LINE 1: SELECT * FROM cypher('expr', $$
+                                      ^
+SELECT * FROM cypher('expr', $$
+    RETURN cot()
+$$) AS (results agtype);
+ERROR:  unrecognized or unsupported function
+LINE 1: SELECT * FROM cypher('expr', $$
+                                      ^
+SELECT * FROM r_sin('0');
+ERROR:  sin() unsuppoted argument type 25
+SELECT * FROM r_cos('0');
+ERROR:  cos() unsuppoted argument type 25
+SELECT * FROM r_tan('0');
+ERROR:  tan() unsuppoted argument type 25
+SELECT * FROM r_cot('0');
+ERROR:  cot() unsuppoted argument type 25
+SELECT * FROM r_sin();
+ERROR:  function r_sin() does not exist
+LINE 1: SELECT * FROM r_sin();
+                      ^
+HINT:  No function matches the given name and argument types. You might need 
to add explicit type casts.
+SELECT * FROM r_cos();
+ERROR:  function r_cos() does not exist
+LINE 1: SELECT * FROM r_cos();
+                      ^
+HINT:  No function matches the given name and argument types. You might need 
to add explicit type casts.
+SELECT * FROM r_tan();
+ERROR:  function r_tan() does not exist
+LINE 1: SELECT * FROM r_tan();
+                      ^
+HINT:  No function matches the given name and argument types. You might need 
to add explicit type casts.
+SELECT * FROM r_cot();
+ERROR:  function r_cot() does not exist
+LINE 1: SELECT * FROM r_cot();
+                      ^
+HINT:  No function matches the given name and argument types. You might need 
to add explicit type casts.
+--
 -- Cleanup
 --
 SELECT * FROM drop_graph('expr', true);
diff --git a/regress/sql/expr.sql b/regress/sql/expr.sql
index 4ad18f3..06dbc11 100644
--- a/regress/sql/expr.sql
+++ b/regress/sql/expr.sql
@@ -1270,6 +1270,76 @@ SELECT * FROM replace('Hello', 'e', 1);
 SELECT * FROM replace('Hello', 1, 'E');
 
 --
+-- sin, cos, tan, cot
+--
+SELECT * FROM cypher('expr', $$
+    RETURN sin(3.1415)
+$$) AS (results agtype), sin(3.1415);
+SELECT * FROM cypher('expr', $$
+    RETURN cos(3.1415)
+$$) AS (results agtype), cos(3.1415);
+SELECT * FROM cypher('expr', $$
+    RETURN tan(3.1415)
+$$) AS (results agtype), tan(3.1415);
+SELECT * FROM cypher('expr', $$
+    RETURN cot(3.1415)
+$$) AS (results agtype), cot(3.1415);
+SELECT * FROM sin(3.1415), r_sin(3.1415);
+SELECT * FROM cos(3.1415), r_cos(3.1415);
+SELECT * FROM tan(3.1415), r_tan(3.1415);
+SELECT * FROM cot(3.1415), r_cot(3.1415);
+-- should return null
+SELECT * FROM cypher('expr', $$
+    RETURN sin(null)
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+    RETURN cos(null)
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+    RETURN tan(null)
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+    RETURN cot(null)
+$$) AS (results agtype);
+SELECT * FROM r_sin(null);
+SELECT * FROM r_cos(null);
+SELECT * FROM r_tan(null);
+SELECT * FROM r_cot(null);
+-- should fail
+SELECT * FROM cypher('expr', $$
+    RETURN sin("0")
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+    RETURN cos("0")
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+    RETURN tan("0")
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+    RETURN cot("0")
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+    RETURN sin()
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+    RETURN cos()
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+    RETURN tan()
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+    RETURN cot()
+$$) AS (results agtype);
+SELECT * FROM r_sin('0');
+SELECT * FROM r_cos('0');
+SELECT * FROM r_tan('0');
+SELECT * FROM r_cot('0');
+SELECT * FROM r_sin();
+SELECT * FROM r_cos();
+SELECT * FROM r_tan();
+SELECT * FROM r_cot();
+
+--
 -- Cleanup
 --
 SELECT * FROM drop_graph('expr', true);
diff --git a/src/backend/parser/cypher_expr.c b/src/backend/parser/cypher_expr.c
index 5860c3e..acdc94b 100644
--- a/src/backend/parser/cypher_expr.c
+++ b/src/backend/parser/cypher_expr.c
@@ -68,6 +68,11 @@
 #define FUNC_BSUBSTR    {"substring",  "b_substr",   ANYOID,    ANYOID, 
ANYOID, AGTYPEOID, -1, 1, false}
 #define FUNC_SPLIT      {"split",      "split",      ANYOID,    ANYOID, 0, 
AGTYPEOID, 2, 1, false}
 #define FUNC_REPLACE    {"replace",    "replace",    ANYOID,    ANYOID, 0, 
AGTYPEOID, 3, 1, false}
+#define FUNC_RSIN       {"sin",        "r_sin",      ANYOID,    0, 0, 
AGTYPEOID, 1, 1, false}
+#define FUNC_RCOS       {"cos",        "r_cos",      ANYOID,    0, 0, 
AGTYPEOID, 1, 1, false}
+#define FUNC_RTAN       {"tan",        "r_tan",      ANYOID,    0, 0, 
AGTYPEOID, 1, 1, false}
+#define FUNC_RCOT       {"cot",        "r_cot",      ANYOID,    0, 0, 
AGTYPEOID, 1, 1, false}
+
 
 /* supported functions */
 #define SUPPORTED_FUNCTIONS {FUNC_TYPE, FUNC_ENDNODE, FUNC_HEAD, FUNC_ID, \
@@ -78,7 +83,8 @@
                              FUNC_TOUPPER, FUNC_TOLOWER, FUNC_LTRIM, \
                              FUNC_RTRIM, FUNC_BTRIM, FUNC_RSUBSTR, \
                              FUNC_LSUBSTR, FUNC_BSUBSTR, FUNC_SPLIT, \
-                             FUNC_REPLACE}
+                             FUNC_REPLACE, FUNC_RSIN, FUNC_RCOS, FUNC_RTAN, \
+                             FUNC_RCOT}
 
 /* structure for supported function signatures */
 typedef struct function_signature
diff --git a/src/backend/utils/adt/agtype.c b/src/backend/utils/adt/agtype.c
index 942ab5d..b879af7 100644
--- a/src/backend/utils/adt/agtype.c
+++ b/src/backend/utils/adt/agtype.c
@@ -129,6 +129,8 @@ static Datum get_vertex(const char *graph, const char 
*vertex_label,
 static Datum column_get_datum(TupleDesc tupdesc, HeapTuple tuple, int column,
                         const char *attname, Oid typid, bool isnull);
 static char *get_label_name(const char *graph_name, int64 graph_id);
+static float8 get_float_compatible_arg(Datum arg, Oid type, char *funcname,
+                                       bool *is_null);
 
 PG_FUNCTION_INFO_V1(agtype_in);
 
@@ -5611,3 +5613,297 @@ Datum replace(PG_FUNCTION_ARGS)
 
     PG_RETURN_POINTER(agtype_value_to_agtype(&agtv_result));
 }
+
+/*
+ * Helper function to extract one float8 compatible value from a variadic any.
+ * It supports integer2/4/8, float4/8, and numeric or the agtype integer, 
float,
+ * and numeric for the argument. It does not support a character based float,
+ * otherwise we would just use tofloat. It returns an agtype float on success 
or
+ * fails with a message stating the funcname that called it and a specific
+ * message stating the error.
+ */
+static float8 get_float_compatible_arg(Datum arg, Oid type, char *funcname,
+                                       bool *is_null)
+{
+    float8 result;
+
+    /* Assume the value is null. Although, this is only necessary for agtypes 
*/
+    *is_null = true;
+
+    if (type != AGTYPEOID)
+    {
+        if (type == INT2OID)
+            result = (float8) DatumGetInt16(arg);
+        else if (type == INT4OID)
+            result = (float8) DatumGetInt32(arg);
+        else if (type == INT8OID)
+        {
+            /*
+             * Get the string representation of the integer because it could be
+             * too large to fit in a float. Let the float routine determine
+             * what to do with it.
+             */
+            char *string = DatumGetCString(DirectFunctionCall1(int8out, arg));
+            bool is_valid = false;
+            /* turn it into a float */
+            result = float8in_internal_null(string, NULL, "double precision",
+                                            string, &is_valid);
+
+            /* return null if it was not a invalid float */
+            if (!is_valid)
+                return 0;
+        }
+        else if (type == FLOAT4OID)
+            result = (float8) DatumGetFloat4(arg);
+        else if (type == FLOAT8OID)
+            result = DatumGetFloat8(arg);
+        else if (type == NUMERICOID)
+            result = DatumGetFloat8(DirectFunctionCall1(
+                numeric_float8_no_overflow, arg));
+        else
+            ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                            errmsg("%s() unsuppoted argument type %d", 
funcname,
+                                   type)));
+    }
+    else
+    {
+        agtype *agt_arg;
+        agtype_value *agtv_value;
+
+        /* get the agtype argument */
+        agt_arg = DATUM_GET_AGTYPE_P(arg);
+
+        if (!AGT_ROOT_IS_SCALAR(agt_arg))
+            ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                            errmsg("%s() only supports scalar arguments",
+                                   funcname)));
+
+        agtv_value = get_ith_agtype_value_from_container(&agt_arg->root, 0);
+
+        /* check for agtype null */
+        if (agtv_value->type == AGTV_NULL)
+            return 0;
+
+        if (agtv_value->type == AGTV_INTEGER)
+        {
+            /*
+             * Get the string representation of the integer because it could be
+             * too large to fit in a float. Let the float routine determine
+             * what to do with it.
+             */
+            bool is_valid = false;
+            char *string = DatumGetCString(DirectFunctionCall1(int8out,
+                                                               
Int64GetDatum(agtv_value->val.int_value)));
+            /* turn it into a float */
+            result = float8in_internal_null(string, NULL, "double precision",
+                                            string, &is_valid);
+
+            /* return null if it was not a valid float */
+            if (!is_valid)
+                return 0;
+        }
+        else if (agtv_value->type == AGTV_FLOAT)
+            result = agtv_value->val.float_value;
+        else if (agtv_value->type == AGTV_NUMERIC)
+            result = DatumGetFloat8(DirectFunctionCall1(
+                numeric_float8_no_overflow,
+                NumericGetDatum(agtv_value->val.numeric)));
+        else
+            ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                            errmsg("%s() unsuppoted argument agtype %d",
+                                   funcname, agtv_value->type)));
+    }
+
+    /* there is a valid non null value */
+    *is_null = false;
+
+    return result;
+}
+
+PG_FUNCTION_INFO_V1(r_sin);
+
+Datum r_sin(PG_FUNCTION_ARGS)
+{
+    int nargs;
+    Datum *args;
+    bool *nulls;
+    Oid *types;
+    agtype_value agtv_result;
+    float8 angle;
+    float8 result;
+    bool is_null = true;
+
+    /* extract argument values */
+    nargs = extract_variadic_args(fcinfo, 0, true, &args, &types, &nulls);
+
+    /* check number of args */
+    if (nargs != 1)
+        ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                        errmsg("sin() invalid number of arguments")));
+
+    /* check for a null input */
+    if (nargs < 0 || nulls[0])
+        PG_RETURN_NULL();
+
+    /*
+     * sin() supports integer, float, and numeric or the agtype integer, float,
+     * and numeric for the angle
+     */
+
+    angle = get_float_compatible_arg(args[0], types[0], "sin", &is_null);
+
+    /* check for a agtype null input */
+    if (is_null)
+        PG_RETURN_NULL();
+
+    /* We need the numeric input as a float8 so that we can pass it off to PG 
*/
+    result = DatumGetFloat8(DirectFunctionCall1(dsin,
+                                                Float8GetDatum(angle)));
+
+    /* build the result */
+    agtv_result.type = AGTV_FLOAT;
+    agtv_result.val.float_value = result;
+
+    PG_RETURN_POINTER(agtype_value_to_agtype(&agtv_result));
+}
+
+PG_FUNCTION_INFO_V1(r_cos);
+
+Datum r_cos(PG_FUNCTION_ARGS)
+{
+    int nargs;
+    Datum *args;
+    bool *nulls;
+    Oid *types;
+    agtype_value agtv_result;
+    float8 angle;
+    float8 result;
+    bool is_null = true;
+
+    /* extract argument values */
+    nargs = extract_variadic_args(fcinfo, 0, true, &args, &types, &nulls);
+
+    /* check number of args */
+    if (nargs != 1)
+        ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                        errmsg("cos() invalid number of arguments")));
+
+    /* check for a null input */
+    if (nargs < 0 || nulls[0])
+        PG_RETURN_NULL();
+
+    /*
+     * cos() supports integer, float, and numeric or the agtype integer, float,
+     * and numeric for the angle
+     */
+
+    angle = get_float_compatible_arg(args[0], types[0], "cos", &is_null);
+
+    /* check for a agtype null input */
+    if (is_null)
+        PG_RETURN_NULL();
+
+    /* We need the numeric input as a float8 so that we can pass it off to PG 
*/
+    result = DatumGetFloat8(DirectFunctionCall1(dcos,
+                                                Float8GetDatum(angle)));
+
+    /* build the result */
+    agtv_result.type = AGTV_FLOAT;
+    agtv_result.val.float_value = result;
+
+    PG_RETURN_POINTER(agtype_value_to_agtype(&agtv_result));
+}
+
+PG_FUNCTION_INFO_V1(r_tan);
+
+Datum r_tan(PG_FUNCTION_ARGS)
+{
+    int nargs;
+    Datum *args;
+    bool *nulls;
+    Oid *types;
+    agtype_value agtv_result;
+    float8 angle;
+    float8 result;
+    bool is_null = true;
+
+    /* extract argument values */
+    nargs = extract_variadic_args(fcinfo, 0, true, &args, &types, &nulls);
+
+    /* check number of args */
+    if (nargs != 1)
+        ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                        errmsg("tan() invalid number of arguments")));
+
+    /* check for a null input */
+    if (nargs < 0 || nulls[0])
+        PG_RETURN_NULL();
+
+    /*
+     * tan() supports integer, float, and numeric or the agtype integer, float,
+     * and numeric for the angle
+     */
+
+    angle = get_float_compatible_arg(args[0], types[0], "tan", &is_null);
+
+    /* check for a agtype null input */
+    if (is_null)
+        PG_RETURN_NULL();
+
+    /* We need the numeric input as a float8 so that we can pass it off to PG 
*/
+    result = DatumGetFloat8(DirectFunctionCall1(dtan,
+                                                Float8GetDatum(angle)));
+
+    /* build the result */
+    agtv_result.type = AGTV_FLOAT;
+    agtv_result.val.float_value = result;
+
+    PG_RETURN_POINTER(agtype_value_to_agtype(&agtv_result));
+}
+
+PG_FUNCTION_INFO_V1(r_cot);
+
+Datum r_cot(PG_FUNCTION_ARGS)
+{
+    int nargs;
+    Datum *args;
+    bool *nulls;
+    Oid *types;
+    agtype_value agtv_result;
+    float8 angle;
+    float8 result;
+    bool is_null = true;
+
+    /* extract argument values */
+    nargs = extract_variadic_args(fcinfo, 0, true, &args, &types, &nulls);
+
+    /* check number of args */
+    if (nargs != 1)
+        ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                        errmsg("cot() invalid number of arguments")));
+
+    /* check for a null input */
+    if (nargs < 0 || nulls[0])
+        PG_RETURN_NULL();
+
+    /*
+     * cot() supports integer, float, and numeric or the agtype integer, float,
+     * and numeric for the angle
+     */
+
+    angle = get_float_compatible_arg(args[0], types[0], "cot", &is_null);
+
+    /* check for a agtype null input */
+    if (is_null)
+        PG_RETURN_NULL();
+
+    /* We need the numeric input as a float8 so that we can pass it off to PG 
*/
+    result = DatumGetFloat8(DirectFunctionCall1(dcot,
+                                                Float8GetDatum(angle)));
+
+    /* build the result */
+    agtv_result.type = AGTV_FLOAT;
+    agtv_result.val.float_value = result;
+
+    PG_RETURN_POINTER(agtype_value_to_agtype(&agtv_result));
+}

Reply via email to