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 e65fb40  Add openCypher log() and log10 functions
e65fb40 is described below

commit e65fb40ac6864ea618484638547e362cbf85e447
Author: John Gemignani <[email protected]>
AuthorDate: Thu Sep 17 10:33:29 2020 -0700

    Add openCypher log() and log10 functions
    
    Added the openCypher log() aka ln() and log10() functions.
    
    Added logic to return null for arguments of zero or less.
    
    Added regression tests.
---
 age--0.2.0.sql                   |  15 +++++
 regress/expected/expr.out        |  81 +++++++++++++++++++++++++
 regress/sql/expr.sql             |  36 +++++++++++
 src/backend/parser/cypher_expr.c |   4 +-
 src/backend/utils/adt/agtype.c   | 127 +++++++++++++++++++++++++++++++++++++++
 5 files changed, 262 insertions(+), 1 deletion(-)

diff --git a/age--0.2.0.sql b/age--0.2.0.sql
index 741d6c9..76f181d 100644
--- a/age--0.2.0.sql
+++ b/age--0.2.0.sql
@@ -1137,6 +1137,21 @@ LANGUAGE c
 STABLE
 PARALLEL SAFE
 AS 'MODULE_PATHNAME';
+
+CREATE FUNCTION ag_log(variadic "any")
+RETURNS agtype
+LANGUAGE c
+STABLE
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE FUNCTION ag_log10(variadic "any")
+RETURNS agtype
+LANGUAGE c
+STABLE
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
 --
 -- function for typecasting an agtype value to another agtype value
 --
diff --git a/regress/expected/expr.out b/regress/expected/expr.out
index a9fc24e..1ac35e5 100644
--- a/regress/expected/expr.out
+++ b/regress/expected/expr.out
@@ -3857,6 +3857,87 @@ WHERE cypher_1.result = cypher_2.result;
 (0 rows)
 
 --
+-- log (ln) and log10
+--
+SELECT * from cypher('expr', $$
+    RETURN log(2.718281828459045)
+$$) as (result agtype);
+ result 
+--------
+ 1.0
+(1 row)
+
+SELECT * from cypher('expr', $$
+    RETURN log10(10)
+$$) as (result agtype);
+ result 
+--------
+ 1.0
+(1 row)
+
+-- should return null
+SELECT * from cypher('expr', $$
+    RETURN log(null)
+$$) as (result agtype);
+ result 
+--------
+ 
+(1 row)
+
+SELECT * from cypher('expr', $$
+    RETURN log10(null)
+$$) as (result agtype);
+ result 
+--------
+ 
+(1 row)
+
+SELECT * from cypher('expr', $$
+    RETURN log(0)
+$$) as (result agtype);
+ result 
+--------
+ 
+(1 row)
+
+SELECT * from cypher('expr', $$
+    RETURN log10(0)
+$$) as (result agtype);
+ result 
+--------
+ 
+(1 row)
+
+SELECT * from cypher('expr', $$
+    RETURN log(-1)
+$$) as (result agtype);
+ result 
+--------
+ 
+(1 row)
+
+SELECT * from cypher('expr', $$
+    RETURN log10(-1)
+$$) as (result agtype);
+ result 
+--------
+ 
+(1 row)
+
+-- should fail
+SELECT * from cypher('expr', $$
+    RETURN log()
+$$) as (result agtype);
+ERROR:  invalid number of input parameters for log()
+LINE 1: SELECT * from cypher('expr', $$
+                                      ^
+SELECT * from cypher('expr', $$
+    RETURN log10()
+$$) as (result agtype);
+ERROR:  invalid number of input parameters for log10()
+LINE 1: SELECT * from cypher('expr', $$
+                                      ^
+--
 -- Cleanup
 --
 SELECT * FROM drop_graph('expr', true);
diff --git a/regress/sql/expr.sql b/regress/sql/expr.sql
index 9659d70..940161d 100644
--- a/regress/sql/expr.sql
+++ b/regress/sql/expr.sql
@@ -1646,6 +1646,42 @@ $$) AS cypher_2(result agtype)
 WHERE cypher_1.result = cypher_2.result;
 
 --
+-- log (ln) and log10
+--
+SELECT * from cypher('expr', $$
+    RETURN log(2.718281828459045)
+$$) as (result agtype);
+SELECT * from cypher('expr', $$
+    RETURN log10(10)
+$$) as (result agtype);
+-- should return null
+SELECT * from cypher('expr', $$
+    RETURN log(null)
+$$) as (result agtype);
+SELECT * from cypher('expr', $$
+    RETURN log10(null)
+$$) as (result agtype);
+SELECT * from cypher('expr', $$
+    RETURN log(0)
+$$) as (result agtype);
+SELECT * from cypher('expr', $$
+    RETURN log10(0)
+$$) as (result agtype);
+SELECT * from cypher('expr', $$
+    RETURN log(-1)
+$$) as (result agtype);
+SELECT * from cypher('expr', $$
+    RETURN log10(-1)
+$$) as (result agtype);
+-- should fail
+SELECT * from cypher('expr', $$
+    RETURN log()
+$$) as (result agtype);
+SELECT * from cypher('expr', $$
+    RETURN log10()
+$$) as (result agtype);
+
+--
 -- Cleanup
 --
 SELECT * FROM drop_graph('expr', true);
diff --git a/src/backend/parser/cypher_expr.c b/src/backend/parser/cypher_expr.c
index fdd6934..76ff5e4 100644
--- a/src/backend/parser/cypher_expr.c
+++ b/src/backend/parser/cypher_expr.c
@@ -87,6 +87,8 @@
 #define FUNC_ABS        {"abs",        "ag_abs",     ANYOID, 0, 0, AGTYPEOID, 
1, 1, false, false}
 #define FUNC_SIGN       {"sign",       "ag_sign",    ANYOID, 0, 0, AGTYPEOID, 
1, 1, false, false}
 #define FUNC_RAND       {"rand",       "random",     0,      0, 0, FLOAT8OID, 
0, 0, false, true}
+#define FUNC_LOG        {"log",        "ag_log",     ANYOID, 0, 0, AGTYPEOID, 
1, 1, false, false}
+#define FUNC_LOG10      {"log10",      "ag_log10",   ANYOID, 0, 0, AGTYPEOID, 
1, 1, false, false}
 
 /* supported functions */
 #define SUPPORTED_FUNCTIONS {FUNC_TYPE, FUNC_ENDNODE, FUNC_HEAD, FUNC_ID, \
@@ -101,7 +103,7 @@
                              FUNC_RCOT, FUNC_RASIN, FUNC_RACOS, FUNC_RATAN, \
                              FUNC_RATAN2, FUNC_PI, FUNC_DEGREES, FUNC_RADIANS, 
\
                              FUNC_ROUND, FUNC_CEIL, FUNC_FLOOR, FUNC_ABS, \
-                             FUNC_SIGN, FUNC_RAND}
+                             FUNC_SIGN, FUNC_RAND, FUNC_LOG, FUNC_LOG10}
 
 /* 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 1a85d49..217a9d2 100644
--- a/src/backend/utils/adt/agtype.c
+++ b/src/backend/utils/adt/agtype.c
@@ -6644,3 +6644,130 @@ Datum ag_sign(PG_FUNCTION_ARGS)
 
     PG_RETURN_POINTER(agtype_value_to_agtype(&agtv_result));
 }
+
+PG_FUNCTION_INFO_V1(ag_log);
+
+Datum ag_log(PG_FUNCTION_ARGS)
+{
+    int nargs;
+    Datum *args;
+    bool *nulls;
+    Oid *types;
+    agtype_value agtv_result;
+    Numeric arg;
+    Numeric zero;
+    Numeric numeric_result;
+    float8 float_result;
+    bool is_null = true;
+    int test;
+
+    /* 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("log() invalid number of arguments")));
+
+    /* check for a null input */
+    if (nargs < 0 || nulls[0])
+        PG_RETURN_NULL();
+
+    /*
+     * log() supports integer, float, and numeric or the agtype integer,
+     * float, and numeric for the input expression.
+     */
+    arg = get_numeric_compatible_arg(args[0], types[0], "log", &is_null, NULL);
+
+    /* check for a agtype null input */
+    if (is_null)
+        PG_RETURN_NULL();
+
+    /* get a numeric 0 as a datum to test <= 0 log args */
+    zero = DatumGetNumeric(DirectFunctionCall1(int8_numeric, 
Int64GetDatum(0)));
+
+    test = DatumGetInt32(DirectFunctionCall2(numeric_cmp, NumericGetDatum(arg),
+                                             NumericGetDatum(zero)));
+
+    /* return null if the argument is <= 0; these are invalid args for logs */
+    if (test <= 0)
+        PG_RETURN_NULL();
+
+    /* We need the input as a numeric so that we can pass it off to PG */
+    numeric_result = DatumGetNumeric(DirectFunctionCall1(numeric_ln,
+                                                         
NumericGetDatum(arg)));
+
+    float_result = 
DatumGetFloat8(DirectFunctionCall1(numeric_float8_no_overflow,
+                                                      
NumericGetDatum(numeric_result)));
+    /* build the result */
+    agtv_result.type = AGTV_FLOAT;
+    agtv_result.val.float_value = float_result;
+
+    PG_RETURN_POINTER(agtype_value_to_agtype(&agtv_result));
+}
+
+PG_FUNCTION_INFO_V1(ag_log10);
+
+Datum ag_log10(PG_FUNCTION_ARGS)
+{
+    int nargs;
+    Datum *args;
+    bool *nulls;
+    Oid *types;
+    agtype_value agtv_result;
+    Numeric arg;
+    Numeric zero;
+    Numeric numeric_result;
+    float8 float_result;
+    Datum base;
+    bool is_null = true;
+    int test;
+
+    /* 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("log() invalid number of arguments")));
+
+    /* check for a null input */
+    if (nargs < 0 || nulls[0])
+        PG_RETURN_NULL();
+
+    /*
+     * log10() supports integer, float, and numeric or the agtype integer,
+     * float, and numeric for the input expression.
+     */
+    arg = get_numeric_compatible_arg(args[0], types[0], "log10", &is_null, 
NULL);
+
+    /* check for a agtype null input */
+    if (is_null)
+        PG_RETURN_NULL();
+
+    /* get a numeric 0 as a datum to test <= 0 log args */
+    zero = DatumGetNumeric(DirectFunctionCall1(int8_numeric, 
Int64GetDatum(0)));
+
+    test = DatumGetInt32(DirectFunctionCall2(numeric_cmp, NumericGetDatum(arg),
+                                             NumericGetDatum(zero)));
+
+    /* return null if the argument is <= 0; these are invalid args for logs */
+    if (test <= 0)
+        PG_RETURN_NULL();
+
+    /* get a numeric 10 as a datum for the base */
+    base = DirectFunctionCall1(float8_numeric, Float8GetDatum(10.0));
+
+    /* We need the input as a numeric so that we can pass it off to PG */
+    numeric_result = DatumGetNumeric(DirectFunctionCall2(numeric_log, base,
+                                                         
NumericGetDatum(arg)));
+
+    float_result = 
DatumGetFloat8(DirectFunctionCall1(numeric_float8_no_overflow,
+                                                      
NumericGetDatum(numeric_result)));
+
+    /* build the result */
+    agtv_result.type = AGTV_FLOAT;
+    agtv_result.val.float_value = float_result;
+
+    PG_RETURN_POINTER(agtype_value_to_agtype(&agtv_result));
+}

Reply via email to