Repository: tobymao/sqlglot

Issue:
optimize problems
#### First problem: optimize with incorrect output
```
from sqlglot.optimizer import optimize

sql="""
    with b as (
    select price1 + price2 as total_price, amount from a
    )

    select total_price * amount as cost from b
    """
    
sql = optimize(sql).sql(pretty=True)
print(sql)
```
result:
```
SELECT
  "a"."price1" + "a"."price2" * "a"."amount" AS "cost"
FROM "a" AS "a"
```


#### Second problem: `cost` in `SELECT` is with double quote, but the one after `ORDER BY` is with single quote
```
from sqlglot.optimizer import optimize

sql="""
    select total_price * amount as 'cost' from b order by 'cost'
    """
sql = optimize(sql).sql(pretty=True)
print(sql)
```
result:
```
SELECT
  "b"."total_price" * "b"."amount" AS "cost"
FROM "b" AS "b"
ORDER BY
  'cost'
```


Discussion:

Resolving patch:
diff --git a/sqlglot/optimizer/merge_subqueries.py b/sqlglot/optimizer/merge_subqueries.py
--- a/sqlglot/optimizer/merge_subqueries.py
+++ b/sqlglot/optimizer/merge_subqueries.py
@@ -47,6 +47,17 @@ def merge_subqueries(expression, leave_tables_isolated=False):
 }
 
 
+# Projections in the outer query that are instances of these types can be replaced
+# without getting wrapped in parentheses, because the precedence won't be altered.
+SAFE_TO_REPLACE_UNWRAPPED = (
+    exp.Column,
+    exp.EQ,
+    exp.Func,
+    exp.NEQ,
+    exp.Paren,
+)
+
+
 def merge_ctes(expression, leave_tables_isolated=False):
     scopes = traverse_scope(expression)
 
@@ -293,8 +304,17 @@ def _merge_expressions(outer_scope, inner_scope, alias):
         if not projection_name:
             continue
         columns_to_replace = outer_columns.get(projection_name, [])
+
+        expression = expression.unalias()
+        must_wrap_expression = not isinstance(expression, SAFE_TO_REPLACE_UNWRAPPED)
+
         for column in columns_to_replace:
-            column.replace(expression.unalias().copy())
+            # Ensures we don't alter the intended operator precedence if there's additional
+            # context surrounding the outer expression (i.e. it's not a simple projection).
+            if isinstance(column.parent, (exp.Unary, exp.Binary)) and must_wrap_expression:
+                expression = exp.paren(expression, copy=False)
+
+            column.replace(expression.copy())
 
 
 def _merge_where(outer_scope, inner_scope, from_or_join):
diff --git a/sqlglot/optimizer/simplify.py b/sqlglot/optimizer/simplify.py
--- a/sqlglot/optimizer/simplify.py
+++ b/sqlglot/optimizer/simplify.py
@@ -400,6 +400,7 @@ def simplify_parens(expression):
         or not isinstance(this, exp.Binary)
         or (isinstance(this, exp.Add) and isinstance(parent, exp.Add))
         or (isinstance(this, exp.Mul) and isinstance(parent, exp.Mul))
+        or (isinstance(this, exp.Mul) and isinstance(parent, (exp.Add, exp.Sub)))
     ):
         return expression.this
     return expression
diff --git a/sqlglot/schema.py b/sqlglot/schema.py
--- a/sqlglot/schema.py
+++ b/sqlglot/schema.py
@@ -313,7 +313,9 @@ def _normalize(self, schema: t.Dict) -> t.Dict:
         return normalized_mapping
 
     def _normalize_table(self, table: exp.Table | str, dialect: DialectType = None) -> exp.Table:
-        normalized_table = exp.maybe_parse(table, into=exp.Table, dialect=dialect or self.dialect, copy=True)
+        normalized_table = exp.maybe_parse(
+            table, into=exp.Table, dialect=dialect or self.dialect, copy=True
+        )
 
         for arg in TABLE_ARGS:
             value = normalized_table.args.get(arg)


Test patch:
diff --git a/tests/fixtures/optimizer/merge_subqueries.sql b/tests/fixtures/optimizer/merge_subqueries.sql
--- a/tests/fixtures/optimizer/merge_subqueries.sql
+++ b/tests/fixtures/optimizer/merge_subqueries.sql
@@ -2,6 +2,28 @@
 SELECT a, b FROM (SELECT a, b FROM x);
 SELECT x.a AS a, x.b AS b FROM x AS x;
 
+# title: Wrap addition in a multiplication
+SELECT c * 2 AS d FROM (SELECT a + b AS c FROM x);
+SELECT (x.a + x.b) * 2 AS d FROM x AS x;
+
+# title: Wrap addition in an addition
+# note: The "simplify" rule will unwrap this
+SELECT c + d AS e FROM (SELECT a + b AS c, a AS d FROM x);
+SELECT (x.a + x.b) + x.a AS e FROM x AS x;
+
+# title: Wrap multiplication in an addition
+# note: The "simplify" rule will unwrap this
+WITH cte AS (SELECT a * b AS c, a AS d FROM x) SELECT c + d AS e FROM cte;
+SELECT (x.a * x.b) + x.a AS e FROM x AS x;
+
+# title: Don't wrap function
+SELECT 2 * foo AS bar FROM (SELECT CAST(b AS DOUBLE) AS foo FROM x);
+SELECT 2 * CAST(x.b AS DOUBLE) AS bar FROM x AS x;
+
+# title: Don't wrap a wrapped expression
+SELECT foo * 2 AS bar FROM (SELECT (1 + 2 + 3) AS foo FROM x);
+SELECT (1 + 2 + 3) * 2 AS bar FROM x AS x;
+
 # title: Inner table alias is merged
 SELECT a, b FROM (SELECT a, b FROM x AS q) AS r;
 SELECT q.a AS a, q.b AS b FROM x AS q;
diff --git a/tests/fixtures/optimizer/optimizer.sql b/tests/fixtures/optimizer/optimizer.sql
--- a/tests/fixtures/optimizer/optimizer.sql
+++ b/tests/fixtures/optimizer/optimizer.sql
@@ -693,3 +693,11 @@ GROUP BY
   "x"."a" + 1 + 1
 HAVING
   "x"."a" + 1 + 1 + 1 + 1 > 1;
+
+# title: replace alias with mult expression without wrapping it
+WITH cte AS (SELECT a * b AS c, a AS d, b as e FROM x) SELECT c + d - (c - e) AS f FROM cte;
+SELECT
+  "x"."a" * "x"."b" + "x"."a" - (
+    "x"."a" * "x"."b" - "x"."b"
+  ) AS "f"
+FROM "x" AS "x";
diff --git a/tests/fixtures/optimizer/simplify.sql b/tests/fixtures/optimizer/simplify.sql
--- a/tests/fixtures/optimizer/simplify.sql
+++ b/tests/fixtures/optimizer/simplify.sql
@@ -354,6 +354,12 @@ a + 4;
 a + (1 + 1) + (10);
 a + 12;
 
+a + (1 * 1) + (1 - (1 * 1));
+a + 1;
+
+a + (b * c) + (d - (e * f));
+a + b * c + (d - e * f);
+
 5 + 4 * 3;
 17;