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 9ab41bc Add openCypher pi function
9ab41bc is described below
commit 9ab41bcf54dfb5c61ffc120b119cd24a507d1847
Author: John Gemignani <[email protected]>
AuthorDate: Fri Sep 4 14:10:50 2020 -0700
Add openCypher pi function
Added the openCypher pi() function.
Added a field in the function definitions in cypher_expr.c to state
whether the function is in the pg_catalog, or not.
Added a cast from float8 to agtype.
Added a cast from agtype (integer and float) to float8.
Added regression tests.
---
age--0.2.0.sql | 27 ++++++++++++
regress/expected/expr.out | 64 +++++++++++++++++++++++++++
regress/sql/expr.sql | 29 +++++++++++++
src/backend/catalog/ag_namespace.c | 5 +++
src/backend/parser/cypher_expr.c | 89 +++++++++++++++++++++-----------------
src/backend/parser/cypher_gram.y | 4 ++
src/backend/utils/adt/agtype.c | 53 +++++++++++++++++++++++
src/backend/utils/ag_func.c | 32 +++++++++++++-
src/include/catalog/ag_namespace.h | 1 +
src/include/utils/ag_func.h | 1 +
10 files changed, 265 insertions(+), 40 deletions(-)
diff --git a/age--0.2.0.sql b/age--0.2.0.sql
index 98ab451..9348931 100644
--- a/age--0.2.0.sql
+++ b/age--0.2.0.sql
@@ -694,6 +694,33 @@ AS 'MODULE_PATHNAME';
CREATE CAST (boolean AS agtype)
WITH FUNCTION bool_to_agtype(boolean);
+-- float8 -> agtype (explicit)
+
+CREATE FUNCTION float8_to_agtype(float8)
+RETURNS agtype
+LANGUAGE c
+STABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE CAST (float8 AS agtype)
+WITH FUNCTION float8_to_agtype(float8);
+
+-- agtype -> float8 (implicit)
+
+CREATE FUNCTION agtype_to_float8(agtype)
+RETURNS float8
+LANGUAGE c
+STABLE
+RETURNS NULL ON NULL INPUT
+PARALLEL SAFE
+AS 'MODULE_PATHNAME';
+
+CREATE CAST (agtype AS float8)
+WITH FUNCTION agtype_to_float8(agtype)
+AS IMPLICIT;
+
--
-- agtype - access operators
--
diff --git a/regress/expected/expr.out b/regress/expected/expr.out
index af28b22..b735d00 100644
--- a/regress/expected/expr.out
+++ b/regress/expected/expr.out
@@ -3401,6 +3401,70 @@ HINT: No function matches the given name and argument
types. You might need to
SELECT * FROM r_atan2(1);
ERROR: atan2() invalid number of arguments
--
+-- pi
+--
+SELECT * FROM cypher('expr', $$
+ RETURN pi()
+$$) AS (results agtype);
+ results
+------------------
+ 3.14159265358979
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+ RETURN sin(pi())
+$$) AS (results agtype);
+ results
+----------------------
+ 1.22464679914735e-16
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+ RETURN sin(pi()/4)
+$$) AS (results agtype);
+ results
+-------------------
+ 0.707106781186547
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+ RETURN cos(pi())
+$$) AS (results agtype);
+ results
+---------
+ -1.0
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+ RETURN cos(pi()/2)
+$$) AS (results agtype);
+ results
+----------------------
+ 6.12323399573677e-17
+(1 row)
+
+SELECT * FROM cypher('expr', $$
+ RETURN sin(pi()/2)
+$$) AS (results agtype);
+ results
+---------
+ 1.0
+(1 row)
+
+-- should fail
+SELECT * FROM cypher('expr', $$
+ RETURN pi(null)
+$$) AS (results agtype);
+ERROR: invalid number of input parameters for pi()
+LINE 1: SELECT * FROM cypher('expr', $$
+ ^
+SELECT * FROM cypher('expr', $$
+ RETURN pi(1)
+$$) AS (results agtype);
+ERROR: invalid number of input parameters for pi()
+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 246f0f4..a689c8d 100644
--- a/regress/sql/expr.sql
+++ b/regress/sql/expr.sql
@@ -1438,6 +1438,35 @@ SELECT * FROM r_atan2();
SELECT * FROM r_atan2(1);
--
+-- pi
+--
+SELECT * FROM cypher('expr', $$
+ RETURN pi()
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+ RETURN sin(pi())
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+ RETURN sin(pi()/4)
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+ RETURN cos(pi())
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+ RETURN cos(pi()/2)
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+ RETURN sin(pi()/2)
+$$) AS (results agtype);
+-- should fail
+SELECT * FROM cypher('expr', $$
+ RETURN pi(null)
+$$) AS (results agtype);
+SELECT * FROM cypher('expr', $$
+ RETURN pi(1)
+$$) AS (results agtype);
+
+--
-- Cleanup
--
SELECT * FROM drop_graph('expr', true);
diff --git a/src/backend/catalog/ag_namespace.c
b/src/backend/catalog/ag_namespace.c
index 1c95a8e..4416aaf 100644
--- a/src/backend/catalog/ag_namespace.c
+++ b/src/backend/catalog/ag_namespace.c
@@ -24,3 +24,8 @@ Oid ag_catalog_namespace_id(void)
{
return get_namespace_oid("ag_catalog", false);
}
+
+Oid pg_catalog_namespace_id(void)
+{
+ return get_namespace_oid("pg_catalog", false);
+}
diff --git a/src/backend/parser/cypher_expr.c b/src/backend/parser/cypher_expr.c
index d313d02..359136a 100644
--- a/src/backend/parser/cypher_expr.c
+++ b/src/backend/parser/cypher_expr.c
@@ -43,41 +43,42 @@
#include "utils/agtype.h"
/* supported function definitions */
-#define FUNC_ENDNODE {"endNode", "endnode", AGTYPEOID, AGTYPEOID, 0,
AGTYPEOID, 1, 2, true}
-#define FUNC_HEAD {"head", "head", AGTYPEOID, 0, 0,
AGTYPEOID, 1, 1, false}
-#define FUNC_ID {"id", "id", AGTYPEOID, 0, 0,
AGTYPEOID, 1, 1, false}
-#define FUNC_STARTID {"start_id", "start_id", AGTYPEOID, 0, 0,
AGTYPEOID, 1, 1, false}
-#define FUNC_ENDID {"end_id", "end_id", AGTYPEOID, 0, 0,
AGTYPEOID, 1, 1, false}
-#define FUNC_LAST {"last", "last", AGTYPEOID, 0, 0,
AGTYPEOID, 1, 1, false}
-#define FUNC_LENGTH {"length", "length", AGTYPEOID, 0, 0,
AGTYPEOID, 1, 1, false}
-#define FUNC_PROPERTIES {"properties", "properties", AGTYPEOID, 0, 0,
AGTYPEOID, 1, 1, false}
-#define FUNC_SIZE {"size", "size", ANYOID, 0, 0,
AGTYPEOID, 1, 1, false}
-#define FUNC_STARTNODE {"startNode", "startnode", AGTYPEOID, AGTYPEOID, 0,
AGTYPEOID, 1, 2, true}
-#define FUNC_TOBOOLEAN {"toBoolean", "toboolean", ANYOID, 0, 0,
AGTYPEOID, 1, 1, false}
-#define FUNC_TOFLOAT {"toFloat", "tofloat", ANYOID, 0, 0,
AGTYPEOID, 1, 1, false}
-#define FUNC_TOINTEGER {"toInteger", "tointeger", ANYOID, 0, 0,
AGTYPEOID, 1, 1, false}
-#define FUNC_TYPE {"type", "type", AGTYPEOID, 0, 0,
AGTYPEOID, 1, 1, false}
-#define FUNC_EXISTS {"exists", "exists_property", AGTYPEOID, 0, 0,
BOOLOID, 1, 1, false}
-#define FUNC_TOSTRING {"toString", "tostring", ANYOID, 0, 0,
AGTYPEOID, 1, 1, false}
-#define FUNC_REVERSE {"reverse", "reverse", ANYOID, 0, 0,
AGTYPEOID, 1, 1, false}
-#define FUNC_TOUPPER {"toUpper", "touppercase", ANYOID, 0, 0,
AGTYPEOID, 1, 1, false}
-#define FUNC_TOLOWER {"toLower", "tolowercase", ANYOID, 0, 0,
AGTYPEOID, 1, 1, false}
-#define FUNC_LTRIM {"lTrim", "l_trim", ANYOID, 0, 0,
AGTYPEOID, 1, 1, false}
-#define FUNC_RTRIM {"rTrim", "r_trim", ANYOID, 0, 0,
AGTYPEOID, 1, 1, false}
-#define FUNC_BTRIM {"trim", "b_trim", ANYOID, 0, 0,
AGTYPEOID, 1, 1, false}
-#define FUNC_RSUBSTR {"right", "r_substr", ANYOID, ANYOID, 0,
AGTYPEOID, 2, 1, false}
-#define FUNC_LSUBSTR {"left", "l_substr", ANYOID, ANYOID, 0,
AGTYPEOID, 2, 1, false}
-#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}
-#define FUNC_RASIN {"asin", "r_asin", ANYOID, 0, 0,
AGTYPEOID, 1, 1, false}
-#define FUNC_RACOS {"acos", "r_acos", ANYOID, 0, 0,
AGTYPEOID, 1, 1, false}
-#define FUNC_RATAN {"atan", "r_atan", ANYOID, 0, 0,
AGTYPEOID, 1, 1, false}
-#define FUNC_RATAN2 {"atan2", "r_atan2", ANYOID, 0, 0,
AGTYPEOID, 2, 1, false}
+#define FUNC_ENDNODE {"endNode", "endnode", AGTYPEOID, AGTYPEOID, 0,
AGTYPEOID, 1, 2, true, false}
+#define FUNC_HEAD {"head", "head", AGTYPEOID, 0, 0,
AGTYPEOID, 1, 1, false, false}
+#define FUNC_ID {"id", "id", AGTYPEOID, 0, 0,
AGTYPEOID, 1, 1, false, false}
+#define FUNC_STARTID {"start_id", "start_id", AGTYPEOID, 0, 0,
AGTYPEOID, 1, 1, false, false}
+#define FUNC_ENDID {"end_id", "end_id", AGTYPEOID, 0, 0,
AGTYPEOID, 1, 1, false, false}
+#define FUNC_LAST {"last", "last", AGTYPEOID, 0, 0,
AGTYPEOID, 1, 1, false, false}
+#define FUNC_LENGTH {"length", "length", AGTYPEOID, 0, 0,
AGTYPEOID, 1, 1, false, false}
+#define FUNC_PROPERTIES {"properties", "properties", AGTYPEOID, 0, 0,
AGTYPEOID, 1, 1, false, false}
+#define FUNC_SIZE {"size", "size", ANYOID, 0, 0,
AGTYPEOID, 1, 1, false, false}
+#define FUNC_STARTNODE {"startNode", "startnode", AGTYPEOID, AGTYPEOID, 0,
AGTYPEOID, 1, 2, true, false}
+#define FUNC_TOBOOLEAN {"toBoolean", "toboolean", ANYOID, 0, 0,
AGTYPEOID, 1, 1, false, false}
+#define FUNC_TOFLOAT {"toFloat", "tofloat", ANYOID, 0, 0,
AGTYPEOID, 1, 1, false, false}
+#define FUNC_TOINTEGER {"toInteger", "tointeger", ANYOID, 0, 0,
AGTYPEOID, 1, 1, false, false}
+#define FUNC_TYPE {"type", "type", AGTYPEOID, 0, 0,
AGTYPEOID, 1, 1, false, false}
+#define FUNC_EXISTS {"exists", "exists_property", AGTYPEOID, 0, 0,
BOOLOID, 1, 1, false, false}
+#define FUNC_TOSTRING {"toString", "tostring", ANYOID, 0, 0,
AGTYPEOID, 1, 1, false, false}
+#define FUNC_REVERSE {"reverse", "reverse", ANYOID, 0, 0,
AGTYPEOID, 1, 1, false, false}
+#define FUNC_TOUPPER {"toUpper", "touppercase", ANYOID, 0, 0,
AGTYPEOID, 1, 1, false, false}
+#define FUNC_TOLOWER {"toLower", "tolowercase", ANYOID, 0, 0,
AGTYPEOID, 1, 1, false, false}
+#define FUNC_LTRIM {"lTrim", "l_trim", ANYOID, 0, 0,
AGTYPEOID, 1, 1, false, false}
+#define FUNC_RTRIM {"rTrim", "r_trim", ANYOID, 0, 0,
AGTYPEOID, 1, 1, false, false}
+#define FUNC_BTRIM {"trim", "b_trim", ANYOID, 0, 0,
AGTYPEOID, 1, 1, false, false}
+#define FUNC_RSUBSTR {"right", "r_substr", ANYOID, ANYOID, 0,
AGTYPEOID, 2, 1, false, false}
+#define FUNC_LSUBSTR {"left", "l_substr", ANYOID, ANYOID, 0,
AGTYPEOID, 2, 1, false, false}
+#define FUNC_BSUBSTR {"substring", "b_substr", ANYOID, ANYOID,
ANYOID, AGTYPEOID, -1, 1, false, false}
+#define FUNC_SPLIT {"split", "split", ANYOID, ANYOID, 0,
AGTYPEOID, 2, 1, false, false}
+#define FUNC_REPLACE {"replace", "replace", ANYOID, ANYOID, 0,
AGTYPEOID, 3, 1, false, false}
+#define FUNC_RSIN {"sin", "r_sin", ANYOID, 0, 0,
AGTYPEOID, 1, 1, false, false}
+#define FUNC_RCOS {"cos", "r_cos", ANYOID, 0, 0,
AGTYPEOID, 1, 1, false, false}
+#define FUNC_RTAN {"tan", "r_tan", ANYOID, 0, 0,
AGTYPEOID, 1, 1, false, false}
+#define FUNC_RCOT {"cot", "r_cot", ANYOID, 0, 0,
AGTYPEOID, 1, 1, false, false}
+#define FUNC_RASIN {"asin", "r_asin", ANYOID, 0, 0,
AGTYPEOID, 1, 1, false, false}
+#define FUNC_RACOS {"acos", "r_acos", ANYOID, 0, 0,
AGTYPEOID, 1, 1, false, false}
+#define FUNC_RATAN {"atan", "r_atan", ANYOID, 0, 0,
AGTYPEOID, 1, 1, false, false}
+#define FUNC_RATAN2 {"atan2", "r_atan2", ANYOID, 0, 0,
AGTYPEOID, 2, 1, false, false}
+#define FUNC_PI {"pi", "pi", 0, 0, 0,
FLOAT8OID, 0, 0, false, true}
/* supported functions */
#define SUPPORTED_FUNCTIONS {FUNC_TYPE, FUNC_ENDNODE, FUNC_HEAD, FUNC_ID, \
@@ -90,7 +91,7 @@
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_RATAN2, FUNC_PI}
/* structure for supported function signatures */
typedef struct function_signature
@@ -115,6 +116,8 @@ typedef struct function_signature
int nargs;
/* needs graph name passed */
bool needs_graph_name;
+ /* is the function listed in pg_catalog */
+ bool in_pg_catalog;
} function_signature;
static Node *transform_cypher_expr_recurse(cypher_parsestate *cpstate,
@@ -804,9 +807,17 @@ static Node *transform_cypher_function(cypher_parsestate
*cpstate,
fs = &supported_functions[i];
if (strcmp(funcname, fs->parsed_name) == 0)
{
- func_operator_oid = get_ag_func_oid(fs->actual_name, fs->nargs,
- fs->input1_oid, fs->input2_oid,
- fs->input3_oid);
+ /* is the function listed in pg_catalog */
+ if (fs->in_pg_catalog)
+ func_operator_oid = get_pg_func_oid(fs->actual_name, fs->nargs,
+ fs->input1_oid,
+ fs->input2_oid,
+ fs->input3_oid);
+ else
+ func_operator_oid = get_ag_func_oid(fs->actual_name, fs->nargs,
+ fs->input1_oid,
+ fs->input2_oid,
+ fs->input3_oid);
break;
}
}
diff --git a/src/backend/parser/cypher_gram.y b/src/backend/parser/cypher_gram.y
index afd76ac..7f20d6b 100644
--- a/src/backend/parser/cypher_gram.y
+++ b/src/backend/parser/cypher_gram.y
@@ -1453,6 +1453,10 @@ static Node *make_immediate_no_arg_function_expr(char
*funcname, int location)
return (Node *)node;
}
+ else if (pg_strcasecmp(funcname, "pi") == 0)
+ {
+ return make_function_expr(funcname, NIL, location);
+ }
else
ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR),
errmsg("unrecognized or unsupported function")));
diff --git a/src/backend/utils/adt/agtype.c b/src/backend/utils/adt/agtype.c
index 8ee02dd..4b165f4 100644
--- a/src/backend/utils/adt/agtype.c
+++ b/src/backend/utils/adt/agtype.c
@@ -2159,6 +2159,49 @@ Datum agtype_to_bool(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(agtv.val.boolean);
}
+PG_FUNCTION_INFO_V1(agtype_to_float8);
+
+/*
+ * Cast agtype to float8.
+ */
+Datum agtype_to_float8(PG_FUNCTION_ARGS)
+{
+ agtype *agtype_in = AG_GET_ARG_AGTYPE_P(0);
+ agtype_value agtv;
+ float8 result;
+
+ if (!agtype_extract_scalar(&agtype_in->root, &agtv) ||
+ (agtv.type != AGTV_FLOAT && agtv.type != AGTV_INTEGER))
+ cannot_cast_agtype_value(agtv.type, "float");
+
+ PG_FREE_IF_COPY(agtype_in, 0);
+
+ if (agtv.type == AGTV_FLOAT)
+ result = agtv.val.float_value;
+
+ if (agtv.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.
+ */
+ char *string = DatumGetCString(DirectFunctionCall1(int8out,
+ Int64GetDatum(agtv.val.int_value)));
+ 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)
+ ereport(ERROR, (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("cannot cast to float8, integer value out
of range")));
+ }
+
+ PG_RETURN_FLOAT8(result);
+}
+
PG_FUNCTION_INFO_V1(bool_to_agtype);
/*
@@ -2169,6 +2212,16 @@ Datum bool_to_agtype(PG_FUNCTION_ARGS)
return boolean_to_agtype(PG_GETARG_BOOL(0));
}
+PG_FUNCTION_INFO_V1(float8_to_agtype);
+
+/*
+ * Cast float8 to agtype.
+ */
+Datum float8_to_agtype(PG_FUNCTION_ARGS)
+{
+ return float_to_agtype(PG_GETARG_FLOAT8(0));
+}
+
/*
* Helper function for agtype_access_operator map access.
* Note: This function expects that a map and a scalar key are being passed.
diff --git a/src/backend/utils/ag_func.c b/src/backend/utils/ag_func.c
index 27a04a8..bd78f12 100644
--- a/src/backend/utils/ag_func.c
+++ b/src/backend/utils/ag_func.c
@@ -78,7 +78,37 @@ Oid get_ag_func_oid(const char *func_name, const int nargs,
...)
ObjectIdGetDatum(ag_catalog_namespace_id()));
if (!OidIsValid(func_oid))
{
- ereport(ERROR, (errmsg_internal("function does not exist"),
+ ereport(ERROR, (errmsg_internal("ag function does not exist"),
+ errdetail_internal("%s(%d)", func_name, nargs)));
+ }
+
+ return func_oid;
+}
+
+Oid get_pg_func_oid(const char *func_name, const int nargs, ...)
+{
+ Oid oids[FUNC_MAX_ARGS];
+ va_list ap;
+ int i;
+ oidvector *arg_types;
+ Oid func_oid;
+
+ AssertArg(func_name);
+ AssertArg(nargs >= 0 && nargs <= FUNC_MAX_ARGS);
+
+ va_start(ap, nargs);
+ for (i = 0; i < nargs; i++)
+ oids[i] = va_arg(ap, Oid);
+ va_end(ap);
+
+ arg_types = buildoidvector(oids, nargs);
+
+ func_oid = GetSysCacheOid3(PROCNAMEARGSNSP, CStringGetDatum(func_name),
+ PointerGetDatum(arg_types),
+ ObjectIdGetDatum(pg_catalog_namespace_id()));
+ if (!OidIsValid(func_oid))
+ {
+ ereport(ERROR, (errmsg_internal("pg function does not exist"),
errdetail_internal("%s(%d)", func_name, nargs)));
}
diff --git a/src/include/catalog/ag_namespace.h
b/src/include/catalog/ag_namespace.h
index 94248f1..17269d0 100644
--- a/src/include/catalog/ag_namespace.h
+++ b/src/include/catalog/ag_namespace.h
@@ -20,5 +20,6 @@
#include "postgres.h"
Oid ag_catalog_namespace_id(void);
+Oid pg_catalog_namespace_id(void);
#endif
diff --git a/src/include/utils/ag_func.h b/src/include/utils/ag_func.h
index 18fd201..e20afeb 100644
--- a/src/include/utils/ag_func.h
+++ b/src/include/utils/ag_func.h
@@ -21,5 +21,6 @@
bool is_oid_ag_func(Oid func_oid, const char *func_name);
Oid get_ag_func_oid(const char *func_name, const int nargs, ...);
+Oid get_pg_func_oid(const char *func_name, const int nargs, ...);
#endif