66#include < cctype>
77#include " duckdb/parser/statement/select_statement.hpp"
88#include " duckdb/parser/query_node/select_node.hpp"
9+ #include " duckdb/parser/query_node/cte_node.hpp"
910#include " duckdb/parser/tableref/basetableref.hpp"
1011#include " duckdb/parser/tableref/joinref.hpp"
1112#include " duckdb/parser/tableref/subqueryref.hpp"
1213#include " duckdb/function/scalar/nested_functions.hpp"
1314
14-
1515namespace duckdb {
1616
1717inline const char *ToString (TableContext context) {
@@ -130,7 +130,7 @@ static void ExtractTablesFromQueryNode(
130130 if (node.type == QueryNodeType::SELECT_NODE) {
131131 auto &select_node = (SelectNode &)node;
132132
133- // Emit CTE definitions
133+ // Handle CTE definitions
134134 for (const auto &entry : select_node.cte_map .map ) {
135135 results.push_back (TableRefResult{
136136 " " , entry.first , TableContext::CTE
@@ -144,6 +144,23 @@ static void ExtractTablesFromQueryNode(
144144 if (select_node.from_table ) {
145145 ExtractTablesFromRef (*select_node.from_table , results, context, true , &select_node.cte_map );
146146 }
147+ }
148+ // for ctes, we need an extra step to extract the cte body, and then the rest of the statement
149+ // don't actually record any details from this node in the result otherwise it will be duplicated in the recursive calls below.
150+ else if (node.type == QueryNodeType::CTE_NODE) {
151+ auto &cte_node = (CTENode &)node;
152+
153+ // Extract tables from the CTE query definition
154+ if (cte_node.query ) {
155+ ExtractTablesFromQueryNode (*cte_node.query , results, TableContext::From, cte_map);
156+ }
157+
158+ // Extract tables from the child query (the main query that uses the CTE)
159+ if (cte_node.child ) {
160+ // Pass the existing CTE map to the child query
161+ // The current CTE will be available for reference in the child
162+ ExtractTablesFromQueryNode (*cte_node.child , results, context, cte_map);
163+ }
147164 }
148165}
149166
0 commit comments