Repository: tobymao/sqlglot

Issue:
Wrong Optimizer Behavior
Considering the following right join query, the optimizer wrongly creates an CTE where it pushes into the CTE the predicate `a.rank>3`.
However that can't be done without transforming the right outer join into an inner join. Because The predicate is filtering all NULL values that remain after the outer join. If that predicate is pushed down to the CTE, and the join is not transformed, then the NULL values, that originate after the join, will not be removed. 

In this specific case is simply filtering everything less or equal than 3. However,after the optimization, the CTE will generate NULL values after the join that aren't removed.

```
	select 
		 a.rank
		,a.name
		,b.name
		,b.rank
	from people as a
	right join people as b
	on a.rank = b.rank 
	where a.rank>3
```

wrongly optimizes into:
```
WITH "a" AS (
  SELECT
    "people"."rank" AS "rank",
    "people"."name" AS "name"
  FROM "people" AS "people"
  WHERE
    "people"."rank" > 3  -- WRONG
)
SELECT
  "a"."rank" AS "rank",
  "a"."name" AS "name",
  "people"."name" AS "name",
  "people"."rank" AS "rank"
FROM "a" AS "a"
RIGHT JOIN "people" AS "people"
  ON "a"."rank" = "people"."rank"
```

this is an correct query:
```
WITH "a" AS (
  SELECT
    "people"."rank" AS "rank",
    "people"."name" AS "name"
  FROM "people" AS "people"
)
SELECT
  "a"."rank" AS "rank",
  "a"."name" AS "name",
  "people"."name" AS "name",
  "people"."rank" AS "rank"
FROM "a" AS "a"
RIGHT JOIN "people" AS "people"
  ON "a"."rank" = "people"."rank"

  WHERE     "a"."rank" > 3  --  <===
```






Discussion:

Resolving patch:
diff --git a/sqlglot/optimizer/pushdown_predicates.py b/sqlglot/optimizer/pushdown_predicates.py
--- a/sqlglot/optimizer/pushdown_predicates.py
+++ b/sqlglot/optimizer/pushdown_predicates.py
@@ -27,7 +27,14 @@ def pushdown_predicates(expression):
         select = scope.expression
         where = select.args.get("where")
         if where:
-            pushdown(where.this, scope.selected_sources, scope_ref_count)
+            selected_sources = scope.selected_sources
+            # a right join can only push down to itself and not the source FROM table
+            for k, (node, source) in selected_sources.items():
+                parent = node.find_ancestor(exp.Join, exp.From)
+                if isinstance(parent, exp.Join) and parent.side == "RIGHT":
+                    selected_sources = {k: (node, source)}
+                    break
+            pushdown(where.this, selected_sources, scope_ref_count)
 
         # joins should only pushdown into itself, not to other joins
         # so we limit the selected sources to only itself
@@ -154,7 +161,7 @@ def nodes_for_predicate(predicate, sources, scope_ref_count):
             node = source.expression
 
         if isinstance(node, exp.Join):
-            if node.side:
+            if node.side and node.side != "RIGHT":
                 return {}
             nodes[table] = node
         elif isinstance(node, exp.Select) and len(tables) == 1:


Test patch:
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
@@ -334,3 +334,38 @@ SELECT
   "cte1"."b" AS "b",
   "cte1"."c" AS "c"
 FROM "cte1";
+
+# title: right join should not push down to from
+SELECT x.a, y.b
+FROM x
+RIGHT JOIN y
+ON x.a = y.b
+WHERE x.b = 1;
+SELECT
+  "x"."a" AS "a",
+  "y"."b" AS "b"
+FROM "x" AS "x"
+RIGHT JOIN "y" AS "y"
+  ON "x"."a" = "y"."b"
+WHERE
+  "x"."b" = 1;
+
+# title: right join can push down to itself
+SELECT x.a, y.b
+FROM x
+RIGHT JOIN y
+ON x.a = y.b
+WHERE y.b = 1;
+WITH "y_2" AS (
+  SELECT
+    "y"."b" AS "b"
+  FROM "y" AS "y"
+  WHERE
+    "y"."b" = 1
+)
+SELECT
+  "x"."a" AS "a",
+  "y"."b" AS "b"
+FROM "x" AS "x"
+RIGHT JOIN "y_2" AS "y"
+  ON "x"."a" = "y"."b";