Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions src/parse_functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -73,6 +77,8 @@ static unique_ptr<GlobalTableFunctionState> ParseFunctionsInit(ClientContext &co
return make_uniq<ParseFunctionsState>();
}

static void ExtractFunctionsFromQueryNode(const QueryNode &node, std::vector<FunctionResult> &results);

class FunctionExtractor {
public:
static void ExtractFromExpression(const ParsedExpression &expr,
Expand Down Expand Up @@ -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) {
Expand All @@ -163,6 +177,44 @@ class FunctionExtractor {
}
};

static void ExtractFunctionsFromRef(const TableRef &ref, std::vector<FunctionResult> &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<FunctionResult> &results) {
if (node.type == QueryNodeType::SELECT_NODE) {
Expand Down Expand Up @@ -202,6 +254,10 @@ static void ExtractFunctionsFromQueryNode(const QueryNode &node, std::vector<Fun
}
}
}

if (select_node.from_table) {
ExtractFunctionsFromRef(*select_node.from_table, results);
}
}
}

Expand Down
29 changes: 28 additions & 1 deletion test/sql/parse_tools/scalar_functions/parse_function_names.test
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,33 @@ SELECT parse_function_names('WITH ranked AS (SELECT name, rank() OVER (ORDER BY
----
[rank, upper]

# functions in FROM subqueries and table function arguments
query I
SELECT parse_function_names('SELECT * FROM (SELECT avg(n) AS a FROM range(5) t(n)) s');
----
[avg]

query I
SELECT parse_function_names('SELECT * FROM (SELECT avg(n) FROM range(abs(-5))) s JOIN (SELECT lower(x) FROM t) r ON true');
----
[avg, abs, lower]

# scalar and nested subqueries
query I
SELECT parse_function_names('SELECT (SELECT max(x) FROM t)');
----
[max]

query I
SELECT parse_function_names('SELECT * FROM t WHERE x > (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;');
Expand Down Expand Up @@ -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;');
----
[]
[]
29 changes: 28 additions & 1 deletion test/sql/parse_tools/scalar_functions/parse_functions.test
Original file line number Diff line number Diff line change
Expand Up @@ -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;');
Expand Down Expand Up @@ -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;');
----
[]
[]