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));
+}