This is an automated email from the ASF dual-hosted git repository.

mtaha pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/age.git


The following commit(s) were added to refs/heads/master by this push:
     new 94770a69 Fix issue with CALL/YIELD for user defined and qualified 
functions. (#2217)
94770a69 is described below

commit 94770a69751c8ecdbf21887ee83a98ed6ae979c8
Author: John Gemignani <jrgemign...@gmail.com>
AuthorDate: Mon Sep 15 06:35:52 2025 -0700

    Fix issue with CALL/YIELD for user defined and qualified functions. (#2217)
    
    Fixed 2 issues with CALL/YIELD -
    
       1) If a user defined function was in search_path, the transform_FuncCall
          logic would only find it, if it were part of an extension.
    
       2) If a function were qualified, the transform_cypher_call_subquery
          logic would mistakenly extract the schema name instead of the
          function name.
    
    NOTE: transform_FuncCall should be reviewed for possible refactor.
    
    Added regression tests.
    
        modified:   src/backend/parser/cypher_clause.c
        modified:   src/backend/parser/cypher_expr.c
        modified:   regress/expected/cypher_call.out
        modified:   regress/sql/cypher_call.sql
---
 regress/expected/cypher_call.out   | 52 ++++++++++++++++++++++++++++++++++++++
 regress/sql/cypher_call.sql        | 25 ++++++++++++++++++
 src/backend/parser/cypher_clause.c |  6 ++---
 src/backend/parser/cypher_expr.c   | 13 +++++-----
 4 files changed, 86 insertions(+), 10 deletions(-)

diff --git a/regress/expected/cypher_call.out b/regress/expected/cypher_call.out
index 6980abe4..bb1185b9 100644
--- a/regress/expected/cypher_call.out
+++ b/regress/expected/cypher_call.out
@@ -240,6 +240,58 @@ SELECT * FROM cypher('cypher_call', $$ CALL sqrt(64) YIELD 
sqrt CALL agtype_sum(
 ERROR:  duplicate variable "sqrt"
 LINE 1: ...LL sqrt(64) YIELD sqrt CALL agtype_sum(2,2) YIELD agtype_sum...
                                                              ^
+-- Fix CALL/YIELD issues
+CREATE OR REPLACE FUNCTION myfunc(i agtype)
+RETURNS agtype
+LANGUAGE plpgsql
+AS $$
+DECLARE
+    result agtype;
+BEGIN
+    RETURN ag_catalog.age_sqrt(i);
+END;
+$$;
+-- should have no errors
+SELECT * FROM cypher('cypher_call', $$ CALL ag_catalog.age_sqrt(64) YIELD 
age_sqrt RETURN age_sqrt $$) as (sqrt agtype);
+ sqrt 
+------
+ 8.0
+(1 row)
+
+SELECT * FROM cypher('cypher_call', $$ CALL myfunc(25) YIELD myfunc RETURN 
myfunc $$) as (result agtype);
+ result 
+--------
+ 5.0
+(1 row)
+
+SELECT * FROM cypher('cypher_call', $$ CALL ag_catalog.myfunc(25) YIELD myfunc 
RETURN myfunc $$) as (result agtype);
+ result 
+--------
+ 5.0
+(1 row)
+
+-- should error
+SELECT * FROM cypher('cypher_call', $$ CALL myfunc() YIELD myfunc RETURN 
myfunc $$) as (result agtype);
+ERROR:  function myfunc does not exist
+LINE 1: SELECT * FROM cypher('cypher_call', $$ CALL myfunc() YIELD m...
+                                             ^
+HINT:  If the function is from an external extension, make sure the extension 
is installed and the function is in the search path.
+SELECT * FROM cypher('cypher_call', $$ CALL myfunz(25) YIELD myfunc RETURN 
myfunc $$) as (result agtype);
+ERROR:  function myfunz does not exist
+LINE 1: SELECT * FROM cypher('cypher_call', $$ CALL myfunz(25) YIELD...
+                                             ^
+HINT:  If the function is from an external extension, make sure the extension 
is installed and the function is in the search path.
+SELECT * FROM cypher('cypher_call', $$ CALL ag_catalog.myfunc() YIELD myfunc 
RETURN myfunc $$) as (result agtype);
+ERROR:  function ag_catalog.myfunc() does not exist
+LINE 1: ...T * FROM cypher('cypher_call', $$ CALL ag_catalog.myfunc() Y...
+                                                             ^
+HINT:  No function matches the given name and argument types. You might need 
to add explicit type casts.
+SELECT * FROM cypher('cypher_call', $$ CALL ag_catalog.myfunz(25) YIELD myfunc 
RETURN myfunc $$) as (result agtype);
+ERROR:  function ag_catalog.myfunz(agtype) does not exist
+LINE 1: ...OM cypher('cypher_call', $$ CALL ag_catalog.myfunz(25) YIELD...
+                                                             ^
+HINT:  No function matches the given name and argument types. You might need 
to add explicit type casts.
+DROP FUNCTION myfunc;
 DROP SCHEMA call_stmt_test CASCADE;
 NOTICE:  drop cascades to function call_stmt_test.add_agtype(agtype,agtype)
 SELECT drop_graph('cypher_call', true);
diff --git a/regress/sql/cypher_call.sql b/regress/sql/cypher_call.sql
index 91727680..ee146875 100644
--- a/regress/sql/cypher_call.sql
+++ b/regress/sql/cypher_call.sql
@@ -104,5 +104,30 @@ SELECT * FROM cypher('cypher_call', $$ CALL sqrt(64) YIELD 
sqrt AS sqrt1 CALL sq
 SELECT * FROM cypher('cypher_call', $$ CALL sqrt(64) YIELD sqrt AS sqrt1 CALL 
sqrt(81) YIELD sqrt AS sqrt1 RETURN sqrt1, sqrt1 $$) as (a agtype, b agtype);
 SELECT * FROM cypher('cypher_call', $$ CALL sqrt(64) YIELD sqrt CALL 
agtype_sum(2,2) YIELD agtype_sum AS sqrt RETURN sqrt, sqrt $$) as (a agtype, b 
agtype);
 
+-- Fix CALL/YIELD issues
+CREATE OR REPLACE FUNCTION myfunc(i agtype)
+RETURNS agtype
+LANGUAGE plpgsql
+AS $$
+DECLARE
+    result agtype;
+BEGIN
+    RETURN ag_catalog.age_sqrt(i);
+END;
+$$;
+
+-- should have no errors
+SELECT * FROM cypher('cypher_call', $$ CALL ag_catalog.age_sqrt(64) YIELD 
age_sqrt RETURN age_sqrt $$) as (sqrt agtype);
+SELECT * FROM cypher('cypher_call', $$ CALL myfunc(25) YIELD myfunc RETURN 
myfunc $$) as (result agtype);
+SELECT * FROM cypher('cypher_call', $$ CALL ag_catalog.myfunc(25) YIELD myfunc 
RETURN myfunc $$) as (result agtype);
+
+-- should error
+SELECT * FROM cypher('cypher_call', $$ CALL myfunc() YIELD myfunc RETURN 
myfunc $$) as (result agtype);
+SELECT * FROM cypher('cypher_call', $$ CALL myfunz(25) YIELD myfunc RETURN 
myfunc $$) as (result agtype);
+SELECT * FROM cypher('cypher_call', $$ CALL ag_catalog.myfunc() YIELD myfunc 
RETURN myfunc $$) as (result agtype);
+SELECT * FROM cypher('cypher_call', $$ CALL ag_catalog.myfunz(25) YIELD myfunc 
RETURN myfunc $$) as (result agtype);
+
+DROP FUNCTION myfunc;
+
 DROP SCHEMA call_stmt_test CASCADE;
 SELECT drop_graph('cypher_call', true);
diff --git a/src/backend/parser/cypher_clause.c 
b/src/backend/parser/cypher_clause.c
index 172e6305..93e710ab 100644
--- a/src/backend/parser/cypher_clause.c
+++ b/src/backend/parser/cypher_clause.c
@@ -1153,7 +1153,7 @@ static Query 
*transform_cypher_call_subquery(cypher_parsestate *cpstate,
                                                      EXPR_KIND_FROM_FUNCTION));
 
     /* retrieve the column name from funccall */
-    colName = strVal(linitial(self->funccall->funcname));
+    colName = strVal(llast(self->funccall->funcname));
 
     /* make a targetentry from the funcexpr node */
     tle = makeTargetEntry((Expr *) node,
@@ -3957,7 +3957,7 @@ static List 
*transform_map_to_ind_recursive(cypher_parsestate *cpstate,
  *
  * Transforms the map to a list of equality irrespective of
  * value type. For example,
- * 
+ *
  * x.name = 'xyz'
  * x.map = {"city": "abc", "street": {"name": "pqr", "number": 123}}
  * x.list = [9, 8, 7]
@@ -4011,7 +4011,7 @@ static List 
*transform_map_to_ind_top_level(cypher_parsestate *cpstate,
         qual = (Node *)make_op(pstate, op, lhs, rhs, last_srf, -1);
         quals = lappend(quals, qual);
     }
-    
+
     return quals;
 }
 
diff --git a/src/backend/parser/cypher_expr.c b/src/backend/parser/cypher_expr.c
index 390bfb39..993957c8 100644
--- a/src/backend/parser/cypher_expr.c
+++ b/src/backend/parser/cypher_expr.c
@@ -2036,7 +2036,7 @@ static Node *transform_FuncCall(cypher_parsestate 
*cpstate, FuncCall *fn)
                 targs = lcons(c, targs);
             }
         }
-        /* 
+        /*
          * If it's not in age, check if it's a potential call to some function
          * in another installed extension.
          */
@@ -2055,14 +2055,13 @@ static Node *transform_FuncCall(cypher_parsestate 
*cpstate, FuncCall *fn)
                                                          procform, extension);
                 return retval;
             }
+            /*
+             * Else we have a function that is in the search_path, and not
+             * qualified, but is not in an extension. Pass it through.
+             */
             else
             {
-                ereport(ERROR,
-                        (errcode(ERRCODE_UNDEFINED_FUNCTION),
-                        errmsg("function %s does not exist", name),
-                        errhint("If the function is from an external 
extension, "
-                                "make sure the extension is installed and the "
-                                "function is in the search path.")));
+                fname = fn->funcname;
             }
         }
         /* no function found */

Reply via email to