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/10/30 20:08:37 UTC

[incubator-age] branch master updated: Refactor transform logic for functions - part 2

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 dde2858  Refactor transform logic for functions - part 2
dde2858 is described below

commit dde28589c21b2721797266cffc612372fdf18c00
Author: John Gemignani <jr...@gmail.com>
AuthorDate: Fri Oct 23 14:54:44 2020 -0700

    Refactor transform logic for functions - part 2
    
    Refactored the grammar and transform logic to use PG's function call
    build routines. All functions use a FuncCall node and the
    ParseFuncOrColumn to look up the function.
    
    The grammar creates the FuncCall node and the transform logic processes
    it into a node acceptable for PG to execute.
    
    There currently are 3 types of AGE openCypher functions -
    
    First, are the functions that map directly to a PG function. There
    currently are 2: pi() -> pi() and rand() -> random(). They are qualified
    by adding 'pg_catalog' to the front of the function name list. The name
    is translated, if necessary, and passed on to the transform phase. These
    are done in the grammar file 'cypher_gram.y'.
    
    Second, are the functions that require just the prefix 'age_'. The majority
    of the functions are of this type. These have the function name, in lower
    case, added to the prefix 'age_'. They are then qualified with 'ag_catalog'
    and passed to ParseFuncOrColumn. These are done the in transform file
    'cypher_expr.c'.
    
    Third, are the functions that are just like the second type, but also
    require the graph name to be passed as the first argument. Currently, there
    are 2: startNode() and endNode(). They have the graph name prepended to the
    argument list. Everything else is done the same.
    
    All regression test output was verified for each function for the new PG
    style error messages.
    
    All old code was removed. This ends the refactor, for now, of the function
    parse and transform logic. Although, when we start doing aggregate functions,
    we may need to change some more.
---
 regress/expected/expr.out        | 336 ++++++++++++++++++++++-----------------
 src/backend/nodes/ag_nodes.c     |   2 -
 src/backend/nodes/outfuncs.c     |  10 --
 src/backend/parser/cypher_expr.c | 273 +++++++++----------------------
 src/backend/parser/cypher_gram.y |  39 +++--
 src/include/nodes/ag_nodes.h     |   3 +-
 src/include/nodes/cypher_nodes.h |  13 --
 7 files changed, 295 insertions(+), 381 deletions(-)

diff --git a/regress/expected/expr.out b/regress/expected/expr.out
index fdd7293..74e2bca 100644
--- a/regress/expected/expr.out
+++ b/regress/expected/expr.out
@@ -1311,9 +1311,10 @@ $$) AS (id agtype);
 SELECT * FROM cypher('expr', $$
     RETURN id()
 $$) AS (id agtype);
-ERROR:  invalid number of input parameters for id()
-LINE 1: SELECT * FROM cypher('expr', $$
-                                      ^
+ERROR:  function ag_catalog.age_id() does not exist
+LINE 2:     RETURN id()
+                   ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 -- start_id()
 SELECT * FROM cypher('expr', $$
     MATCH ()-[e]-() RETURN start_id(e)
@@ -1341,9 +1342,10 @@ ERROR:  start_id() argument must be an edge or null
 SELECT * FROM cypher('expr', $$
     RETURN start_id()
 $$) AS (start_id agtype);
-ERROR:  invalid number of input parameters for start_id()
-LINE 1: SELECT * FROM cypher('expr', $$
-                                      ^
+ERROR:  function ag_catalog.age_start_id() does not exist
+LINE 2:     RETURN start_id()
+                   ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 -- end_id()
 SELECT * FROM cypher('expr', $$
     MATCH ()-[e]-() RETURN end_id(e)
@@ -1371,9 +1373,10 @@ ERROR:  end_id() argument must be an edge or null
 SELECT * FROM cypher('expr', $$
     RETURN end_id()
 $$) AS (end_id agtype);
-ERROR:  invalid number of input parameters for end_id()
-LINE 1: SELECT * FROM cypher('expr', $$
-                                      ^
+ERROR:  function ag_catalog.age_end_id() does not exist
+LINE 2:     RETURN end_id()
+                   ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 -- startNode()
 SELECT * FROM cypher('expr', $$
     MATCH ()-[e]-() RETURN id(e), start_id(e), startNode(e)
@@ -1401,9 +1404,10 @@ ERROR:  startNode() argument must be an edge or null
 SELECT * FROM cypher('expr', $$
     RETURN startNode()
 $$) AS (startNode agtype);
-ERROR:  invalid number of input parameters for startNode()
-LINE 1: SELECT * FROM cypher('expr', $$
-                                      ^
+ERROR:  function ag_catalog.age_startnode() does not exist
+LINE 2:     RETURN startNode()
+                   ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 -- endNode()
 SELECT * FROM cypher('expr', $$
     MATCH ()-[e]-() RETURN id(e), end_id(e), endNode(e)
@@ -1431,9 +1435,10 @@ ERROR:  endNode() argument must be an edge or null
 SELECT * FROM cypher('expr', $$
     RETURN endNode()
 $$) AS (endNode agtype);
-ERROR:  invalid number of input parameters for endNode()
-LINE 1: SELECT * FROM cypher('expr', $$
-                                      ^
+ERROR:  function ag_catalog.age_endnode() does not exist
+LINE 2:     RETURN endNode()
+                   ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 -- type()
 SELECT * FROM cypher('expr', $$
     MATCH ()-[e]-() RETURN type(e)
@@ -1461,9 +1466,10 @@ ERROR:  type() argument must be an edge or null
 SELECT * FROM cypher('expr', $$
     RETURN type()
 $$) AS (type agtype);
-ERROR:  invalid number of input parameters for type()
-LINE 1: SELECT * FROM cypher('expr', $$
-                                      ^
+ERROR:  function ag_catalog.age_type() does not exist
+LINE 2:     RETURN type()
+                   ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 -- timestamp() can't be done as it will always have a different value
 -- size() of a string
 SELECT * FROM cypher('expr', $$
@@ -1516,9 +1522,10 @@ ERROR:  size() unsupported argument
 SELECT * FROM cypher('expr', $$
     RETURN size()
 $$) AS (size agtype);
-ERROR:  invalid number of input parameters for size()
-LINE 1: SELECT * FROM cypher('expr', $$
-                                      ^
+ERROR:  function ag_catalog.age_size() does not exist
+LINE 2:     RETURN size()
+                   ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 -- head() of an array
 SELECT * FROM cypher('expr', $$
     RETURN head([1, 2, 3, 4, 5])
@@ -1561,9 +1568,10 @@ ERROR:  head() argument must resolve to a list or null
 SELECT * FROM cypher('expr', $$
     RETURN head()
 $$) AS (head agtype);
-ERROR:  invalid number of input parameters for head()
-LINE 1: SELECT * FROM cypher('expr', $$
-                                      ^
+ERROR:  function ag_catalog.age_head() does not exist
+LINE 2:     RETURN head()
+                   ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 -- last()
 SELECT * FROM cypher('expr', $$
     RETURN last([1, 2, 3, 4, 5])
@@ -1606,9 +1614,10 @@ ERROR:  last() argument must resolve to a list or null
 SELECT * FROM cypher('expr', $$
     RETURN last()
 $$) AS (last agtype);
-ERROR:  invalid number of input parameters for last()
-LINE 1: SELECT * FROM cypher('expr', $$
-                                      ^
+ERROR:  function ag_catalog.age_last() does not exist
+LINE 2:     RETURN last()
+                   ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 -- properties()
 SELECT * FROM cypher('expr', $$
     MATCH (v) RETURN properties(v)
@@ -1649,9 +1658,10 @@ ERROR:  properties() argument must be a vertex, an edge or null
 SELECT * FROM cypher('expr', $$
     RETURN properties()
 $$) AS (properties agtype);
-ERROR:  invalid number of input parameters for properties()
-LINE 1: SELECT * FROM cypher('expr', $$
-                                      ^
+ERROR:  function ag_catalog.age_properties() does not exist
+LINE 2:     RETURN properties()
+                   ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 -- coalesce
 SELECT * FROM cypher('expr', $$
     RETURN coalesce(null, 1, null, null)
@@ -1775,9 +1785,10 @@ ERROR:  toBoolean() unsupported argument agtype 3
 SELECT * FROM cypher('expr', $$
     RETURN toBoolean()
 $$) AS (toBoolean agtype);
-ERROR:  invalid number of input parameters for toBoolean()
-LINE 1: SELECT * FROM cypher('expr', $$
-                                      ^
+ERROR:  function ag_catalog.age_toboolean() does not exist
+LINE 2:     RETURN toBoolean()
+                   ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 -- toFloat()
 SELECT * FROM cypher('expr', $$
     RETURN toFloat(1)
@@ -1844,9 +1855,10 @@ ERROR:  toFloat() unsupported argument agtype 5
 SELECT * FROM cypher('expr', $$
     RETURN toFloat()
 $$) AS (toFloat agtype);
-ERROR:  invalid number of input parameters for toFloat()
-LINE 1: SELECT * FROM cypher('expr', $$
-                                      ^
+ERROR:  function ag_catalog.age_tofloat() does not exist
+LINE 2:     RETURN toFloat()
+                   ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 -- toInteger()
 SELECT * FROM cypher('expr', $$
     RETURN toInteger(1)
@@ -1913,9 +1925,10 @@ ERROR:  toInteger() unsupported argument agtype 5
 SELECT * FROM cypher('expr', $$
     RETURN toInteger()
 $$) AS (toInteger agtype);
-ERROR:  invalid number of input parameters for toInteger()
-LINE 1: SELECT * FROM cypher('expr', $$
-                                      ^
+ERROR:  function ag_catalog.age_tointeger() does not exist
+LINE 2:     RETURN toInteger()
+                   ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 -- length() of a path
 SELECT * FROM cypher('expr', $$
     RETURN length([{id: 0, label: "vertex 0", properties: {}}::vertex, {id: 2, label: "edge 0", end_id: 1, start_id: 0, properties: {}}::edge, {id: 1, label: "vertex 1", properties: {}}::vertex]::path)
@@ -1950,9 +1963,10 @@ ERROR:  length() argument must resolve to a path or null
 SELECT * FROM cypher('expr', $$
     RETURN length()
 $$) AS (length agtype);
-ERROR:  invalid number of input parameters for length()
-LINE 1: SELECT * FROM cypher('expr', $$
-                                      ^
+ERROR:  function ag_catalog.age_length() does not exist
+LINE 2:     RETURN length()
+                   ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 --
 -- toString()
 --
@@ -2080,9 +2094,10 @@ LINE 1: SELECT * FROM age_toString();
                       ^
 HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 SELECT * FROM cypher('expr', $$ RETURN toString() $$) AS (results agtype);
-ERROR:  invalid number of input parameters for toString()
+ERROR:  function ag_catalog.age_tostring() does not exist
 LINE 1: SELECT * FROM cypher('expr', $$ RETURN toString() $$) AS (re...
-                                      ^
+                                               ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 --
 -- reverse(string)
 --
@@ -2143,9 +2158,10 @@ ERROR:  reverse() unsupported argument type 1700
 SELECT * FROM cypher('expr', $$
     RETURN reverse()
 $$) AS (results agtype);
-ERROR:  invalid number of input parameters for reverse()
-LINE 1: SELECT * FROM cypher('expr', $$
-                                      ^
+ERROR:  function ag_catalog.age_reverse() does not exist
+LINE 2:     RETURN reverse()
+                   ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 SELECT * FROM age_reverse();
 ERROR:  function age_reverse() does not exist
 LINE 1: SELECT * FROM age_reverse();
@@ -2231,9 +2247,10 @@ ERROR:  toUpper() unsupported argument agtype 5
 SELECT * FROM cypher('expr', $$
     RETURN toUpper()
 $$) AS (toUpper agtype);
-ERROR:  invalid number of input parameters for toUpper()
-LINE 1: SELECT * FROM cypher('expr', $$
-                                      ^
+ERROR:  function ag_catalog.age_toupper() does not exist
+LINE 2:     RETURN toUpper()
+                   ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 SELECT * FROM cypher('expr', $$
     RETURN toLower(true)
 $$) AS (toLower agtype);
@@ -2241,9 +2258,10 @@ ERROR:  toLower() unsupported argument agtype 5
 SELECT * FROM cypher('expr', $$
     RETURN toLower()
 $$) AS (toLower agtype);
-ERROR:  invalid number of input parameters for toLower()
-LINE 1: SELECT * FROM cypher('expr', $$
-                                      ^
+ERROR:  function ag_catalog.age_tolower() does not exist
+LINE 2:     RETURN toLower()
+                   ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 SELECT * FROM age_toupper();
 ERROR:  function age_toupper() does not exist
 LINE 1: SELECT * FROM age_toupper();
@@ -2358,21 +2376,24 @@ ERROR:  trim() unsupported argument agtype 5
 SELECT * FROM cypher('expr', $$
     RETURN lTrim()
 $$) AS (results agtype);
-ERROR:  invalid number of input parameters for lTrim()
-LINE 1: SELECT * FROM cypher('expr', $$
-                                      ^
+ERROR:  function ag_catalog.age_ltrim() does not exist
+LINE 2:     RETURN lTrim()
+                   ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 SELECT * FROM cypher('expr', $$
     RETURN rTrim()
 $$) AS (results agtype);
-ERROR:  invalid number of input parameters for rTrim()
-LINE 1: SELECT * FROM cypher('expr', $$
-                                      ^
+ERROR:  function ag_catalog.age_rtrim() does not exist
+LINE 2:     RETURN rTrim()
+                   ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 SELECT * FROM cypher('expr', $$
     RETURN trim()
 $$) AS (results agtype);
-ERROR:  invalid number of input parameters for trim()
-LINE 1: SELECT * FROM cypher('expr', $$
-                                      ^
+ERROR:  function ag_catalog.age_trim() does not exist
+LINE 2:     RETURN trim()
+                   ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 SELECT * FROM age_ltrim();
 ERROR:  function age_ltrim() does not exist
 LINE 1: SELECT * FROM age_ltrim();
@@ -2456,9 +2477,10 @@ ERROR:  left() negative values are not supported for length
 SELECT * FROM cypher('expr', $$
     RETURN left()
 $$) AS (results agtype);
-ERROR:  invalid number of input parameters for left()
-LINE 1: SELECT * FROM cypher('expr', $$
-                                      ^
+ERROR:  function ag_catalog.age_left() does not exist
+LINE 2:     RETURN left()
+                   ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 SELECT * FROM age_left('123456789', null);
 ERROR:  left() length parameter cannot be null
 SELECT * FROM age_left('123456789', -1);
@@ -2534,9 +2556,10 @@ ERROR:  right() negative values are not supported for length
 SELECT * FROM cypher('expr', $$
     RETURN right()
 $$) AS (results agtype);
-ERROR:  invalid number of input parameters for right()
-LINE 1: SELECT * FROM cypher('expr', $$
-                                      ^
+ERROR:  function ag_catalog.age_right() does not exist
+LINE 2:     RETURN right()
+                   ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 SELECT * FROM age_right('123456789', null);
 ERROR:  right() length parameter cannot be null
 SELECT * FROM age_right('123456789', -1);
@@ -2768,15 +2791,14 @@ ERROR:  split() unsupported argument agtype 3
 SELECT * FROM cypher('expr', $$
     RETURN split("a,b,c,d,e,f")
 $$) AS (results agtype);
-ERROR:  invalid number of input parameters for split()
-LINE 1: SELECT * FROM cypher('expr', $$
-                                      ^
+ERROR:  split() invalid number of arguments
 SELECT * FROM cypher('expr', $$
     RETURN split()
 $$) AS (results agtype);
-ERROR:  invalid number of input parameters for split()
-LINE 1: SELECT * FROM cypher('expr', $$
-                                      ^
+ERROR:  function ag_catalog.age_split() does not exist
+LINE 2:     RETURN split()
+                   ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 SELECT * FROM age_split(123456789, ',');
 ERROR:  split() unsupported argument type 23
 SELECT * FROM age_split('a,b,c,d,e,f', -1);
@@ -2920,21 +2942,18 @@ SELECT * FROM age_replace('', 'Hello', 'Mellow');
 SELECT * FROM cypher('expr', $$
     RETURN replace()
 $$) AS (results agtype);
-ERROR:  invalid number of input parameters for replace()
-LINE 1: SELECT * FROM cypher('expr', $$
-                                      ^
+ERROR:  function ag_catalog.age_replace() does not exist
+LINE 2:     RETURN replace()
+                   ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 SELECT * FROM cypher('expr', $$
     RETURN replace("Hello")
 $$) AS (results agtype);
-ERROR:  invalid number of input parameters for replace()
-LINE 1: SELECT * FROM cypher('expr', $$
-                                      ^
+ERROR:  replace() invalid number of arguments
 SELECT * FROM cypher('expr', $$
     RETURN replace("Hello", null)
 $$) AS (results agtype);
-ERROR:  invalid number of input parameters for replace()
-LINE 1: SELECT * FROM cypher('expr', $$
-                                      ^
+ERROR:  replace() invalid number of arguments
 SELECT * FROM cypher('expr', $$
     RETURN replace("Hello", "e", 1)
 $$) AS (results agtype);
@@ -3092,27 +3111,31 @@ ERROR:  cot() unsupported argument agtype 1
 SELECT * FROM cypher('expr', $$
     RETURN sin()
 $$) AS (results agtype);
-ERROR:  invalid number of input parameters for sin()
-LINE 1: SELECT * FROM cypher('expr', $$
-                                      ^
+ERROR:  function ag_catalog.age_sin() does not exist
+LINE 2:     RETURN sin()
+                   ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 SELECT * FROM cypher('expr', $$
     RETURN cos()
 $$) AS (results agtype);
-ERROR:  invalid number of input parameters for cos()
-LINE 1: SELECT * FROM cypher('expr', $$
-                                      ^
+ERROR:  function ag_catalog.age_cos() does not exist
+LINE 2:     RETURN cos()
+                   ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 SELECT * FROM cypher('expr', $$
     RETURN tan()
 $$) AS (results agtype);
-ERROR:  invalid number of input parameters for tan()
-LINE 1: SELECT * FROM cypher('expr', $$
-                                      ^
+ERROR:  function ag_catalog.age_tan() does not exist
+LINE 2:     RETURN tan()
+                   ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 SELECT * FROM cypher('expr', $$
     RETURN cot()
 $$) AS (results agtype);
-ERROR:  invalid number of input parameters for cot()
-LINE 1: SELECT * FROM cypher('expr', $$
-                                      ^
+ERROR:  function ag_catalog.age_cot() does not exist
+LINE 2:     RETURN cot()
+                   ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 SELECT * FROM age_sin('0');
 ERROR:  sin() unsupported argument type 25
 SELECT * FROM age_cos('0');
@@ -3341,33 +3364,35 @@ ERROR:  atan2() unsupported argument agtype 1
 SELECT * FROM cypher('expr', $$
     RETURN asin()
 $$) AS (results agtype);
-ERROR:  invalid number of input parameters for asin()
-LINE 1: SELECT * FROM cypher('expr', $$
-                                      ^
+ERROR:  function ag_catalog.age_asin() does not exist
+LINE 2:     RETURN asin()
+                   ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 SELECT * FROM cypher('expr', $$
     RETURN acos()
 $$) AS (results agtype);
-ERROR:  invalid number of input parameters for acos()
-LINE 1: SELECT * FROM cypher('expr', $$
-                                      ^
+ERROR:  function ag_catalog.age_acos() does not exist
+LINE 2:     RETURN acos()
+                   ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 SELECT * FROM cypher('expr', $$
     RETURN atan()
 $$) AS (results agtype);
-ERROR:  invalid number of input parameters for atan()
-LINE 1: SELECT * FROM cypher('expr', $$
-                                      ^
+ERROR:  function ag_catalog.age_atan() does not exist
+LINE 2:     RETURN atan()
+                   ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 SELECT * FROM cypher('expr', $$
     RETURN atan2()
 $$) AS (results agtype);
-ERROR:  invalid number of input parameters for atan2()
-LINE 1: SELECT * FROM cypher('expr', $$
-                                      ^
+ERROR:  function ag_catalog.age_atan2() does not exist
+LINE 2:     RETURN atan2()
+                   ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 SELECT * FROM cypher('expr', $$
     RETURN atan2(null)
 $$) AS (results agtype);
-ERROR:  invalid number of input parameters for atan2()
-LINE 1: SELECT * FROM cypher('expr', $$
-                                      ^
+ERROR:  atan2() invalid number of arguments
 SELECT * FROM age_asin('0');
 ERROR:  asin() unsupported argument type 25
 SELECT * FROM age_acos('0');
@@ -3455,15 +3480,17 @@ $$) AS (results agtype);
 SELECT * FROM cypher('expr', $$
     RETURN pi(null)
 $$) AS (results agtype);
-ERROR:  invalid number of input parameters for pi()
-LINE 1: SELECT * FROM cypher('expr', $$
-                                      ^
+ERROR:  function pg_catalog.pi(agtype) does not exist
+LINE 2:     RETURN pi(null)
+                     ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 SELECT * FROM cypher('expr', $$
     RETURN pi(1)
 $$) AS (results agtype);
-ERROR:  invalid number of input parameters for pi()
-LINE 1: SELECT * FROM cypher('expr', $$
-                                      ^
+ERROR:  function pg_catalog.pi(agtype) does not exist
+LINE 2:     RETURN pi(1)
+                     ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 --
 -- radians() & degrees()
 --
@@ -3552,15 +3579,17 @@ $$) AS (results agtype);
 SELECT * FROM cypher('expr', $$
     RETURN radians()
 $$) AS (results agtype);
-ERROR:  invalid number of input parameters for radians()
-LINE 1: SELECT * FROM cypher('expr', $$
-                                      ^
+ERROR:  function ag_catalog.age_radians() does not exist
+LINE 2:     RETURN radians()
+                   ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 SELECT * FROM cypher('expr', $$
     RETURN degrees()
 $$) AS (results agtype);
-ERROR:  invalid number of input parameters for degrees()
-LINE 1: SELECT * FROM cypher('expr', $$
-                                      ^
+ERROR:  function ag_catalog.age_degrees() does not exist
+LINE 2:     RETURN degrees()
+                   ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 SELECT * FROM cypher('expr', $$
     RETURN radians("1")
 $$) AS (results agtype);
@@ -3785,33 +3814,38 @@ $$) AS (results agtype);
 SELECT * FROM cypher('expr', $$
     RETURN abs()
 $$) AS (results agtype);
-ERROR:  invalid number of input parameters for abs()
-LINE 1: SELECT * FROM cypher('expr', $$
-                                      ^
+ERROR:  function ag_catalog.age_abs() does not exist
+LINE 2:     RETURN abs()
+                   ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 SELECT * FROM cypher('expr', $$
     RETURN ceil()
 $$) AS (results agtype);
-ERROR:  invalid number of input parameters for ceil()
-LINE 1: SELECT * FROM cypher('expr', $$
-                                      ^
+ERROR:  function ag_catalog.age_ceil() does not exist
+LINE 2:     RETURN ceil()
+                   ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 SELECT * FROM cypher('expr', $$
     RETURN floor()
 $$) AS (results agtype);
-ERROR:  invalid number of input parameters for floor()
-LINE 1: SELECT * FROM cypher('expr', $$
-                                      ^
+ERROR:  function ag_catalog.age_floor() does not exist
+LINE 2:     RETURN floor()
+                   ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 SELECT * FROM cypher('expr', $$
     RETURN round()
 $$) AS (results agtype);
-ERROR:  invalid number of input parameters for round()
-LINE 1: SELECT * FROM cypher('expr', $$
-                                      ^
+ERROR:  function ag_catalog.age_round() does not exist
+LINE 2:     RETURN round()
+                   ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 SELECT * FROM cypher('expr', $$
     RETURN sign()
 $$) AS (results agtype);
-ERROR:  invalid number of input parameters for sign()
-LINE 1: SELECT * FROM cypher('expr', $$
-                                      ^
+ERROR:  function ag_catalog.age_sign() does not exist
+LINE 2:     RETURN sign()
+                   ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 SELECT * FROM cypher('expr', $$
     RETURN abs("1")
 $$) AS (results agtype);
@@ -3928,15 +3962,17 @@ $$) as (result agtype);
 SELECT * from cypher('expr', $$
     RETURN log()
 $$) as (result agtype);
-ERROR:  invalid number of input parameters for log()
-LINE 1: SELECT * from cypher('expr', $$
-                                      ^
+ERROR:  function ag_catalog.age_log() does not exist
+LINE 2:     RETURN log()
+                   ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 SELECT * from cypher('expr', $$
     RETURN log10()
 $$) as (result agtype);
-ERROR:  invalid number of input parameters for log10()
-LINE 1: SELECT * from cypher('expr', $$
-                                      ^
+ERROR:  function ag_catalog.age_log10() does not exist
+LINE 2:     RETURN log10()
+                   ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 --
 -- e()
 --
@@ -3988,9 +4024,10 @@ $$) as (result agtype);
 SELECT * from cypher('expr', $$
     RETURN exp()
 $$) as (result agtype);
-ERROR:  invalid number of input parameters for exp()
-LINE 1: SELECT * from cypher('expr', $$
-                                      ^
+ERROR:  function ag_catalog.age_exp() does not exist
+LINE 2:     RETURN exp()
+                   ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 SELECT * from cypher('expr', $$
     RETURN exp("1")
 $$) as (result agtype);
@@ -4043,9 +4080,10 @@ $$) as (result agtype);
 SELECT * from cypher('expr', $$
     RETURN sqrt()
 $$) as (result agtype);
-ERROR:  invalid number of input parameters for sqrt()
-LINE 1: SELECT * from cypher('expr', $$
-                                      ^
+ERROR:  function ag_catalog.age_sqrt() does not exist
+LINE 2:     RETURN sqrt()
+                   ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 SELECT * from cypher('expr', $$
     RETURN sqrt("1")
 $$) as (result agtype);
diff --git a/src/backend/nodes/ag_nodes.c b/src/backend/nodes/ag_nodes.c
index 2de04c8..dcab596 100644
--- a/src/backend/nodes/ag_nodes.c
+++ b/src/backend/nodes/ag_nodes.c
@@ -45,7 +45,6 @@ const char *node_names[] = {
     "cypher_list",
     "cypher_string_match",
     "cypher_typecast",
-    "cypher_function",
     "cypher_integer_const",
     "cypher_sub_pattern"
 };
@@ -78,7 +77,6 @@ const ExtensibleNodeMethods node_methods[] = {
     DEFINE_NODE_METHODS(cypher_list),
     DEFINE_NODE_METHODS(cypher_string_match),
     DEFINE_NODE_METHODS(cypher_typecast),
-    DEFINE_NODE_METHODS(cypher_function),
     DEFINE_NODE_METHODS(cypher_integer_const),
     DEFINE_NODE_METHODS(cypher_sub_pattern)
 };
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 5fe9541..2dd4e12 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -227,16 +227,6 @@ void out_cypher_typecast(StringInfo str, const ExtensibleNode *node)
     write_location_field(location);
 }
 
-/* function */
-void out_cypher_function(StringInfo str, const ExtensibleNode *node)
-{
-    DEFINE_AG_NODE(cypher_function);
-
-    write_node_field(exprs);
-    write_node_field(funcname);
-    write_location_field(location);
-}
-
 /* integer constant */
 void out_cypher_integer_const(StringInfo str, const ExtensibleNode *node)
 {
diff --git a/src/backend/parser/cypher_expr.c b/src/backend/parser/cypher_expr.c
index 5abb4d0..f0c191c 100644
--- a/src/backend/parser/cypher_expr.c
+++ b/src/backend/parser/cypher_expr.c
@@ -26,6 +26,7 @@
 #include "optimizer/tlist.h"
 #include "parser/parse_coerce.h"
 #include "parser/parse_expr.h"
+#include "parser/parse_func.h"
 #include "parser/cypher_clause.h"
 #include "parser/parse_node.h"
 #include "parser/parse_oper.h"
@@ -42,99 +43,6 @@
 #include "utils/ag_func.h"
 #include "utils/agtype.h"
 
-/* supported function definitions */
-#define FUNC_ENDNODE    {"endNode",    AGTYPEOID, AGTYPEOID, 0, AGTYPEOID, 1, 2, true, false}
-#define FUNC_HEAD       {"head",       AGTYPEOID, 0, 0, AGTYPEOID, 1, 1, false, false}
-#define FUNC_ID         {"id",         AGTYPEOID, 0, 0, AGTYPEOID, 1, 1, false, false}
-#define FUNC_STARTID    {"start_id",   AGTYPEOID, 0, 0, AGTYPEOID, 1, 1, false, false}
-#define FUNC_ENDID      {"end_id",     AGTYPEOID, 0, 0, AGTYPEOID, 1, 1, false, false}
-#define FUNC_LAST       {"last",       AGTYPEOID, 0, 0, AGTYPEOID, 1, 1, false, false}
-#define FUNC_LENGTH     {"length",     AGTYPEOID, 0, 0, AGTYPEOID, 1, 1, false, false}
-#define FUNC_PROPERTIES {"properties", AGTYPEOID, 0, 0, AGTYPEOID, 1, 1, false, false}
-#define FUNC_SIZE       {"size",       ANYOID,    0, 0, AGTYPEOID, 1, 1, false, false}
-#define FUNC_STARTNODE  {"startNode",  AGTYPEOID, AGTYPEOID, 0, AGTYPEOID, 1, 2, true, false}
-#define FUNC_TOBOOLEAN  {"toBoolean",  ANYOID,    0, 0, AGTYPEOID, 1, 1, false, false}
-#define FUNC_TOFLOAT    {"toFloat",    ANYOID,    0, 0, AGTYPEOID, 1, 1, false, false}
-#define FUNC_TOINTEGER  {"toInteger",  ANYOID,    0, 0, AGTYPEOID, 1, 1, false, false}
-#define FUNC_TYPE       {"type",       AGTYPEOID, 0, 0, AGTYPEOID, 1, 1, false, false}
-#define FUNC_EXISTS     {"exists",     AGTYPEOID, 0, 0, BOOLOID,   1, 1, false, false}
-#define FUNC_TOSTRING   {"toString",   ANYOID,    0, 0, AGTYPEOID, 1, 1, false, false}
-#define FUNC_REVERSE    {"reverse",    ANYOID,    0, 0, AGTYPEOID, 1, 1, false, false}
-#define FUNC_TOUPPER    {"toUpper",    ANYOID,    0, 0, AGTYPEOID, 1, 1, false, false}
-#define FUNC_TOLOWER    {"toLower",    ANYOID,    0, 0, AGTYPEOID, 1, 1, false, false}
-#define FUNC_LTRIM      {"lTrim",      ANYOID,    0, 0, AGTYPEOID, 1, 1, false, false}
-#define FUNC_RTRIM      {"rTrim",      ANYOID,    0, 0, AGTYPEOID, 1, 1, false, false}
-#define FUNC_BTRIM      {"trim",       ANYOID,    0, 0, AGTYPEOID, 1, 1, false, false}
-#define FUNC_RSUBSTR    {"right",      ANYOID,    ANYOID, 0, AGTYPEOID, 2, 1, false, false}
-#define FUNC_LSUBSTR    {"left",       ANYOID,    ANYOID, 0, AGTYPEOID, 2, 1, false, false}
-#define FUNC_BSUBSTR    {"substring",  ANYOID,    ANYOID, ANYOID, AGTYPEOID, -1, 1, false, false}
-#define FUNC_SPLIT      {"split",      ANYOID,    ANYOID, 0, AGTYPEOID, 2, 1, false, false}
-#define FUNC_REPLACE    {"replace",    ANYOID,    ANYOID, 0, AGTYPEOID, 3, 1, false, false}
-#define FUNC_RSIN       {"sin",        ANYOID,    0, 0, AGTYPEOID, 1, 1, false, false}
-#define FUNC_RCOS       {"cos",        ANYOID,    0, 0, AGTYPEOID, 1, 1, false, false}
-#define FUNC_RTAN       {"tan",        ANYOID,    0, 0, AGTYPEOID, 1, 1, false, false}
-#define FUNC_RCOT       {"cot",        ANYOID,    0, 0, AGTYPEOID, 1, 1, false, false}
-#define FUNC_RASIN      {"asin",       ANYOID,    0, 0, AGTYPEOID, 1, 1, false, false}
-#define FUNC_RACOS      {"acos",       ANYOID,    0, 0, AGTYPEOID, 1, 1, false, false}
-#define FUNC_RATAN      {"atan",       ANYOID,    0, 0, AGTYPEOID, 1, 1, false, false}
-#define FUNC_RATAN2     {"atan2",      ANYOID,    0, 0, AGTYPEOID, 2, 1, false, false}
-#define FUNC_PI         {"pi",         0,         0, 0, FLOAT8OID, 0, 0, false, true}
-#define FUNC_DEGREES    {"degrees",    ANYOID,    0, 0, AGTYPEOID, 1, 1, false, false}
-#define FUNC_RADIANS    {"radians",    ANYOID,    0, 0, AGTYPEOID, 1, 1, false, false}
-#define FUNC_ROUND      {"round",      ANYOID,    0, 0, AGTYPEOID, 1, 1, false, false}
-#define FUNC_CEIL       {"ceil",       ANYOID,    0, 0, AGTYPEOID, 1, 1, false, false}
-#define FUNC_FLOOR      {"floor",      ANYOID,    0, 0, AGTYPEOID, 1, 1, false, false}
-#define FUNC_ABS        {"abs",        ANYOID,    0, 0, AGTYPEOID, 1, 1, false, false}
-#define FUNC_SIGN       {"sign",       ANYOID,    0, 0, AGTYPEOID, 1, 1, false, false}
-#define FUNC_RAND       {"random",     0,         0, 0, FLOAT8OID, 0, 0, false, true}
-#define FUNC_LOG        {"log",        ANYOID,    0, 0, AGTYPEOID, 1, 1, false, false}
-#define FUNC_LOG10      {"log10",      ANYOID,    0, 0, AGTYPEOID, 1, 1, false, false}
-#define FUNC_E          {"e",          0,         0, 0, AGTYPEOID, 0, 0, false, false}
-#define FUNC_EXP        {"exp",        ANYOID,    0, 0, AGTYPEOID, 1, 1, false, false}
-#define FUNC_SQRT       {"sqrt",       ANYOID,    0, 0, AGTYPEOID, 1, 1, false, false}
-#define FUNC_TIMESTAMP  {"timestamp",  0,         0, 0, AGTYPEOID, 0, 0, false, false}
-
-/* supported functions */
-#define SUPPORTED_FUNCTIONS {FUNC_TYPE, FUNC_ENDNODE, FUNC_HEAD, FUNC_ID, \
-                             FUNC_STARTID, FUNC_ENDID, FUNC_LAST, FUNC_LENGTH, \
-                             FUNC_PROPERTIES, FUNC_SIZE, FUNC_STARTNODE, \
-                             FUNC_TOINTEGER, FUNC_TOBOOLEAN, FUNC_TOFLOAT, \
-                             FUNC_EXISTS, FUNC_TOSTRING, FUNC_REVERSE, \
-                             FUNC_TOUPPER, FUNC_TOLOWER, FUNC_LTRIM, \
-                             FUNC_RTRIM, FUNC_BTRIM, FUNC_RSUBSTR, \
-                             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_ROUND, FUNC_CEIL, FUNC_FLOOR, FUNC_ABS, \
-                             FUNC_SIGN, FUNC_RAND, FUNC_LOG, FUNC_LOG10, \
-                             FUNC_E, FUNC_EXP, FUNC_SQRT, FUNC_TIMESTAMP}
-
-/* structure for supported function signatures */
-typedef struct function_signature
-{
-    /* the name from the parser */
-    char *parsed_name;
-    /* input types, currently only up to 3 are supported */
-    Oid input1_oid;
-    Oid input2_oid;
-    Oid input3_oid;
-    /* output type */
-    Oid result_oid;
-    /* number of expressions (arguments) passed by the parser */
-    int nexprs;
-    /*
-     * The number of actual arguments to the function. This can differ from the
-     * number of expressions passed if the function requires additional
-     * information to be passed, such as the graph name.
-     */
-    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,
                                            Node *expr);
 static Node *transform_A_Const(cypher_parsestate *cpstate, A_Const *ac);
@@ -157,12 +65,10 @@ static Node *transform_cypher_string_match(cypher_parsestate *cpstate,
                                            cypher_string_match *csm_node);
 static Node *transform_cypher_typecast(cypher_parsestate *cpstate,
                                        cypher_typecast *ctypecast);
-static Node *transform_cypher_function(cypher_parsestate *cpstate,
-                                       cypher_function *cfunction);
 static Node *transform_CoalesceExpr(cypher_parsestate *cpstate,
                                     CoalesceExpr *cexpr);
 static Node *transform_SubLink(cypher_parsestate *cpstate, SubLink *sublink);
-
+static Node *transform_FuncCall(cypher_parsestate *cpstate, FuncCall *fn);
 Node *transform_cypher_expr(cypher_parsestate *cpstate, Node *expr,
                             ParseExprKind expr_kind)
 {
@@ -247,14 +153,12 @@ static Node *transform_cypher_expr_recurse(cypher_parsestate *cpstate,
         if (is_ag_node(expr, cypher_typecast))
             return transform_cypher_typecast(cpstate,
                                              (cypher_typecast *)expr);
-        if (is_ag_node(expr, cypher_function))
-            return transform_cypher_function(cpstate,
-                                             (cypher_function *)expr);
-
         ereport(ERROR,
                 (errmsg_internal("unrecognized ExtensibleNode: %s",
                                  ((ExtensibleNode *)expr)->extnodename)));
         return NULL;
+    case T_FuncCall:
+        return transform_FuncCall(cpstate, (FuncCall *)expr);
     case T_SubLink:
         return transform_SubLink(cpstate, (SubLink *)expr);
         break;
@@ -787,117 +691,98 @@ static Node *transform_cypher_typecast(cypher_parsestate *cpstate,
 }
 
 /*
- * Function to create a function execute node
+ * Code borrowed from PG's transformFuncCall and updated for AGE
  */
-static Node *transform_cypher_function(cypher_parsestate *cpstate,
-                                       cypher_function *cfunction)
+static Node *transform_FuncCall(cypher_parsestate *cpstate, FuncCall *fn)
 {
-    FuncExpr *func_expr = NULL;
-    char *funcname = NULL;
-    List *exprs = NIL;
-    List *texprs = NIL;
-    ListCell *lc = NULL;
-    int nexprs;
-    Oid func_operator_oid = InvalidOid;
-    char *graph_name = cpstate->graph_name;
-    int i;
-    /* load supported functions */
-    function_signature supported_functions[] = SUPPORTED_FUNCTIONS;
-    function_signature *fs = NULL;
-    int nfunctions = sizeof(supported_functions)/sizeof(function_signature);
+    ParseState *pstate = &cpstate->pstate;
+    Node *last_srf = pstate->p_last_srf;
+    List *targs = NIL;
+    List *fname = NIL;
+    ListCell *args;
 
-    /* verify input parameter */
-    Assert (cpstate != NULL);
-    Assert (cfunction != NULL);
-    Assert (nfunctions >= 0);
+    /* Transform the list of arguments ... */
+    foreach(args, fn->args)
+        targs = lappend(targs,
+                        transform_cypher_expr_recurse(cpstate,
+                                                      (Node *)lfirst(args)));
 
-    /* get the function name and expressions */
-    funcname = ((Value*)linitial(cfunction->funcname))->val.str;
-    exprs = cfunction->exprs;
-    nexprs = list_length(exprs);
+    /*
+     * When WITHIN GROUP is used, we treat its ORDER BY expressions as
+     * additional arguments to the function, for purposes of function lookup
+     * and argument type coercion.  So, transform each such expression and add
+     * them to the targs list.  We don't explicitly mark where each argument
+     * came from, but ParseFuncOrColumn can tell what's what by reference to
+     * list_length(fn->agg_order).
+     */
 
-    /* iterate through SUPPORTED_FUNCTIONS */
-    for (i = 0; i < nfunctions; i++)
+    /* This part needs to be worked on. So, for now, we exit if it is set. */
+    Assert(fn->agg_within_group == false);
+    if (fn->agg_within_group)
     {
-        fs = &supported_functions[i];
-        /* we need to ignore case */
-        if (pg_strcasecmp(funcname, fs->parsed_name) == 0)
+        Assert(fn->agg_order != NIL);
+        foreach(args, fn->agg_order)
         {
-            /* is the function listed in pg_catalog */
-            if (fs->in_pg_catalog)
-                func_operator_oid = get_pg_func_oid(fs->parsed_name, fs->nargs,
-                                                    fs->input1_oid,
-                                                    fs->input2_oid,
-                                                    fs->input3_oid);
-            /* this is an AGE function - all are prefixed with age_ */
-            else
-            {
-                /* get the function name, length, and allocate a new string */
-                int pnlen = strlen(fs->parsed_name);
-                char *actual_name = palloc(pnlen + 5);
-                int i;
-
-                /* copy in the prefix */
-                strncpy(actual_name, "age_", 4);
-
-                /*
-                 * All PG function names are in lower case. So, copy in the name
-                 * in lower case for the search.
-                 */
-                for(i = 0; i < pnlen; i++)
-                    actual_name[i + 4] = tolower(fs->parsed_name[i]);
-
-                /* terminate it with 0 */
-                actual_name[i + 4] = 0;
-
-                /* look for a matching function */
-                func_operator_oid = get_ag_func_oid(actual_name, fs->nargs,
-                                                    fs->input1_oid,
-                                                    fs->input2_oid,
-                                                    fs->input3_oid);
-            }
-            break;
+            SortBy *arg = (SortBy *) lfirst(args);
+
+            targs = lappend(targs, transformExpr(pstate, arg->node,
+                                                 EXPR_KIND_ORDER_BY));
         }
     }
 
-    /* we should have something at this point */
-    Assert(fs != NULL);
-    /* if none was found, error out */
-    if (func_operator_oid == InvalidOid)
-        ereport(ERROR, (errmsg_internal("function \'%s\' not supported",
-                                        funcname)));
     /*
-     * verify the number of passed arguments -
-     * if -1 its variable but at least 1
-     * otherwise they must match.
+     * If the function name is not qualified, then it is one of ours. We need to
+     * construct its name, and qualify it, so that PG can find it.
      */
-    if (((fs->nexprs != -1) || (nexprs == 0)) &&
-        (fs->nexprs != nexprs))
-        ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                        errmsg("invalid number of input parameters for %s()",
-                               funcname)));
-    /* does this function need the graph name passed in as the first arg? */
-    if (fs->needs_graph_name)
+    if (list_length(fn->funcname) == 1)
     {
-        Datum d = string_to_agtype(graph_name);
-        Const *c = makeConst(AGTYPEOID, -1, InvalidOid, -1, d, false, false);
+        /* get the name, size, and the ag name allocated */
+        char *name = ((Value*)linitial(fn->funcname))->val.str;
+        int pnlen = strlen(name);
+        char *ag_name = palloc(pnlen + 5);
+        int i;
 
-        texprs = lappend(texprs, c);
-    }
-    /* transform expression arguments */
-    foreach(lc, exprs)
-    {
-        Node *expr = (Node *)lfirst(lc);
+        /* copy in the prefix - all AGE functions are prefixed with age_ */
+        strncpy(ag_name, "age_", 4);
+
+        /*
+         * All AGE function names are in lower case. So, copy in the name
+         * in lower case.
+         */
+        for(i = 0; i < pnlen; i++)
+            ag_name[i + 4] = tolower(name[i]);
+
+        /* terminate it with 0 */
+        ag_name[i + 4] = 0;
+
+        /* qualify the name with our schema name */
+        fname = list_make2(makeString("ag_catalog"), makeString(ag_name));
+
+        /*
+         * Currently 2 functions need the graph name passed in as the first
+         * argument - in addition to the other arguments: startNode and endNode.
+         * So, check for those 2 functions here and that the arg list is not
+         * empty. Then prepend the graph name if necessary.
+         */
+        if ((list_length(targs) != 0) &&
+            ((pg_strcasecmp("startNode", name) == 0 ||
+              pg_strcasecmp("endNode", name) == 0)))
+        {
+            char *graph_name = cpstate->graph_name;
+            Datum d = string_to_agtype(graph_name);
+            Const *c = makeConst(AGTYPEOID, -1, InvalidOid, -1, d, false, false);
+
+            targs = lcons(c, targs);
+        }
 
-        expr = transform_cypher_expr_recurse(cpstate, expr);
-        texprs = lappend(texprs, expr);
     }
-    /* make function node */
-    func_expr = makeFuncExpr(func_operator_oid, fs->result_oid, texprs,
-                             InvalidOid, InvalidOid, COERCE_EXPLICIT_CALL);
-    func_expr->location = cfunction->location;
+    /* If it is not one of our functions, pass the name list through */
+    else
+        fname = fn->funcname;
 
-    return (Node *)func_expr;
+    /* ... and hand off to ParseFuncOrColumn */
+    return ParseFuncOrColumn(pstate, fname, targs, last_srf, fn, false,
+                             fn->location);
 }
 
 /*
diff --git a/src/backend/parser/cypher_gram.y b/src/backend/parser/cypher_gram.y
index 33d1bee..95341d3 100644
--- a/src/backend/parser/cypher_gram.y
+++ b/src/backend/parser/cypher_gram.y
@@ -1171,6 +1171,7 @@ func_name:
             $$ = list_make1(makeString($1));
         }
     ;
+
 property_key_name:
     schema_name
     ;
@@ -1430,23 +1431,39 @@ static Node *make_typecast_expr(Node *expr, char *typecast, int location)
  */
 static Node *make_function_expr(List *func_name, List *exprs, int location)
 {
-    cypher_function *node;
+    FuncCall *fnode;
 
-    /* check for AGE functions that are mapped to another function */
+    /* AGE function names are unqualified. So, their list size = 1 */
     if (list_length(func_name) == 1)
     {
+        List *funcname;
+        char *name;
+
         /* get the name of the function */
-        char *name = ((Value*)linitial(func_name))->val.str;
+        name = ((Value*)linitial(func_name))->val.str;
 
-        /* currently we only map rand (cypher) -> random (PG) */
+        /*
+         * Check for openCypher functions that are directly mapped to PG
+         * functions. Currently, we only map rand() and pi().
+         */
         if (pg_strcasecmp(name, "rand") == 0)
-            func_name = list_make1(makeString("random"));
-    }
+            funcname = SystemFuncName("random");
+        else if (pg_strcasecmp(name, "pi") == 0)
+            funcname = SystemFuncName("pi");
+        else
+            /*
+             * We don't qualify AGE functions here. This is done in the
+             * transform layer and allows us to know which functions are ours.
+             */
+            funcname = func_name;
 
-    node = make_ag_node(cypher_function);
-    node->exprs = exprs;
-    node->funcname = func_name;
-    node->location = location;
+        /* build the function call */
+        fnode = makeFuncCall(funcname, exprs, location);
+    }
+    /* all other functions are passed as is */
+    else
+        fnode = makeFuncCall(func_name, exprs, location);
 
-    return (Node *)node;
+    /* return the node */
+    return (Node *)fnode;
 }
diff --git a/src/include/nodes/ag_nodes.h b/src/include/nodes/ag_nodes.h
index dc1e52f..b530f53 100644
--- a/src/include/nodes/ag_nodes.h
+++ b/src/include/nodes/ag_nodes.h
@@ -50,8 +50,7 @@ typedef enum ag_node_tag
     cypher_string_match_t,
     // typecast
     cypher_typecast_t,
-    // functions
-    cypher_function_t,
+    // integer constant
     cypher_integer_const_t,
     // sub patterns
     cypher_sub_pattern_t
diff --git a/src/include/nodes/cypher_nodes.h b/src/include/nodes/cypher_nodes.h
index f5f56a4..c903fa7 100644
--- a/src/include/nodes/cypher_nodes.h
+++ b/src/include/nodes/cypher_nodes.h
@@ -274,16 +274,6 @@ typedef struct cypher_typecast
     int location;
 } cypher_typecast;
 
-/* grammar node for functions (scalar, etc.) */
-typedef struct cypher_function
-{
-    ExtensibleNode extensible;
-    /* we take an expr_list */
-    List *exprs;
-    List *funcname;
-    int location;
-} cypher_function;
-
 /* clauses */
 void out_cypher_return(StringInfo str, const ExtensibleNode *node);
 void out_cypher_with(StringInfo str, const ExtensibleNode *node);
@@ -310,9 +300,6 @@ void out_cypher_string_match(StringInfo str, const ExtensibleNode *node);
 /* typecast */
 void out_cypher_typecast(StringInfo str, const ExtensibleNode *node);
 
-/* function */
-void out_cypher_function(StringInfo str, const ExtensibleNode *node);
-
 /* integer constant */
 void out_cypher_integer_const(StringInfo str, const ExtensibleNode *node);