You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@age.apache.org by jg...@apache.org on 2020/09/15 02:12:37 UTC

[incubator-age] branch master updated: Add openCypher abs(), ceil(), floor(), round(), & sign() functions

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 023250d  Add openCypher abs(), ceil(), floor(), round(), & sign() functions
023250d is described below

commit 023250d3c6e2c0be4a91ab7ee75870a2e3be3ca4
Author: John Gemignani <jr...@gmail.com>
AuthorDate: Fri Sep 11 12:13:15 2020 -0700

    Add openCypher abs(), ceil(), floor(), round(), & sign() functions
    
    Added the openCypher functions abs, ceil, floor, round, & sign.
    
    Added regression tests.
---
 age--0.2.0.sql                   |  34 ++++
 regress/expected/expr.out        | 263 +++++++++++++++++++++++++++
 regress/sql/expr.sql             | 114 ++++++++++++
 src/backend/parser/cypher_expr.c |   9 +-
 src/backend/utils/adt/agtype.c   | 373 ++++++++++++++++++++++++++++++++++++++-
 5 files changed, 788 insertions(+), 5 deletions(-)

diff --git a/age--0.2.0.sql b/age--0.2.0.sql
index 11b106b..741d6c9 100644
--- a/age--0.2.0.sql
+++ b/age--0.2.0.sql
@@ -1103,6 +1103,40 @@ STABLE
 PARALLEL SAFE
 AS 'MODULE_PATHNAME';
 
+CREATE FUNCTION ag_round(variadic "any")
+RETURNS agtype
+LANGUAGE c
+STABLE
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE FUNCTION ag_ceil(variadic "any")
+RETURNS agtype
+LANGUAGE c
+STABLE
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE FUNCTION ag_floor(variadic "any")
+RETURNS agtype
+LANGUAGE c
+STABLE
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE FUNCTION ag_abs(variadic "any")
+RETURNS agtype
+LANGUAGE c
+STABLE
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE FUNCTION ag_sign(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 293acc7..3f97989 100644
--- a/regress/expected/expr.out
+++ b/regress/expected/expr.out
@@ -3570,6 +3570,269 @@ SELECT * FROM cypher('expr', $$
 $$) AS (results agtype);
 ERROR:  degrees() unsuppoted argument agtype 1
 --
+-- abs(), ceil(), floor(), & round()
+--
+SELECT * FROM cypher('expr', $$
+    RETURN abs(0)
+$$) AS (results agtype);
+ results 
+---------
+ 0
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+    RETURN abs(10)
+$$) AS (results agtype);
+ results 
+---------
+ 10
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+    RETURN abs(-10)
+$$) AS (results agtype);
+ results 
+---------
+ 10
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+    RETURN ceil(0)
+$$) AS (results agtype);
+ results 
+---------
+ 0.0
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+    RETURN ceil(1)
+$$) AS (results agtype);
+ results 
+---------
+ 1.0
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+    RETURN ceil(-1)
+$$) AS (results agtype);
+ results 
+---------
+ -1.0
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+    RETURN ceil(1.01)
+$$) AS (results agtype);
+ results 
+---------
+ 2.0
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+    RETURN ceil(-1.01)
+$$) AS (results agtype);
+ results 
+---------
+ -1.0
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+    RETURN floor(0)
+$$) AS (results agtype);
+ results 
+---------
+ 0.0
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+    RETURN floor(1)
+$$) AS (results agtype);
+ results 
+---------
+ 1.0
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+    RETURN floor(-1)
+$$) AS (results agtype);
+ results 
+---------
+ -1.0
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+    RETURN floor(1.01)
+$$) AS (results agtype);
+ results 
+---------
+ 1.0
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+    RETURN floor(-1.01)
+$$) AS (results agtype);
+ results 
+---------
+ -2.0
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+    RETURN round(0)
+$$) AS (results agtype);
+ results 
+---------
+ 0.0
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+    RETURN round(4.49999999)
+$$) AS (results agtype);
+ results 
+---------
+ 4.0
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+    RETURN round(4.5)
+$$) AS (results agtype);
+ results 
+---------
+ 5.0
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+    RETURN round(-4.49999999)
+$$) AS (results agtype);
+ results 
+---------
+ -4.0
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+    RETURN round(-4.5)
+$$) AS (results agtype);
+ results 
+---------
+ -5.0
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+    RETURN sign(10)
+$$) AS (results agtype);
+ results 
+---------
+ 1
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+    RETURN sign(-10)
+$$) AS (results agtype);
+ results 
+---------
+ -1
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+    RETURN sign(0)
+$$) AS (results agtype);
+ results 
+---------
+ 0
+(1 row)
+
+-- should return null
+SELECT * FROM cypher('expr', $$
+    RETURN abs(null)
+$$) AS (results agtype);
+ results 
+---------
+ 
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+    RETURN ceil(null)
+$$) AS (results agtype);
+ results 
+---------
+ 
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+    RETURN floor(null)
+$$) AS (results agtype);
+ results 
+---------
+ 
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+    RETURN round(null)
+$$) AS (results agtype);
+ results 
+---------
+ 
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+    RETURN sign(null)
+$$) AS (results agtype);
+ results 
+---------
+ 
+(1 row)
+
+-- should fail
+SELECT * FROM cypher('expr', $$
+    RETURN abs()
+$$) AS (results agtype);
+ERROR:  unrecognized or unsupported function
+LINE 1: SELECT * FROM cypher('expr', $$
+                                      ^
+SELECT * FROM cypher('expr', $$
+    RETURN ceil()
+$$) AS (results agtype);
+ERROR:  unrecognized or unsupported function
+LINE 1: SELECT * FROM cypher('expr', $$
+                                      ^
+SELECT * FROM cypher('expr', $$
+    RETURN floor()
+$$) AS (results agtype);
+ERROR:  unrecognized or unsupported function
+LINE 1: SELECT * FROM cypher('expr', $$
+                                      ^
+SELECT * FROM cypher('expr', $$
+    RETURN round()
+$$) AS (results agtype);
+ERROR:  unrecognized or unsupported function
+LINE 1: SELECT * FROM cypher('expr', $$
+                                      ^
+SELECT * FROM cypher('expr', $$
+    RETURN sign()
+$$) AS (results agtype);
+ERROR:  unrecognized or unsupported function
+LINE 1: SELECT * FROM cypher('expr', $$
+                                      ^
+SELECT * FROM cypher('expr', $$
+    RETURN abs("1")
+$$) AS (results agtype);
+ERROR:  abs() unsuppoted argument agtype 1
+SELECT * FROM cypher('expr', $$
+    RETURN ceil("1")
+$$) AS (results agtype);
+ERROR:  ceil() unsuppoted argument agtype 1
+SELECT * FROM cypher('expr', $$
+    RETURN floor("1")
+$$) AS (results agtype);
+ERROR:  floor() unsuppoted argument agtype 1
+SELECT * FROM cypher('expr', $$
+    RETURN round("1")
+$$) AS (results agtype);
+ERROR:  round() unsuppoted argument agtype 1
+SELECT * FROM cypher('expr', $$
+    RETURN sign("1")
+$$) AS (results agtype);
+ERROR:  sign() unsuppoted argument agtype 1
+--
 -- Cleanup
 --
 SELECT * FROM drop_graph('expr', true);
diff --git a/regress/sql/expr.sql b/regress/sql/expr.sql
index 1ce8182..d7f879a 100644
--- a/regress/sql/expr.sql
+++ b/regress/sql/expr.sql
@@ -1515,6 +1515,120 @@ SELECT * FROM cypher('expr', $$
 $$) AS (results agtype);
 
 --
+-- abs(), ceil(), floor(), & round()
+--
+SELECT * FROM cypher('expr', $$
+    RETURN abs(0)
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+    RETURN abs(10)
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+    RETURN abs(-10)
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+    RETURN ceil(0)
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+    RETURN ceil(1)
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+    RETURN ceil(-1)
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+    RETURN ceil(1.01)
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+    RETURN ceil(-1.01)
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+    RETURN floor(0)
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+    RETURN floor(1)
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+    RETURN floor(-1)
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+    RETURN floor(1.01)
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+    RETURN floor(-1.01)
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+    RETURN round(0)
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+    RETURN round(4.49999999)
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+    RETURN round(4.5)
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+    RETURN round(-4.49999999)
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+    RETURN round(-4.5)
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+    RETURN sign(10)
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+    RETURN sign(-10)
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+    RETURN sign(0)
+$$) AS (results agtype);
+-- should return null
+SELECT * FROM cypher('expr', $$
+    RETURN abs(null)
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+    RETURN ceil(null)
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+    RETURN floor(null)
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+    RETURN round(null)
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+    RETURN sign(null)
+$$) AS (results agtype);
+-- should fail
+SELECT * FROM cypher('expr', $$
+    RETURN abs()
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+    RETURN ceil()
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+    RETURN floor()
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+    RETURN round()
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+    RETURN sign()
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+    RETURN abs("1")
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+    RETURN ceil("1")
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+    RETURN floor("1")
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+    RETURN round("1")
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+    RETURN sign("1")
+$$) AS (results 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 feba68d..bb9974d 100644
--- a/src/backend/parser/cypher_expr.c
+++ b/src/backend/parser/cypher_expr.c
@@ -81,6 +81,11 @@
 #define FUNC_PI         {"pi",         "pi",         0,         0, 0, FLOAT8OID, 0, 0, false, true}
 #define FUNC_DEGREES    {"degrees",    "degrees_from_radians", ANYOID, 0, 0, AGTYPEOID, 1, 1, false, false}
 #define FUNC_RADIANS    {"radians",    "radians_from_degrees", ANYOID, 0, 0, AGTYPEOID, 1, 1, false, false}
+#define FUNC_ROUND      {"round",      "ag_round",   ANYOID, 0, 0, AGTYPEOID, 1, 1, false, false}
+#define FUNC_CEIL       {"ceil",       "ag_ceil",    ANYOID, 0, 0, AGTYPEOID, 1, 1, false, false}
+#define FUNC_FLOOR      {"floor",      "ag_floor",   ANYOID, 0, 0, AGTYPEOID, 1, 1, false, false}
+#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}
 
 /* supported functions */
 #define SUPPORTED_FUNCTIONS {FUNC_TYPE, FUNC_ENDNODE, FUNC_HEAD, FUNC_ID, \
@@ -93,7 +98,9 @@
                              FUNC_LSUBSTR, FUNC_BSUBSTR, FUNC_SPLIT, \
                              FUNC_REPLACE, FUNC_RSIN, FUNC_RCOS, FUNC_RTAN, \
                              FUNC_RCOT, FUNC_RASIN, FUNC_RACOS, FUNC_RATAN, \
-                             FUNC_RATAN2, FUNC_PI, FUNC_DEGREES, FUNC_RADIANS}
+                             FUNC_RATAN2, FUNC_PI, FUNC_DEGREES, FUNC_RADIANS, \
+                             FUNC_ROUND, FUNC_CEIL, FUNC_FLOOR, FUNC_ABS, \
+                             FUNC_SIGN}
 
 /* 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 9152e12..1a85d49 100644
--- a/src/backend/utils/adt/agtype.c
+++ b/src/backend/utils/adt/agtype.c
@@ -131,6 +131,9 @@ static Datum column_get_datum(TupleDesc tupdesc, HeapTuple tuple, int column,
 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);
+static Numeric get_numeric_compatible_arg(Datum arg, Oid type, char *funcname,
+                                       bool *is_null,
+                                       enum agtype_value_type *ag_type);
 
 PG_FUNCTION_INFO_V1(agtype_in);
 
@@ -5691,9 +5694,9 @@ Datum replace(PG_FUNCTION_ARGS)
  * 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.
+ * otherwise we would just use tofloat. It returns a 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)
@@ -5793,6 +5796,96 @@ static float8 get_float_compatible_arg(Datum arg, Oid type, char *funcname,
     return result;
 }
 
+/*
+ * Helper function to extract one numeric 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 numeric,
+ * otherwise we would just cast it to numeric. It returns a numeric on success
+ * or fails with a message stating the funcname that called it and a specific
+ * message stating the error.
+ */
+static Numeric get_numeric_compatible_arg(Datum arg, Oid type, char *funcname,
+                                          bool *is_null,
+                                          enum agtype_value_type *ag_type)
+{
+    Numeric result;
+
+    /* Assume the value is null. Although, this is only necessary for agtypes */
+    *is_null = true;
+
+    if (ag_type != NULL)
+        *ag_type = AGTV_NULL;
+
+    if (type != AGTYPEOID)
+    {
+        if (type == INT2OID)
+            result = DatumGetNumeric(DirectFunctionCall1(int2_numeric, arg));
+        else if (type == INT4OID)
+            result = DatumGetNumeric(DirectFunctionCall1(int4_numeric, arg));
+        else if (type == INT8OID)
+            result = DatumGetNumeric(DirectFunctionCall1(int8_numeric, arg));
+        else if (type == FLOAT4OID)
+            result = DatumGetNumeric(DirectFunctionCall1(float4_numeric, arg));
+        else if (type == FLOAT8OID)
+            result = DatumGetNumeric(DirectFunctionCall1(float8_numeric, arg));
+        else if (type == NUMERICOID)
+            result = DatumGetNumeric(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)
+        {
+            result = DatumGetNumeric(DirectFunctionCall1(
+                int8_numeric, Int64GetDatum(agtv_value->val.int_value)));
+            if (ag_type != NULL)
+                *ag_type = AGTV_INTEGER;
+        }
+        else if (agtv_value->type == AGTV_FLOAT)
+        {
+            result = DatumGetNumeric(DirectFunctionCall1(
+                float8_numeric, Float8GetDatum(agtv_value->val.float_value)));
+            if (ag_type != NULL)
+                *ag_type = AGTV_FLOAT;
+        }
+        else if (agtv_value->type == AGTV_NUMERIC)
+        {
+            result = agtv_value->val.numeric;
+            if (ag_type != NULL)
+                *ag_type = AGTV_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)
@@ -6216,7 +6309,6 @@ Datum degrees_from_radians(PG_FUNCTION_ARGS)
 
     angle_radians = get_float_compatible_arg(args[0], types[0], "degrees",
                                              &is_null);
-
     /* check for a agtype null input */
     if (is_null)
         PG_RETURN_NULL();
@@ -6279,3 +6371,276 @@ Datum radians_from_degrees(PG_FUNCTION_ARGS)
 
     PG_RETURN_POINTER(agtype_value_to_agtype(&agtv_result));
 }
+
+PG_FUNCTION_INFO_V1(ag_round);
+
+Datum ag_round(PG_FUNCTION_ARGS)
+{
+    int nargs;
+    Datum *args;
+    bool *nulls;
+    Oid *types;
+    agtype_value agtv_result;
+    Numeric arg;
+    Numeric numeric_result;
+    float8 float_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("round() invalid number of arguments")));
+
+    /* check for a null input */
+    if (nargs < 0 || nulls[0])
+        PG_RETURN_NULL();
+
+    /*
+     * round() 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], "round", &is_null,
+                                     NULL);
+
+    /* check for a agtype null input */
+    if (is_null)
+        PG_RETURN_NULL();
+
+    /* We need the input as a numeric so that we can pass it off to PG */
+    numeric_result = DatumGetNumeric(DirectFunctionCall2(numeric_round,
+                                                         NumericGetDatum(arg),
+                                                         Int32GetDatum(0)));
+
+    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_ceil);
+
+Datum ag_ceil(PG_FUNCTION_ARGS)
+{
+    int nargs;
+    Datum *args;
+    bool *nulls;
+    Oid *types;
+    agtype_value agtv_result;
+    Numeric arg;
+    Numeric numeric_result;
+    float8 float_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("ceil() invalid number of arguments")));
+
+    /* check for a null input */
+    if (nargs < 0 || nulls[0])
+        PG_RETURN_NULL();
+
+    /*
+     * ceil() 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], "ceil", &is_null, NULL);
+
+    /* check for a agtype null input */
+    if (is_null)
+        PG_RETURN_NULL();
+
+    /* We need the input as a numeric so that we can pass it off to PG */
+    numeric_result = DatumGetNumeric(DirectFunctionCall1(numeric_ceil,
+                                                         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_floor);
+
+Datum ag_floor(PG_FUNCTION_ARGS)
+{
+    int nargs;
+    Datum *args;
+    bool *nulls;
+    Oid *types;
+    agtype_value agtv_result;
+    Numeric arg;
+    Numeric numeric_result;
+    float8 float_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("floor() invalid number of arguments")));
+
+    /* check for a null input */
+    if (nargs < 0 || nulls[0])
+        PG_RETURN_NULL();
+
+    /*
+     * floor() 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], "floor", &is_null,
+                                     NULL);
+
+    /* check for a agtype null input */
+    if (is_null)
+        PG_RETURN_NULL();
+
+    /* We need the input as a numeric so that we can pass it off to PG */
+    numeric_result = DatumGetNumeric(DirectFunctionCall1(numeric_floor,
+                                                         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_abs);
+
+Datum ag_abs(PG_FUNCTION_ARGS)
+{
+    int nargs;
+    Datum *args;
+    bool *nulls;
+    Oid *types;
+    agtype_value agtv_result;
+    Numeric arg;
+    Numeric numeric_result;
+    bool is_null = true;
+    enum agtype_value_type type = AGTV_NULL;
+
+    /* 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("abs() invalid number of arguments")));
+
+    /* check for a null input */
+    if (nargs < 0 || nulls[0])
+        PG_RETURN_NULL();
+
+    /*
+     * abs() 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], "abs", &is_null, &type);
+
+    /* check for a agtype null input */
+    if (is_null)
+        PG_RETURN_NULL();
+
+    /* We need the input as a numeric so that we can pass it off to PG */
+    numeric_result = DatumGetNumeric(DirectFunctionCall1(numeric_abs,
+                                                         NumericGetDatum(arg)));
+
+    /* build the result, based on the type */
+    if (types[0] == INT2OID || types[0] == INT4OID || types[0] == INT8OID ||
+        (types[0] == AGTYPEOID && type == AGTV_INTEGER))
+    {
+        int64 int_result;
+
+        int_result = DatumGetInt64(DirectFunctionCall1(numeric_int8,
+                                                       NumericGetDatum(numeric_result)));
+
+        agtv_result.type = AGTV_INTEGER;
+        agtv_result.val.int_value = int_result;
+    }
+    if (types[0] == FLOAT4OID || types[0] == FLOAT8OID ||
+        (types[0] == AGTYPEOID && type == AGTV_FLOAT))
+    {
+        float8 float_result;
+
+        float_result = DatumGetFloat8(DirectFunctionCall1(numeric_float8_no_overflow,
+                           NumericGetDatum(numeric_result)));
+
+        agtv_result.type = AGTV_FLOAT;
+        agtv_result.val.float_value = float_result;
+    }
+    if (types[0] == NUMERICOID ||
+        (types[0] == AGTYPEOID && type == AGTV_NUMERIC))
+    {
+        agtv_result.type = AGTV_NUMERIC;
+        agtv_result.val.numeric = numeric_result;
+    }
+
+    PG_RETURN_POINTER(agtype_value_to_agtype(&agtv_result));
+}
+
+PG_FUNCTION_INFO_V1(ag_sign);
+
+Datum ag_sign(PG_FUNCTION_ARGS)
+{
+    int nargs;
+    Datum *args;
+    bool *nulls;
+    Oid *types;
+    agtype_value agtv_result;
+    Numeric arg;
+    Numeric numeric_result;
+    int int_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("sign() invalid number of arguments")));
+
+    /* check for a null input */
+    if (nargs < 0 || nulls[0])
+        PG_RETURN_NULL();
+
+    /*
+     * sign() 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], "sign", &is_null, NULL);
+
+    /* check for a agtype null input */
+    if (is_null)
+        PG_RETURN_NULL();
+
+    /* We need the input as a numeric so that we can pass it off to PG */
+    numeric_result = DatumGetNumeric(DirectFunctionCall1(numeric_sign,
+                                                         NumericGetDatum(arg)));
+
+    int_result = DatumGetInt64(DirectFunctionCall1(numeric_int8,
+                                                   NumericGetDatum(numeric_result)));
+
+    /* build the result */
+    agtv_result.type = AGTV_INTEGER;
+    agtv_result.val.int_value = int_result;
+
+    PG_RETURN_POINTER(agtype_value_to_agtype(&agtv_result));
+}