|
15 | 15 | #include "duckdb/function/scalar_macro_function.hpp" |
16 | 16 | #include "duckdb/function/table_macro_function.hpp" |
17 | 17 | #include "duckdb/common/set.hpp" |
| 18 | +#include "duckdb/common/unordered_set.hpp" |
18 | 19 | #include "duckdb/parser/expression/function_expression.hpp" |
19 | 20 | #include "duckdb/parser/expression/subquery_expression.hpp" |
20 | 21 | #include "duckdb/parser/expression/window_expression.hpp" |
21 | 22 | #include "duckdb/parser/parsed_expression_iterator.hpp" |
22 | 23 | #include "duckdb/parser/statement/select_statement.hpp" |
| 24 | +#include "duckdb/parser/tableref/table_function_ref.hpp" |
23 | 25 |
|
24 | 26 | #include <algorithm> |
25 | 27 | #include <sstream> |
@@ -180,43 +182,81 @@ struct MacroReference { |
180 | 182 | //! empty when the call is unqualified |
181 | 183 | string schema; |
182 | 184 | string name; |
| 185 | + //! a FROM-clause table function call names a table macro, every other call a scalar one |
| 186 | + bool is_table_function; |
| 187 | + //! the expression the reference came from, to reclassify table function callees |
| 188 | + const ParsedExpression *source; |
183 | 189 | }; |
184 | 190 |
|
185 | | -static void CollectReferencedFunctionNames(ParsedExpression &expr, vector<MacroReference> &refs); |
| 191 | +struct MacroReferenceCollector { |
| 192 | +public: |
| 193 | + vector<MacroReference> refs; |
186 | 194 |
|
187 | | -static void CollectReferencedFunctionNames(QueryNode &node, vector<MacroReference> &refs) { |
188 | | - ParsedExpressionIterator::EnumerateQueryNodeChildren( |
189 | | - node, [&refs](unique_ptr<ParsedExpression> &child) { CollectReferencedFunctionNames(*child, refs); }); |
190 | | -} |
| 195 | +public: |
| 196 | + void Collect(ParsedExpression &expr) { |
| 197 | + if (expr.GetExpressionClass() == ExpressionClass::FUNCTION) { |
| 198 | + auto &function = expr.Cast<FunctionExpression>(); |
| 199 | + refs.push_back({function.GetQualifiedName().Schema().GetIdentifierName(), |
| 200 | + function.FunctionName().GetIdentifierName(), false, &expr}); |
| 201 | + } else if (expr.GetExpressionClass() == ExpressionClass::WINDOW) { |
| 202 | + // a macro invoked with OVER is a window expression carrying the callee name itself |
| 203 | + auto &window = expr.Cast<WindowExpression>(); |
| 204 | + refs.push_back({window.GetQualifiedName().Schema().GetIdentifierName(), |
| 205 | + window.FunctionName().GetIdentifierName(), false, &expr}); |
| 206 | + } else if (expr.GetExpressionClass() == ExpressionClass::SUBQUERY) { |
| 207 | + Collect(*expr.Cast<SubqueryExpression>().Subquery()->node); |
| 208 | + } |
| 209 | + ParsedExpressionIterator::EnumerateChildren(expr, [this](ParsedExpression &child) { Collect(child); }); |
| 210 | + } |
191 | 211 |
|
192 | | -static void CollectReferencedFunctionNames(ParsedExpression &expr, vector<MacroReference> &refs) { |
193 | | - if (expr.GetExpressionClass() == ExpressionClass::FUNCTION) { |
194 | | - auto &function = expr.Cast<FunctionExpression>(); |
195 | | - refs.push_back( |
196 | | - {function.GetQualifiedName().Schema().GetIdentifierName(), function.FunctionName().GetIdentifierName()}); |
197 | | - } else if (expr.GetExpressionClass() == ExpressionClass::WINDOW) { |
198 | | - // a macro invoked with OVER is a window expression carrying the callee name itself |
199 | | - auto &window = expr.Cast<WindowExpression>(); |
200 | | - refs.push_back( |
201 | | - {window.GetQualifiedName().Schema().GetIdentifierName(), window.FunctionName().GetIdentifierName()}); |
202 | | - } else if (expr.GetExpressionClass() == ExpressionClass::SUBQUERY) { |
203 | | - CollectReferencedFunctionNames(*expr.Cast<SubqueryExpression>().Subquery()->node, refs); |
| 212 | + void Collect(QueryNode &node) { |
| 213 | + ParsedExpressionIterator::EnumerateQueryNodeChildren( |
| 214 | + node, [this](unique_ptr<ParsedExpression> &child) { Collect(*child); }, |
| 215 | + [this](TableRef &ref) { |
| 216 | + if (ref.type == TableReferenceType::TABLE_FUNCTION) { |
| 217 | + auto &table_function = ref.Cast<TableFunctionRef>(); |
| 218 | + if (table_function.function) { |
| 219 | + table_function_roots.insert(table_function.function.get()); |
| 220 | + } |
| 221 | + } |
| 222 | + }); |
204 | 223 | } |
205 | | - ParsedExpressionIterator::EnumerateChildren( |
206 | | - expr, [&refs](ParsedExpression &child) { CollectReferencedFunctionNames(child, refs); }); |
207 | | -} |
| 224 | + |
| 225 | + //! reclassify the callees of table function refs: FROM f(...) names a table macro, while the |
| 226 | + //! calls inside its arguments stay scalar. Must run while the walked tree is alive, the source |
| 227 | + //! pointers dangle afterwards and are cleared here. |
| 228 | + void Finalize() { |
| 229 | + for (auto &ref : refs) { |
| 230 | + if (table_function_roots.count(ref.source) != 0) { |
| 231 | + ref.is_table_function = true; |
| 232 | + } |
| 233 | + ref.source = nullptr; |
| 234 | + } |
| 235 | + } |
| 236 | + |
| 237 | +private: |
| 238 | + unordered_set<const ParsedExpression *> table_function_roots; |
| 239 | +}; |
208 | 240 |
|
209 | 241 | static vector<MacroReference> CollectMacroReferences(CatalogEntry &entry) { |
210 | 242 | vector<MacroReference> refs; |
211 | 243 | auto ¯o_entry = entry.Cast<MacroCatalogEntry>(); |
212 | 244 | for (auto &member : macro_entry.macros) { |
213 | | - // walk a copy: enumeration hands out mutable references |
| 245 | + // one collector per member: Finalize compares pointers into the member's copy, so it must |
| 246 | + // run before that copy is destroyed |
| 247 | + MacroReferenceCollector collector; |
214 | 248 | if (member->type == MacroType::SCALAR_MACRO) { |
| 249 | + // walk a copy: enumeration hands out mutable references |
215 | 250 | auto expression = member->Cast<ScalarMacroFunction>().expression->Copy(); |
216 | | - CollectReferencedFunctionNames(*expression, refs); |
| 251 | + collector.Collect(*expression); |
| 252 | + collector.Finalize(); |
217 | 253 | } else { |
218 | 254 | auto query_node = member->Cast<TableMacroFunction>().query_node->Copy(); |
219 | | - CollectReferencedFunctionNames(*query_node, refs); |
| 255 | + collector.Collect(*query_node); |
| 256 | + collector.Finalize(); |
| 257 | + } |
| 258 | + for (auto &ref : collector.refs) { |
| 259 | + refs.push_back(std::move(ref)); |
220 | 260 | } |
221 | 261 | } |
222 | 262 | return refs; |
@@ -247,6 +287,11 @@ static void OrderMacrosByReference(catalog_entry_vector_t ¯os) { |
247 | 287 | if (callee == i) { |
248 | 288 | continue; |
249 | 289 | } |
| 290 | + // a FROM-clause call names a table macro and any other call a scalar one |
| 291 | + const bool callee_is_table = macros[callee].get().type == CatalogType::TABLE_MACRO_ENTRY; |
| 292 | + if (callee_is_table != ref.is_table_function) { |
| 293 | + continue; |
| 294 | + } |
250 | 295 | // a qualified call narrows the candidates to that schema |
251 | 296 | if (!ref.schema.empty() && |
252 | 297 | !StringUtil::CIEquals(ref.schema, macros[callee].get().ParentSchema().name.GetIdentifierName())) { |
|
0 commit comments