Skip to content

Commit 94770a6

Browse files
authored
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
1 parent 1afd9fb commit 94770a6

File tree

4 files changed

+86
-10
lines changed

4 files changed

+86
-10
lines changed

regress/expected/cypher_call.out

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,58 @@ SELECT * FROM cypher('cypher_call', $$ CALL sqrt(64) YIELD sqrt CALL agtype_sum(
240240
ERROR: duplicate variable "sqrt"
241241
LINE 1: ...LL sqrt(64) YIELD sqrt CALL agtype_sum(2,2) YIELD agtype_sum...
242242
^
243+
-- Fix CALL/YIELD issues
244+
CREATE OR REPLACE FUNCTION myfunc(i agtype)
245+
RETURNS agtype
246+
LANGUAGE plpgsql
247+
AS $$
248+
DECLARE
249+
result agtype;
250+
BEGIN
251+
RETURN ag_catalog.age_sqrt(i);
252+
END;
253+
$$;
254+
-- should have no errors
255+
SELECT * FROM cypher('cypher_call', $$ CALL ag_catalog.age_sqrt(64) YIELD age_sqrt RETURN age_sqrt $$) as (sqrt agtype);
256+
sqrt
257+
------
258+
8.0
259+
(1 row)
260+
261+
SELECT * FROM cypher('cypher_call', $$ CALL myfunc(25) YIELD myfunc RETURN myfunc $$) as (result agtype);
262+
result
263+
--------
264+
5.0
265+
(1 row)
266+
267+
SELECT * FROM cypher('cypher_call', $$ CALL ag_catalog.myfunc(25) YIELD myfunc RETURN myfunc $$) as (result agtype);
268+
result
269+
--------
270+
5.0
271+
(1 row)
272+
273+
-- should error
274+
SELECT * FROM cypher('cypher_call', $$ CALL myfunc() YIELD myfunc RETURN myfunc $$) as (result agtype);
275+
ERROR: function myfunc does not exist
276+
LINE 1: SELECT * FROM cypher('cypher_call', $$ CALL myfunc() YIELD m...
277+
^
278+
HINT: If the function is from an external extension, make sure the extension is installed and the function is in the search path.
279+
SELECT * FROM cypher('cypher_call', $$ CALL myfunz(25) YIELD myfunc RETURN myfunc $$) as (result agtype);
280+
ERROR: function myfunz does not exist
281+
LINE 1: SELECT * FROM cypher('cypher_call', $$ CALL myfunz(25) YIELD...
282+
^
283+
HINT: If the function is from an external extension, make sure the extension is installed and the function is in the search path.
284+
SELECT * FROM cypher('cypher_call', $$ CALL ag_catalog.myfunc() YIELD myfunc RETURN myfunc $$) as (result agtype);
285+
ERROR: function ag_catalog.myfunc() does not exist
286+
LINE 1: ...T * FROM cypher('cypher_call', $$ CALL ag_catalog.myfunc() Y...
287+
^
288+
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
289+
SELECT * FROM cypher('cypher_call', $$ CALL ag_catalog.myfunz(25) YIELD myfunc RETURN myfunc $$) as (result agtype);
290+
ERROR: function ag_catalog.myfunz(agtype) does not exist
291+
LINE 1: ...OM cypher('cypher_call', $$ CALL ag_catalog.myfunz(25) YIELD...
292+
^
293+
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
294+
DROP FUNCTION myfunc;
243295
DROP SCHEMA call_stmt_test CASCADE;
244296
NOTICE: drop cascades to function call_stmt_test.add_agtype(agtype,agtype)
245297
SELECT drop_graph('cypher_call', true);

regress/sql/cypher_call.sql

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,5 +104,30 @@ SELECT * FROM cypher('cypher_call', $$ CALL sqrt(64) YIELD sqrt AS sqrt1 CALL sq
104104
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);
105105
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);
106106

107+
-- Fix CALL/YIELD issues
108+
CREATE OR REPLACE FUNCTION myfunc(i agtype)
109+
RETURNS agtype
110+
LANGUAGE plpgsql
111+
AS $$
112+
DECLARE
113+
result agtype;
114+
BEGIN
115+
RETURN ag_catalog.age_sqrt(i);
116+
END;
117+
$$;
118+
119+
-- should have no errors
120+
SELECT * FROM cypher('cypher_call', $$ CALL ag_catalog.age_sqrt(64) YIELD age_sqrt RETURN age_sqrt $$) as (sqrt agtype);
121+
SELECT * FROM cypher('cypher_call', $$ CALL myfunc(25) YIELD myfunc RETURN myfunc $$) as (result agtype);
122+
SELECT * FROM cypher('cypher_call', $$ CALL ag_catalog.myfunc(25) YIELD myfunc RETURN myfunc $$) as (result agtype);
123+
124+
-- should error
125+
SELECT * FROM cypher('cypher_call', $$ CALL myfunc() YIELD myfunc RETURN myfunc $$) as (result agtype);
126+
SELECT * FROM cypher('cypher_call', $$ CALL myfunz(25) YIELD myfunc RETURN myfunc $$) as (result agtype);
127+
SELECT * FROM cypher('cypher_call', $$ CALL ag_catalog.myfunc() YIELD myfunc RETURN myfunc $$) as (result agtype);
128+
SELECT * FROM cypher('cypher_call', $$ CALL ag_catalog.myfunz(25) YIELD myfunc RETURN myfunc $$) as (result agtype);
129+
130+
DROP FUNCTION myfunc;
131+
107132
DROP SCHEMA call_stmt_test CASCADE;
108133
SELECT drop_graph('cypher_call', true);

src/backend/parser/cypher_clause.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1153,7 +1153,7 @@ static Query *transform_cypher_call_subquery(cypher_parsestate *cpstate,
11531153
EXPR_KIND_FROM_FUNCTION));
11541154

11551155
/* retrieve the column name from funccall */
1156-
colName = strVal(linitial(self->funccall->funcname));
1156+
colName = strVal(llast(self->funccall->funcname));
11571157

11581158
/* make a targetentry from the funcexpr node */
11591159
tle = makeTargetEntry((Expr *) node,
@@ -3957,7 +3957,7 @@ static List *transform_map_to_ind_recursive(cypher_parsestate *cpstate,
39573957
*
39583958
* Transforms the map to a list of equality irrespective of
39593959
* value type. For example,
3960-
*
3960+
*
39613961
* x.name = 'xyz'
39623962
* x.map = {"city": "abc", "street": {"name": "pqr", "number": 123}}
39633963
* x.list = [9, 8, 7]
@@ -4011,7 +4011,7 @@ static List *transform_map_to_ind_top_level(cypher_parsestate *cpstate,
40114011
qual = (Node *)make_op(pstate, op, lhs, rhs, last_srf, -1);
40124012
quals = lappend(quals, qual);
40134013
}
4014-
4014+
40154015
return quals;
40164016
}
40174017

src/backend/parser/cypher_expr.c

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2036,7 +2036,7 @@ static Node *transform_FuncCall(cypher_parsestate *cpstate, FuncCall *fn)
20362036
targs = lcons(c, targs);
20372037
}
20382038
}
2039-
/*
2039+
/*
20402040
* If it's not in age, check if it's a potential call to some function
20412041
* in another installed extension.
20422042
*/
@@ -2055,14 +2055,13 @@ static Node *transform_FuncCall(cypher_parsestate *cpstate, FuncCall *fn)
20552055
procform, extension);
20562056
return retval;
20572057
}
2058+
/*
2059+
* Else we have a function that is in the search_path, and not
2060+
* qualified, but is not in an extension. Pass it through.
2061+
*/
20582062
else
20592063
{
2060-
ereport(ERROR,
2061-
(errcode(ERRCODE_UNDEFINED_FUNCTION),
2062-
errmsg("function %s does not exist", name),
2063-
errhint("If the function is from an external extension, "
2064-
"make sure the extension is installed and the "
2065-
"function is in the search path.")));
2064+
fname = fn->funcname;
20662065
}
20672066
}
20682067
/* no function found */

0 commit comments

Comments
 (0)