diff --git a/src/parse_functions.cpp b/src/parse_functions.cpp index 3423964..e3262c1 100644 --- a/src/parse_functions.cpp +++ b/src/parse_functions.cpp @@ -4,8 +4,12 @@ #include "duckdb/parser/statement/select_statement.hpp" #include "duckdb/parser/query_node/select_node.hpp" #include "duckdb/parser/expression/function_expression.hpp" +#include "duckdb/parser/expression/subquery_expression.hpp" #include "duckdb/parser/expression/window_expression.hpp" #include "duckdb/parser/parsed_expression_iterator.hpp" +#include "duckdb/parser/tableref/joinref.hpp" +#include "duckdb/parser/tableref/subqueryref.hpp" +#include "duckdb/parser/tableref/table_function_ref.hpp" #include "duckdb/parser/result_modifier.hpp" #include "duckdb/main/extension_util.hpp" #include "duckdb/function/scalar/nested_functions.hpp" @@ -73,6 +77,8 @@ static unique_ptr ParseFunctionsInit(ClientContext &co return make_uniq(); } +static void ExtractFunctionsFromQueryNode(const QueryNode &node, std::vector &results); + class FunctionExtractor { public: static void ExtractFromExpression(const ParsedExpression &expr, @@ -144,6 +150,14 @@ class FunctionExtractor { if (window_expr.filter_expr) { ExtractFromExpression(*window_expr.filter_expr, results, FunctionContext::Nested); } + } else if (expr.expression_class == ExpressionClass::SUBQUERY) { + auto &subquery = (SubqueryExpression &)expr; + if (subquery.child) { + ExtractFromExpression(*subquery.child, results, context); + } + if (subquery.subquery && subquery.subquery->node) { + ExtractFunctionsFromQueryNode(*subquery.subquery->node, results); + } } else { // For non-function expressions, preserve the current context ParsedExpressionIterator::EnumerateChildren(expr, [&](const ParsedExpression &child) { @@ -163,6 +177,44 @@ class FunctionExtractor { } }; +static void ExtractFunctionsFromRef(const TableRef &ref, std::vector &results) { + switch (ref.type) { + case TableReferenceType::JOIN: { + auto &join = (JoinRef &)ref; + if (join.left) { + ExtractFunctionsFromRef(*join.left, results); + } + if (join.right) { + ExtractFunctionsFromRef(*join.right, results); + } + if (join.condition) { + FunctionExtractor::ExtractFromExpression(*join.condition, results, FunctionContext::Join); + } + break; + } + case TableReferenceType::SUBQUERY: { + auto &subquery = (SubqueryRef &)ref; + if (subquery.subquery && subquery.subquery->node) { + ExtractFunctionsFromQueryNode(*subquery.subquery->node, results); + } + break; + } + case TableReferenceType::TABLE_FUNCTION: { + auto &table_function = (TableFunctionRef &)ref; + if (table_function.function) { + ParsedExpressionIterator::EnumerateChildren(*table_function.function, [&](const ParsedExpression &child) { + FunctionExtractor::ExtractFromExpression(child, results, FunctionContext::Nested); + }); + } + if (table_function.subquery && table_function.subquery->node) { + ExtractFunctionsFromQueryNode(*table_function.subquery->node, results); + } + break; + } + default: + break; + } +} static void ExtractFunctionsFromQueryNode(const QueryNode &node, std::vector &results) { if (node.type == QueryNodeType::SELECT_NODE) { @@ -202,6 +254,10 @@ static void ExtractFunctionsFromQueryNode(const QueryNode &node, std::vector (SELECT min(y) FROM t2)'); +---- +[min] + +query I +SELECT parse_function_names('SELECT (SELECT sum(x) FROM (SELECT abs(y) AS x FROM t) nested)'); +---- +[sum, abs] + # no functions query I SELECT parse_function_names('SELECT name, age FROM users;'); @@ -94,4 +121,4 @@ SELECT parse_function_names('UPDATE users SET name = upper(name);'); query I SELECT parse_function_names('CREATE VIEW v AS SELECT upper(name) FROM users;'); ---- -[] \ No newline at end of file +[] diff --git a/test/sql/parse_tools/scalar_functions/parse_functions.test b/test/sql/parse_tools/scalar_functions/parse_functions.test index 68c96e8..1f99173 100644 --- a/test/sql/parse_tools/scalar_functions/parse_functions.test +++ b/test/sql/parse_tools/scalar_functions/parse_functions.test @@ -77,6 +77,33 @@ SELECT parse_functions('WITH ranked AS (SELECT name, rank() OVER (ORDER BY salar ---- [{'function_name': rank, 'schema': main, 'context': select}, {'function_name': upper, 'schema': main, 'context': select}] +# functions in FROM subqueries and table function arguments +query I +SELECT parse_functions('SELECT * FROM (SELECT avg(n) AS a FROM range(5) t(n)) s'); +---- +[{'function_name': avg, 'schema': main, 'context': select}] + +query I +SELECT parse_functions('SELECT * FROM (SELECT avg(n) FROM range(abs(-5))) s JOIN (SELECT lower(x) FROM t) r ON true'); +---- +[{'function_name': avg, 'schema': main, 'context': select}, {'function_name': abs, 'schema': main, 'context': nested}, {'function_name': lower, 'schema': main, 'context': select}] + +# scalar and nested subqueries +query I +SELECT parse_functions('SELECT (SELECT max(x) FROM t)'); +---- +[{'function_name': max, 'schema': main, 'context': select}] + +query I +SELECT parse_functions('SELECT * FROM t WHERE x > (SELECT min(y) FROM t2)'); +---- +[{'function_name': min, 'schema': main, 'context': select}] + +query I +SELECT parse_functions('SELECT (SELECT sum(x) FROM (SELECT abs(y) AS x FROM t) nested)'); +---- +[{'function_name': sum, 'schema': main, 'context': select}, {'function_name': abs, 'schema': main, 'context': select}] + # no functions query I SELECT parse_functions('SELECT name, age FROM users;'); @@ -108,4 +135,4 @@ SELECT parse_functions('UPDATE users SET name = upper(name);'); query I SELECT parse_functions('CREATE VIEW v AS SELECT upper(name) FROM users;'); ---- -[] \ No newline at end of file +[]