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 2021/04/06 23:02:45 UTC

[incubator-age] branch master updated: Change input types for some aggregates

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 12e3b2f  Change input types for some aggregates
12e3b2f is described below

commit 12e3b2f524181a85a13bf8dd0f28b58669bb98ac
Author: John Gemignani <jr...@gmail.com>
AuthorDate: Tue Apr 6 13:49:57 2021 -0700

    Change input types for some aggregates
    
    Changed the input for the following aggregate functions to
    agtype -
    
        stddev, stddevp, avg, sum, percentilecont, and
        percentildisc.
    
    Adjusted the regression tests to use casts to PG floats, where
    necessary. This is to allow the removal of the implicit cast to
    float8 from agtype.
    
    Adjusted the regression tests to reflect the small changes for some
    of the output using the above adjusted functions.
---
 age--0.3.0.sql                   | 102 +++++++++++++----
 regress/expected/expr.out        |  34 +++---
 regress/sql/expr.sql             |  10 +-
 src/backend/parser/cypher_gram.y |   4 -
 src/backend/utils/adt/agtype.c   | 242 ++++++++++++++++++++++++++++++++++++---
 5 files changed, 330 insertions(+), 62 deletions(-)

diff --git a/age--0.3.0.sql b/age--0.3.0.sql
index c35d6d3..b336eb9 100644
--- a/age--0.3.0.sql
+++ b/age--0.3.0.sql
@@ -1442,39 +1442,51 @@ PARALLEL SAFE
 AS 'MODULE_PATHNAME';
 
 --
--- aggregate functions components for stdev & stdevp
+-- aggregate function components for stdev(internal, agtype)
+-- and stdevp(internal, agtype)
 --
-
 -- wrapper for the stdev final function to pass 0 instead of null
 CREATE FUNCTION ag_catalog.age_float8_stddev_samp_aggfinalfn(_float8)
-RETURNS float8
+RETURNS agtype
 LANGUAGE c
 IMMUTABLE
 PARALLEL SAFE
 AS 'MODULE_PATHNAME';
--- aggregate for age_stdev
-CREATE AGGREGATE ag_catalog.age_stdev(float8)
+
+-- wrapper for the float8_accum to use agtype input
+CREATE FUNCTION ag_catalog.age_agtype_float8_accum(_float8, agtype)
+RETURNS _float8
+LANGUAGE c
+IMMUTABLE
+STRICT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+-- aggregate definition for age_stdev(agtype)
+CREATE AGGREGATE ag_catalog.age_stdev(agtype)
 (
    stype = _float8,
-   sfunc = float8_accum,
+   sfunc = ag_catalog.age_agtype_float8_accum,
    finalfunc = ag_catalog.age_float8_stddev_samp_aggfinalfn,
    combinefunc = float8_combine,
    finalfunc_modify = read_only,
    initcond = '{0,0,0}',
    parallel = safe
 );
+
 -- wrapper for the stdevp final function to pass 0 instead of null
 CREATE FUNCTION ag_catalog.age_float8_stddev_pop_aggfinalfn(_float8)
-RETURNS float8
+RETURNS agtype
 LANGUAGE c
 IMMUTABLE
 PARALLEL SAFE
 AS 'MODULE_PATHNAME';
--- aggregate for age_stdevp
-CREATE AGGREGATE ag_catalog.age_stdevp (float8)
+
+-- aggregate definition for age_stdevp(agtype)
+CREATE AGGREGATE ag_catalog.age_stdevp(agtype)
 (
    stype = _float8,
-   sfunc = float8_accum,
+   sfunc = age_agtype_float8_accum,
    finalfunc = ag_catalog.age_float8_stddev_pop_aggfinalfn,
    combinefunc = float8_combine,
    finalfunc_modify = read_only,
@@ -1483,16 +1495,51 @@ CREATE AGGREGATE ag_catalog.age_stdevp (float8)
 );
 
 --
--- aggregate transfer functions for min & max
+-- aggregate function components for avg(agtype) and sum(agtype)
+--
+-- aggregate definition for avg(agytpe)
+CREATE AGGREGATE ag_catalog.age_avg(agtype)
+(
+   stype = _float8,
+   sfunc = ag_catalog.age_agtype_float8_accum,
+   finalfunc = float8_avg,
+   combinefunc = float8_combine,
+   finalfunc_modify = read_only,
+   initcond = '{0,0,0}',
+   parallel = safe
+);
+
+-- sum aggtransfn
+CREATE FUNCTION ag_catalog.age_agtype_sum(agtype, agtype)
+RETURNS agtype
+LANGUAGE c
+IMMUTABLE
+STRICT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+-- aggregate definition for sum(agytpe)
+CREATE AGGREGATE ag_catalog.age_sum(agtype)
+(
+   stype = agtype,
+   sfunc = ag_catalog.age_agtype_sum,
+   combinefunc = ag_catalog.age_agtype_sum,
+   finalfunc_modify = read_only,
+   parallel = safe
+);
+
+--
+-- aggregate functions for min(variadic "any") and max(variadic "any")
 --
--- max
+-- max transfer function
 CREATE FUNCTION ag_catalog.age_agtype_larger_aggtransfn(agtype, variadic "any")
 RETURNS agtype
 LANGUAGE c
 IMMUTABLE
 PARALLEL SAFE
 AS 'MODULE_PATHNAME';
--- aggregate for max
+
+-- aggregate definition for max(variadic "any")
 CREATE AGGREGATE ag_catalog.age_max(variadic "any")
 (
    stype = agtype,
@@ -1501,14 +1548,16 @@ CREATE AGGREGATE ag_catalog.age_max(variadic "any")
    finalfunc_modify = read_only,
    parallel = safe
 );
--- min
+
+-- min transfer function
 CREATE FUNCTION ag_catalog.age_agtype_smaller_aggtransfn(agtype, variadic "any")
 RETURNS agtype
 LANGUAGE c
 IMMUTABLE
 PARALLEL SAFE
 AS 'MODULE_PATHNAME';
--- aggregate for min
+
+-- aggregate definition for min(variadic "any")
 CREATE AGGREGATE ag_catalog.age_min(variadic "any")
 (
    stype = agtype,
@@ -1519,30 +1568,35 @@ CREATE AGGREGATE ag_catalog.age_min(variadic "any")
 );
 
 --
--- aggregate transfer/final functions for percentileCont & percentileDisc
+-- aggregate functions percentileCont(internal, agtype) and
+-- percentileDisc(internal, agtype)
 --
-CREATE FUNCTION ag_catalog.age_percentile_aggtransfn(internal, float8, float8)
+-- percentile transfer function
+CREATE FUNCTION ag_catalog.age_percentile_aggtransfn(internal, agtype, agtype)
 RETURNS internal
 LANGUAGE c
 IMMUTABLE
 PARALLEL SAFE
 AS 'MODULE_PATHNAME';
 
+-- percentile_cont final function
 CREATE FUNCTION ag_catalog.age_percentile_cont_aggfinalfn(internal)
-RETURNS float8
+RETURNS agtype
 LANGUAGE c
 IMMUTABLE
 PARALLEL SAFE
 AS 'MODULE_PATHNAME';
 
+-- percentile_disc final function
 CREATE FUNCTION ag_catalog.age_percentile_disc_aggfinalfn(internal)
-RETURNS float8
+RETURNS agtype
 LANGUAGE c
 IMMUTABLE
 PARALLEL SAFE
 AS 'MODULE_PATHNAME';
 
-CREATE AGGREGATE ag_catalog.age_percentilecont(float8, float8)
+-- aggregate definition for _percentilecont(agtype, agytpe)
+CREATE AGGREGATE ag_catalog.age_percentilecont(agtype, agtype)
 (
     stype = internal,
     sfunc = ag_catalog.age_percentile_aggtransfn,
@@ -1550,7 +1604,8 @@ CREATE AGGREGATE ag_catalog.age_percentilecont(float8, float8)
     parallel = safe
 );
 
-CREATE AGGREGATE ag_catalog.age_percentiledisc(float8, float8)
+-- aggregate definition for percentiledisc(agtype, agytpe)
+CREATE AGGREGATE ag_catalog.age_percentiledisc(agtype, agtype)
 (
     stype = internal,
     sfunc = ag_catalog.age_percentile_aggtransfn,
@@ -1559,8 +1614,9 @@ CREATE AGGREGATE ag_catalog.age_percentiledisc(float8, float8)
 );
 
 --
--- aggregate transfer/final functions for collect
+-- aggregate functions for collect(variadic "any")
 --
+-- collect transfer function
 CREATE FUNCTION ag_catalog.age_collect_aggtransfn(internal, variadic "any")
 RETURNS internal
 LANGUAGE c
@@ -1568,6 +1624,7 @@ IMMUTABLE
 PARALLEL SAFE
 AS 'MODULE_PATHNAME';
 
+-- collect final function
 CREATE FUNCTION ag_catalog.age_collect_aggfinalfn(internal)
 RETURNS agtype
 LANGUAGE c
@@ -1575,6 +1632,7 @@ IMMUTABLE
 PARALLEL SAFE
 AS 'MODULE_PATHNAME';
 
+-- aggregate definition for age_collect(variadic "any")
 CREATE AGGREGATE ag_catalog.age_collect(variadic "any")
 (
     stype = internal,
diff --git a/regress/expected/expr.out b/regress/expected/expr.out
index a9f0c5a..9ad76eb 100644
--- a/regress/expected/expr.out
+++ b/regress/expected/expr.out
@@ -4359,7 +4359,7 @@ ERROR:  sqrt() unsupported argument agtype 1
 -- user defined function expressions - using pg functions for these tests
 --
 SELECT * from cypher('expr', $$
-    RETURN pg_catalog.sqrt(25)
+    RETURN pg_catalog.sqrt(25::pg_float8)
 $$) as (result agtype);
  result 
 --------
@@ -4376,7 +4376,7 @@ $$) as (result agtype);
 
 -- should return null
 SELECT * from cypher('expr', $$
-    RETURN pg_catalog.sqrt(null)
+    RETURN pg_catalog.sqrt(null::pg_float8)
 $$) as (result agtype);
  result 
 --------
@@ -4392,18 +4392,18 @@ LINE 2:     RETURN pg_catalog.sqrt()
                               ^
 HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 SELECT * from cypher('expr', $$
-    RETURN pg_catalog.sqrt("1")
+    RETURN pg_catalog.sqrt("1"::pg_float8)
 $$) as (result agtype);
 ERROR:  cannot cast agtype string to type float
 SELECT * from cypher('expr', $$
-    RETURN pg_catalog.sqrt(-1)
+    RETURN pg_catalog.sqrt(-1::pg_float8)
 $$) as (result agtype);
 ERROR:  cannot take square root of a negative number
 SELECT * from cypher('expr', $$
-    RETURN something.pg_catalog.sqrt("1")
+    RETURN something.pg_catalog.sqrt("1"::pg_float8)
 $$) as (result agtype);
 ERROR:  invalid indirection syntax
-LINE 2:     RETURN something.pg_catalog.sqrt("1")
+LINE 2:     RETURN something.pg_catalog.sqrt("1"::pg_float8)
                    ^
 -- should fail do to schema but using a reserved_keyword
 SELECT * from cypher('expr', $$
@@ -4477,9 +4477,9 @@ SELECT * FROM cypher('UCSC', $$ MATCH (u) RETURN (u) $$) AS (vertex agtype);
 
 SELECT * FROM cypher('UCSC', $$ MATCH (u) RETURN avg(u.gpa), sum(u.gpa), sum(u.gpa)/count(u.gpa), count(u.gpa), count(*) $$) 
 AS (avg agtype, sum agtype, sum_divided_by_count agtype, count agtype, count_star agtype);
-       avg        |  sum  | sum_divided_by_count | count | count_star 
-------------------+-------+----------------------+-------+------------
- 3.49285714285714 | 24.45 | 3.49285714285714     | 7     | 7
+       avg        |      sum       |    sum_divided_by_count     | count | count_star 
+------------------+----------------+-----------------------------+-------+------------
+ 3.49285714285714 | 24.45::numeric | 3.4928571428571429::numeric | 7     | 7
 (1 row)
 
 -- add in 2 null gpa records
@@ -4509,9 +4509,9 @@ SELECT * FROM cypher('UCSC', $$ MATCH (u) RETURN (u) $$) AS (vertex agtype);
 
 SELECT * FROM cypher('UCSC', $$ MATCH (u) RETURN avg(u.gpa), sum(u.gpa), sum(u.gpa)/count(u.gpa), count(u.gpa), count(*) $$) 
 AS (avg agtype, sum agtype, sum_divided_by_count agtype, count agtype, count_star agtype);
-       avg        |  sum  | sum_divided_by_count | count | count_star 
-------------------+-------+----------------------+-------+------------
- 3.49285714285714 | 24.45 | 3.49285714285714     | 7     | 9
+       avg        |      sum       |    sum_divided_by_count     | count | count_star 
+------------------+----------------+-----------------------------+-------+------------
+ 3.49285714285714 | 24.45::numeric | 3.4928571428571429::numeric | 7     | 9
 (1 row)
 
 -- should return null
@@ -4536,12 +4536,12 @@ SELECT * FROM cypher('UCSC', $$ RETURN count(NULL) $$) AS (count agtype);
 
 -- should fail
 SELECT * FROM cypher('UCSC', $$ RETURN avg() $$) AS (avg agtype);
-ERROR:  function pg_catalog.avg() does not exist
+ERROR:  function ag_catalog.age_avg() does not exist
 LINE 1: SELECT * FROM cypher('UCSC', $$ RETURN avg() $$) AS (avg agt...
                                                ^
 HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 SELECT * FROM cypher('UCSC', $$ RETURN sum() $$) AS (sum agtype);
-ERROR:  function pg_catalog.sum() does not exist
+ERROR:  function ag_catalog.age_sum() does not exist
 LINE 1: SELECT * FROM cypher('UCSC', $$ RETURN sum() $$) AS (sum agt...
                                                ^
 HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
@@ -4861,9 +4861,9 @@ SELECT * FROM cypher('group_by', $$MATCH (u:row) RETURN u.i, u.j, u.k$$) AS (i a
 SELECT * FROM cypher('group_by', $$MATCH (u:row) RETURN u.i, u.j, sum(u.k)$$) AS (i agtype, j agtype, sumk agtype);
  i | j | sumk 
 ---+---+------
- 1 | 2 | 7.0
- 2 | 3 | 6.0
- 1 | 3 | 5.0
+ 1 | 2 | 7
+ 2 | 3 | 6
+ 1 | 3 | 5
 (3 rows)
 
 SELECT * FROM cypher('group_by', $$CREATE (:L {a: 1, b: 2, c:3})$$) AS (result agtype);
diff --git a/regress/sql/expr.sql b/regress/sql/expr.sql
index 51d3eed..85f8403 100644
--- a/regress/sql/expr.sql
+++ b/regress/sql/expr.sql
@@ -1888,27 +1888,27 @@ $$) as (result agtype);
 -- user defined function expressions - using pg functions for these tests
 --
 SELECT * from cypher('expr', $$
-    RETURN pg_catalog.sqrt(25)
+    RETURN pg_catalog.sqrt(25::pg_float8)
 $$) as (result agtype);
 SELECT * from cypher('expr', $$
     RETURN ag_catalog.age_sqrt(25)
 $$) as (result agtype);
 -- should return null
 SELECT * from cypher('expr', $$
-    RETURN pg_catalog.sqrt(null)
+    RETURN pg_catalog.sqrt(null::pg_float8)
 $$) as (result agtype);
 -- should fail
 SELECT * from cypher('expr', $$
     RETURN pg_catalog.sqrt()
 $$) as (result agtype);
 SELECT * from cypher('expr', $$
-    RETURN pg_catalog.sqrt("1")
+    RETURN pg_catalog.sqrt("1"::pg_float8)
 $$) as (result agtype);
 SELECT * from cypher('expr', $$
-    RETURN pg_catalog.sqrt(-1)
+    RETURN pg_catalog.sqrt(-1::pg_float8)
 $$) as (result agtype);
 SELECT * from cypher('expr', $$
-    RETURN something.pg_catalog.sqrt("1")
+    RETURN something.pg_catalog.sqrt("1"::pg_float8)
 $$) as (result agtype);
 -- should fail do to schema but using a reserved_keyword
 SELECT * from cypher('expr', $$
diff --git a/src/backend/parser/cypher_gram.y b/src/backend/parser/cypher_gram.y
index 1db0fbd..9990965 100644
--- a/src/backend/parser/cypher_gram.y
+++ b/src/backend/parser/cypher_gram.y
@@ -1690,10 +1690,6 @@ static Node *make_function_expr(List *func_name, List *exprs, int location)
             funcname = SystemFuncName("random");
         else if (pg_strcasecmp(name, "pi") == 0)
             funcname = SystemFuncName("pi");
-        else if (pg_strcasecmp(name, "avg") == 0)
-            funcname = SystemFuncName("avg");
-        else if (pg_strcasecmp(name, "sum") == 0)
-            funcname = SystemFuncName("sum");
         else if (pg_strcasecmp(name, "count") == 0)
             funcname = SystemFuncName("count");
         else
diff --git a/src/backend/utils/adt/agtype.c b/src/backend/utils/adt/agtype.c
index b6ac788..35d4ad5 100644
--- a/src/backend/utils/adt/agtype.c
+++ b/src/backend/utils/adt/agtype.c
@@ -6076,7 +6076,7 @@ static float8 get_float_compatible_arg(Datum arg, Oid type, char *funcname,
             result = float8in_internal_null(string, NULL, "double precision",
                                             string, &is_valid);
 
-            /* return null if it was not a invalid float */
+            /* return 0 if it was an invalid float */
             if (!is_valid)
                 return 0;
         }
@@ -7431,29 +7431,222 @@ agtype *get_one_agtype_from_variadic_args(FunctionCallInfo fcinfo,
     return agtype_result;
 }
 
+/*
+ * Transfer function for age_sum(agtype, agtype).
+ *
+ * Note: that the running sum will change type depending on the
+ * precision of the input. The most precise value determines the
+ * result type.
+ *
+ * Note: The sql definition is STRICT so no input NULLs need to
+ * be dealt with except for agtype.
+ */
+PG_FUNCTION_INFO_V1(age_agtype_sum);
+
+Datum age_agtype_sum(PG_FUNCTION_ARGS)
+{
+    agtype *agt_arg0 = AG_GET_ARG_AGTYPE_P(0);
+    agtype *agt_arg1 = AG_GET_ARG_AGTYPE_P(1);
+    agtype_value *agtv_lhs;
+    agtype_value *agtv_rhs;
+    agtype_value agtv_result;
+
+    /* get our args */
+    agt_arg0 = AG_GET_ARG_AGTYPE_P(0);
+    agt_arg1 = AG_GET_ARG_AGTYPE_P(1);
+
+    /* only scalars are allowed */
+    if (!AGT_ROOT_IS_SCALAR(agt_arg0) || !AGT_ROOT_IS_SCALAR(agt_arg1))
+        ereport(ERROR,
+                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                 errmsg("arguments must resolve to a scalar")));
+
+    /* get the values */
+    agtv_lhs = get_ith_agtype_value_from_container(&agt_arg0->root, 0);
+    agtv_rhs = get_ith_agtype_value_from_container(&agt_arg1->root, 0);
+
+    /* only numbers are allowed */
+    if ((agtv_lhs->type != AGTV_INTEGER && agtv_lhs->type != AGTV_FLOAT &&
+         agtv_lhs->type != AGTV_NUMERIC) || (agtv_rhs->type != AGTV_INTEGER &&
+        agtv_rhs->type != AGTV_FLOAT && agtv_rhs->type != AGTV_NUMERIC))
+        ereport(ERROR,
+                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                 errmsg("arguments must resolve to a number")));
+
+    /* check for agtype null */
+    if (agtv_lhs->type == AGTV_NULL)
+        PG_RETURN_POINTER(agt_arg1);
+    if (agtv_rhs->type == AGTV_NULL)
+        PG_RETURN_POINTER(agt_arg0);
+
+    /* we want to maintain the precision of the most precise input */
+    if (agtv_lhs->type == AGTV_NUMERIC || agtv_rhs->type == AGTV_NUMERIC)
+    {
+        agtv_result.type = AGTV_NUMERIC;
+    }
+    else if (agtv_lhs->type == AGTV_FLOAT || agtv_rhs->type == AGTV_FLOAT)
+    {
+        agtv_result.type = AGTV_FLOAT;
+    }
+    else
+    {
+        agtv_result.type = AGTV_INTEGER;
+    }
+
+    /* switch on the type to perform the correct addition */
+    switch(agtv_result.type)
+    {
+        /* if the type is integer, they are obviously both ints */
+        case AGTV_INTEGER:
+            agtv_result.val.int_value = DatumGetInt64(
+                DirectFunctionCall2(int8pl,
+                                    Int64GetDatum(agtv_lhs->val.int_value),
+                                    Int64GetDatum(agtv_rhs->val.int_value)));
+            break;
+        /* for float it can be either, float + float or float + int */
+        case AGTV_FLOAT:
+        {
+            Datum dfl;
+            Datum dfr;
+            Datum dresult;
+            /* extract and convert the values as necessary */
+            /* float + float */
+            if (agtv_lhs->type == AGTV_FLOAT && agtv_rhs->type == AGTV_FLOAT)
+            {
+                dfl = Float8GetDatum(agtv_lhs->val.float_value);
+                dfr = Float8GetDatum(agtv_rhs->val.float_value);
+            }
+            /* float + int */
+            else
+            {
+                int64 ival;
+                float8 fval;
+                bool is_null;
+
+                ival = (agtv_lhs->type == AGTV_INTEGER) ?
+                    agtv_lhs->val.int_value : agtv_rhs->val.int_value;
+                fval = (agtv_lhs->type == AGTV_FLOAT) ?
+                    agtv_lhs->val.float_value : agtv_rhs->val.float_value;
+
+                dfl = Float8GetDatum(get_float_compatible_arg(Int64GetDatum(ival),
+                                                              INT8OID, "",
+                                                              &is_null));
+                dfr = Float8GetDatum(fval);
+            }
+            /* add the floats and set the result */
+            dresult = DirectFunctionCall2(float8pl, dfl, dfr);
+            agtv_result.val.float_value = DatumGetFloat8(dresult);
+        }
+            break;
+        /*
+         * For numeric it can be either, numeric + numeric or numeric + float or
+         * numeric + int
+         */
+        case AGTV_NUMERIC:
+        {
+            Datum dnl;
+            Datum dnr;
+            Datum dresult;
+            /* extract and convert the values as necessary */
+            /* numeric + numeric */
+            if (agtv_lhs->type == AGTV_NUMERIC && agtv_rhs->type == AGTV_NUMERIC)
+            {
+                dnl = NumericGetDatum(agtv_lhs->val.numeric);
+                dnr = NumericGetDatum(agtv_rhs->val.numeric);
+            }
+            /* numeric + float */
+            else if (agtv_lhs->type == AGTV_FLOAT || agtv_rhs->type == AGTV_FLOAT)
+            {
+                float8 fval;
+                Numeric nval;
+
+                fval = (agtv_lhs->type == AGTV_FLOAT) ?
+                    agtv_lhs->val.float_value : agtv_rhs->val.float_value;
+                nval = (agtv_lhs->type == AGTV_NUMERIC) ?
+                    agtv_lhs->val.numeric : agtv_rhs->val.numeric;
+
+                dnl = DirectFunctionCall1(float8_numeric, Float8GetDatum(fval));
+                dnr = NumericGetDatum(nval);
+            }
+            /* numeric + int */
+            else
+            {
+                int64 ival;
+                Numeric nval;
+
+                ival = (agtv_lhs->type == AGTV_INTEGER) ?
+                    agtv_lhs->val.int_value : agtv_rhs->val.int_value;
+                nval = (agtv_lhs->type == AGTV_NUMERIC) ?
+                    agtv_lhs->val.numeric : agtv_rhs->val.numeric;
+
+                dnl = DirectFunctionCall1(int8_numeric, Int64GetDatum(ival));
+                dnr = NumericGetDatum(nval);
+            }
+            /* add the numerics and set the result */
+            dresult = DirectFunctionCall2(numeric_add, dnl, dnr);
+            agtv_result.val.numeric = DatumGetNumeric(dresult);
+        }
+            break;
+
+        default:
+            elog(ERROR, "unexpected agtype");
+            break;
+    }
+    /* return the result */
+    PG_RETURN_POINTER(agtype_value_to_agtype(&agtv_result));
+}
+
+/*
+ * Wrapper function for float8_accum to take an agtype input.
+ * This function is defined as STRICT so it does not need to check
+ * for NULL input parameters
+ */
+PG_FUNCTION_INFO_V1(age_agtype_float8_accum);
+
+Datum age_agtype_float8_accum(PG_FUNCTION_ARGS)
+{
+    Datum dfloat;
+    Datum result;
+
+    /* convert to a float8 datum, if possible */
+    dfloat = DirectFunctionCall1(agtype_to_float8, PG_GETARG_DATUM(1));
+    /* pass the arguments off to float8_accum */
+    result = DirectFunctionCall2(float8_accum, PG_GETARG_DATUM(0), dfloat);
+
+    PG_RETURN_DATUM(result);
+}
+
+/* Wrapper for stdDev function. */
 PG_FUNCTION_INFO_V1(age_float8_stddev_samp_aggfinalfn);
 
 Datum age_float8_stddev_samp_aggfinalfn(PG_FUNCTION_ARGS)
 {
     Datum result;
     PGFunction func;
+    agtype_value agtv_float;
 
     /* we can't use DirectFunctionCall1 as it errors for NULL values */
     func = float8_stddev_samp;
     result = (*func) (fcinfo);
 
+    agtv_float.type = AGTV_FLOAT;
+
     /*
      * Check to see if float8_stddev_samp returned null. If so, we need to
-     * return a float8 0.
+     * return a agtype float 0.
      */
     if (fcinfo->isnull)
     {
+        /* we need to clear the flag */
         fcinfo->isnull = false;
-
-        PG_RETURN_FLOAT8(0.0);
+        agtv_float.val.float_value = 0.0;
+    }
+    else
+    {
+        agtv_float.val.float_value = DatumGetFloat8(result);
     }
 
-    return result;
+    PG_RETURN_POINTER(agtype_value_to_agtype(&agtv_float));
 }
 
 PG_FUNCTION_INFO_V1(age_float8_stddev_pop_aggfinalfn);
@@ -7462,23 +7655,30 @@ Datum age_float8_stddev_pop_aggfinalfn(PG_FUNCTION_ARGS)
 {
     Datum result;
     PGFunction func;
+    agtype_value agtv_float;
 
     /* we can't use DirectFunctionCall1 as it errors for NULL values */
     func = float8_stddev_pop;
     result = (*func) (fcinfo);
 
+    agtv_float.type = AGTV_FLOAT;
+
     /*
      * Check to see if float8_stddev_pop returned null. If so, we need to
-     * return a float8 0.
+     * return a agtype float 0.
      */
     if (fcinfo->isnull)
     {
+        /* we need to clear the flag */
         fcinfo->isnull = false;
-
-        PG_RETURN_FLOAT8(0.0);
+        agtv_float.val.float_value = 0.0;
+    }
+    else
+    {
+        agtv_float.val.float_value = DatumGetFloat8(result);
     }
 
-    return result;
+    PG_RETURN_POINTER(agtype_value_to_agtype(&agtv_float));
 }
 
 PG_FUNCTION_INFO_V1(age_agtype_larger_aggtransfn);
@@ -7575,7 +7775,9 @@ Datum age_percentile_aggtransfn(PG_FUNCTION_ARGS)
             ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                 errmsg("percentile value NULL is not a valid numeric value")));
 
-        percentile = PG_GETARG_FLOAT8(2);
+        percentile = DatumGetFloat8(DirectFunctionCall1(agtype_to_float8,
+                         PG_GETARG_DATUM(2)));
+
         if (percentile < 0 || percentile > 1 || isnan(percentile))
         ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                         errmsg("percentile value %g is not between 0 and 1",
@@ -7609,7 +7811,9 @@ Datum age_percentile_aggtransfn(PG_FUNCTION_ARGS)
     /* Load the datum into the tuplesort object, but only if it's not null */
     if (!PG_ARGISNULL(1))
     {
-        tuplesort_putdatum(pgastate->sortstate, PG_GETARG_DATUM(1), false);
+        Datum dfloat = DirectFunctionCall1(agtype_to_float8, PG_GETARG_DATUM(1));
+
+        tuplesort_putdatum(pgastate->sortstate, dfloat, false);
         pgastate->number_of_rows++;
     }
     /* return the state */
@@ -7630,6 +7834,7 @@ Datum age_percentile_cont_aggfinalfn(PG_FUNCTION_ARGS)
     Datum second_val;
     double proportion;
     bool isnull;
+    agtype_value agtv_float;
 
     /* verify we are in an aggregate context */
     Assert(AggCheckCallContext(fcinfo, NULL) == AGG_CONTEXT_AGGREGATE);
@@ -7685,7 +7890,11 @@ Datum age_percentile_cont_aggfinalfn(PG_FUNCTION_ARGS)
         val = float8_lerp(first_val, second_val, proportion);
     }
 
-    PG_RETURN_DATUM(val);
+    /* convert to an agtype float and return the result */
+    agtv_float.type = AGTV_FLOAT;
+    agtv_float.val.float_value = DatumGetFloat8(val);
+
+    PG_RETURN_POINTER(agtype_value_to_agtype(&agtv_float));
 }
 
 /* Code borrowed and adjusted from PG's percentile_disc_final function */
@@ -7698,6 +7907,7 @@ Datum age_percentile_disc_aggfinalfn(PG_FUNCTION_ARGS)
     Datum val;
     bool isnull;
     int64 rownum;
+    agtype_value agtv_float;
 
     Assert(AggCheckCallContext(fcinfo, NULL) == AGG_CONTEXT_AGGREGATE);
 
@@ -7742,8 +7952,12 @@ Datum age_percentile_disc_aggfinalfn(PG_FUNCTION_ARGS)
     /* We shouldn't have stored any nulls, but do the right thing anyway */
     if (isnull)
         PG_RETURN_NULL();
-    else
-        PG_RETURN_DATUM(val);
+
+    /* convert to an agtype float and return the result */
+    agtv_float.type = AGTV_FLOAT;
+    agtv_float.val.float_value = DatumGetFloat8(val);
+
+    PG_RETURN_POINTER(agtype_value_to_agtype(&agtv_float));
 }
 
 /* functions to support the aggregate function COLLECT() */