44#include " duckdb/catalog/catalog.hpp"
55#include " duckdb/catalog/catalog_entry/scalar_macro_catalog_entry.hpp"
66#include " duckdb/catalog/catalog_entry/table_macro_catalog_entry.hpp"
7+ #include " duckdb/common/case_insensitive_map.hpp"
78#include " duckdb/function/scalar_macro_function.hpp"
89#include " duckdb/function/table_macro_function.hpp"
910#include " duckdb/main/attached_database.hpp"
1011#include " duckdb/main/client_context.hpp"
1112#include " duckdb/parser/expression/columnref_expression.hpp"
1213#include " duckdb/parser/expression/constant_expression.hpp"
14+ #include " duckdb/parser/expression/function_expression.hpp"
15+ #include " duckdb/parser/expression/subquery_expression.hpp"
1316#include " duckdb/parser/parsed_data/create_macro_info.hpp"
17+ #include " duckdb/parser/parsed_expression_iterator.hpp"
1418#include " duckdb/parser/parser.hpp"
1519#include " duckdb/parser/statement/select_statement.hpp"
1620
2327
2428namespace duckdb {
2529
30+ static void QualifyWorkerFunctions (QueryNode &node, const case_insensitive_set_t &worker_functions,
31+ const string &catalog_name, const string &schema_name);
32+
33+ static void QualifyWorkerFunctions (ParsedExpression &expression,
34+ const case_insensitive_set_t &worker_functions,
35+ const string &catalog_name, const string &schema_name) {
36+ if (expression.GetExpressionClass () == ExpressionClass::FUNCTION ) {
37+ auto &function = expression.Cast <FunctionExpression>();
38+ if (function.catalog .empty () && function.schema .empty () &&
39+ worker_functions.find (function.function_name ) != worker_functions.end ()) {
40+ function.catalog = catalog_name;
41+ function.schema = schema_name;
42+ }
43+ } else if (expression.GetExpressionClass () == ExpressionClass::SUBQUERY ) {
44+ auto &subquery = expression.Cast <SubqueryExpression>();
45+ QualifyWorkerFunctions (*subquery.subquery ->node , worker_functions, catalog_name, schema_name);
46+ }
47+
48+ ParsedExpressionIterator::EnumerateChildren (expression, [&](ParsedExpression &child) {
49+ QualifyWorkerFunctions (child, worker_functions, catalog_name, schema_name);
50+ });
51+ }
52+
53+ static void QualifyWorkerFunctions (QueryNode &node, const case_insensitive_set_t &worker_functions,
54+ const string &catalog_name, const string &schema_name) {
55+ ParsedExpressionIterator::EnumerateQueryNodeChildren (
56+ node, [&](unique_ptr<ParsedExpression> &expression) {
57+ QualifyWorkerFunctions (*expression, worker_functions, catalog_name, schema_name);
58+ });
59+ }
60+
61+ static void AddFunctionNames (const vgi::CatalogRpcContext &rpc_ctx, const string &schema_name,
62+ const char *function_type, ClientContext &context,
63+ case_insensitive_set_t &worker_functions) {
64+ for (const auto &function :
65+ vgi::InvokeCatalogSchemaContentsFunctions (rpc_ctx, schema_name, function_type, context)) {
66+ worker_functions.insert (function.name );
67+ }
68+ }
69+
70+ static case_insensitive_set_t LoadWorkerFunctionNames (const vgi::CatalogRpcContext &rpc_ctx,
71+ const VgiSchemaEntry &schema,
72+ const std::vector<vgi::VgiMacroInfo> ¯os,
73+ CatalogType macro_catalog_type,
74+ bool trust_empty_kinds,
75+ ClientContext &context) {
76+ case_insensitive_set_t worker_functions;
77+ for (const auto ¯o : macros) {
78+ worker_functions.insert (macro.name );
79+ }
80+
81+ const auto &counts = schema.GetEstimatedCounts ();
82+ if (!trust_empty_kinds || counts.scalar_function != 0 ) {
83+ AddFunctionNames (rpc_ctx, schema.name , " SCALAR_FUNCTION" , context, worker_functions);
84+ }
85+ if (!trust_empty_kinds || counts.aggregate_function != 0 ) {
86+ AddFunctionNames (rpc_ctx, schema.name , " AGGREGATE_FUNCTION" , context, worker_functions);
87+ }
88+ if (!trust_empty_kinds || counts.table_function != 0 ) {
89+ AddFunctionNames (rpc_ctx, schema.name , " TABLE_FUNCTION" , context, worker_functions);
90+ }
91+
92+ // Table macros can use scalar macros in their SELECT expressions. Scalar
93+ // macros cannot call table macros, and macros of the current kind are already
94+ // present in `macros`, so this is the only additional macro inventory needed.
95+ if (macro_catalog_type == CatalogType::TABLE_MACRO_ENTRY &&
96+ (!trust_empty_kinds || counts.macro != 0 )) {
97+ for (const auto ¯o :
98+ vgi::InvokeCatalogSchemaContentsMacros (rpc_ctx, schema.name , " SCALAR_MACRO" , context)) {
99+ worker_functions.insert (macro.name );
100+ }
101+ }
102+ return worker_functions;
103+ }
104+
26105VgiMacroSet::VgiMacroSet (Catalog &catalog, VgiSchemaEntry &schema, CatalogType macro_type)
27106 : VgiCatalogSet(catalog, &schema), schema_(schema), macro_catalog_type_(macro_type) {
28107}
@@ -86,6 +165,8 @@ void VgiMacroSet::LoadEntries(ClientContext &context, const std::lock_guard<std:
86165 rpc_ctx.entity_qualifier = schema_.name ;
87166
88167 auto macros = vgi::InvokeCatalogSchemaContentsMacros (rpc_ctx, schema_.name , rpc_type, context);
168+ auto worker_functions = LoadWorkerFunctionNames (rpc_ctx, schema_, macros, macro_catalog_type_,
169+ ReadTrustEmptyKinds (context), context);
89170
90171 for (auto ¯o_info : macros) {
91172 try {
@@ -99,6 +180,7 @@ void VgiMacroSet::LoadEntries(ClientContext &context, const std::lock_guard<std:
99180 macro_info.name .c_str ());
100181 continue ;
101182 }
183+ QualifyWorkerFunctions (*expressions[0 ], worker_functions, catalog_.GetName (), schema_.name );
102184 macro_func = make_uniq<ScalarMacroFunction>(std::move (expressions[0 ]));
103185 } else {
104186 // Table macro: parse query
@@ -111,6 +193,7 @@ void VgiMacroSet::LoadEntries(ClientContext &context, const std::lock_guard<std:
111193 continue ;
112194 }
113195 auto &select = parser.statements [0 ]->Cast <SelectStatement>();
196+ QualifyWorkerFunctions (*select.node , worker_functions, catalog_.GetName (), schema_.name );
114197 macro_func = make_uniq<TableMacroFunction>(std::move (select.node ));
115198 }
116199
0 commit comments