Repository: tobymao/sqlglot

Issue:
qualify_tables incorrectly modifies CTEs in CTAS
**Before you file an issue**
- Make sure you specify the "read" dialect eg. parse_one(sql, read="spark")
- Check if the issue still exists on main 

**Fully reproducible code snippet**

This create table statement includes a CTE. This used to be unchanged by `qualify_tables`, but now the CTEs are incorrectly modified with the db/catalog prefixes. I suspect this is a regression from https://github.com/tobymao/sqlglot/pull/2862

I had initially thought that `qualify_tables` was only meant for Subqueryable and it would be a no-op for everything else. The above PR suggests that it should now work for other statement types as well.

```py
import sqlglot
from sqlglot.optimizer.qualify import qualify_tables

print(
    qualify_tables(
        sqlglot.parse_one(
            """
create table active_customer_ltv as (

with purchases as (
  select * from ecommerce.purchases
)

select
  active_customers.user_fk
  , active_customers.email
  , active_customers.last_purchase_date
  , sum(purchases.purchase_amount) as lifetime_purchase_amount
  , count(distinct(purchases.pk)) as lifetime_purchase_count
  , sum(purchases.purchase_amount) / count(distinct(purchases.pk)) as average_purchase_amount
from
  active_customers
join
  purchases
  on active_customers.user_fk = purchases.user_fk
group by 1,2,3

)
""",
            dialect="snowflake",
        ),
        db="mydb",
        catalog="mycatalog",
        dialect="snowflake",
    ).sql(pretty=True)
)

```


**Official Documentation**
Please include links to official SQL documentation related to your issue.



Discussion:

Resolving patch:
diff --git a/sqlglot/optimizer/qualify_tables.py b/sqlglot/optimizer/qualify_tables.py
--- a/sqlglot/optimizer/qualify_tables.py
+++ b/sqlglot/optimizer/qualify_tables.py
@@ -54,8 +54,9 @@ def _qualify(table: exp.Table) -> None:
                 table.set("catalog", catalog)
 
     if not isinstance(expression, exp.Subqueryable):
-        for table in expression.find_all(exp.Table):
-            _qualify(table)
+        for node, *_ in expression.walk(prune=lambda n, *_: isinstance(n, exp.Unionable)):
+            if isinstance(node, exp.Table):
+                _qualify(node)
 
     for scope in traverse_scope(expression):
         for derived_table in itertools.chain(scope.ctes, scope.derived_tables):
diff --git a/sqlglot/optimizer/scope.py b/sqlglot/optimizer/scope.py
--- a/sqlglot/optimizer/scope.py
+++ b/sqlglot/optimizer/scope.py
@@ -482,11 +482,12 @@ def traverse_scope(expression: exp.Expression) -> t.List[Scope]:
 
     Args:
         expression (exp.Expression): expression to traverse
+
     Returns:
         list[Scope]: scope instances
     """
     if isinstance(expression, exp.Unionable) or (
-        isinstance(expression, exp.DDL) and isinstance(expression.expression, exp.Subqueryable)
+        isinstance(expression, exp.DDL) and isinstance(expression.expression, exp.Unionable)
     ):
         return list(_traverse_scope(Scope(expression)))
 


Test patch:
diff --git a/tests/fixtures/optimizer/qualify_tables.sql b/tests/fixtures/optimizer/qualify_tables.sql
--- a/tests/fixtures/optimizer/qualify_tables.sql
+++ b/tests/fixtures/optimizer/qualify_tables.sql
@@ -138,3 +138,11 @@ SELECT * FROM c.db.t AS t CROSS JOIN JSON_ARRAY_ELEMENTS(t.response) WITH ORDINA
 # title: alter table
 ALTER TABLE t ADD PRIMARY KEY (id) NOT ENFORCED;
 ALTER TABLE c.db.t ADD PRIMARY KEY (id) NOT ENFORCED;
+
+# title: create statement with cte
+CREATE TABLE t1 AS (WITH cte AS (SELECT x FROM t2) SELECT * FROM cte);
+CREATE TABLE c.db.t1 AS (WITH cte AS (SELECT x FROM c.db.t2 AS t2) SELECT * FROM cte AS cte);
+
+# title: insert statement with cte
+WITH cte AS (SELECT b FROM y) INSERT INTO s SELECT * FROM cte;
+WITH cte AS (SELECT b FROM c.db.y AS y) INSERT INTO c.db.s SELECT * FROM cte AS cte;