Repository: ibis-project/ibis

Issue:
SQLAlchemy: self-references are not properly implemented (or tested)
A test case triggering the error. Self-references are not correctly catalogued during compiler-analysis

``` python
t = con.table('functional_alltypes')
t2 = t.view()
expr = t[-(t.string_col == t2.string_col).any()]
expr.compile()
```



Discussion:

Resolving patch:
diff --git a/ibis/sql/compiler.py b/ibis/sql/compiler.py
--- a/ibis/sql/compiler.py
+++ b/ibis/sql/compiler.py
@@ -716,7 +716,6 @@ def _ref_check(self, node, in_subquery=False):
                 if (not is_aliased and
                         self.ctx.has_ref(node, parent_contexts=True)):
                     self.ctx.make_alias(node)
-
             elif not self.ctx.has_ref(node):
                 self.ctx.make_alias(node)
 
diff --git a/ibis/sql/transforms.py b/ibis/sql/transforms.py
--- a/ibis/sql/transforms.py
+++ b/ibis/sql/transforms.py
@@ -99,11 +99,7 @@ def _ref_check(self, expr):
             pass
         else:
             # Foreign ref
-            if isinstance(node, ops.SelfReference):
-                foreign_table = node.table
-            else:
-                foreign_table = expr
-
+            foreign_table = expr
             self.foreign_table = foreign_table
 
     def _is_root(self, what):


Test patch:
diff --git a/ibis/sql/sqlite/tests/test_functions.py b/ibis/sql/sqlite/tests/test_functions.py
--- a/ibis/sql/sqlite/tests/test_functions.py
+++ b/ibis/sql/sqlite/tests/test_functions.py
@@ -287,8 +287,6 @@ def test_distinct_aggregates(self):
         self._execute_aggregation(table, exprs)
 
     def test_not_exists_works(self):
-        pytest.skip('blocked on self-reference bug')
-
         t = self.alltypes
         t2 = t.view()
 
diff --git a/ibis/sql/tests/test_compiler.py b/ibis/sql/tests/test_compiler.py
--- a/ibis/sql/tests/test_compiler.py
+++ b/ibis/sql/tests/test_compiler.py
@@ -661,6 +661,16 @@ def _case_search_case(self):
                 .when(t.c < 0, t.a * 2)
                 .end())
 
+    def _case_self_reference_in_exists(self):
+        t = self.con.table('functional_alltypes')
+        t2 = t.view()
+
+        cond = (t.string_col == t2.string_col).any()
+        semi = t[cond]
+        anti = t[-cond]
+
+        return semi, anti
+
 
 class TestSelectSQL(unittest.TestCase, ExprTestCases):
 
@@ -1665,6 +1675,31 @@ def test_not_exists(self):
 )"""
         assert result == expected
 
+    def test_self_reference_in_exists(self):
+        semi, anti = self._case_self_reference_in_exists()
+
+        result = to_sql(semi)
+        expected = """\
+SELECT t0.*
+FROM functional_alltypes t0
+WHERE EXISTS (
+  SELECT 1
+  FROM functional_alltypes t1
+  WHERE t0.`string_col` = t1.`string_col`
+)"""
+        assert result == expected
+
+        result = to_sql(anti)
+        expected = """\
+SELECT t0.*
+FROM functional_alltypes t0
+WHERE NOT EXISTS (
+  SELECT 1
+  FROM functional_alltypes t1
+  WHERE t0.`string_col` = t1.`string_col`
+)"""
+        assert result == expected
+
     def test_sort_by(self):
         cases = self._case_sort_by()
 
diff --git a/ibis/sql/tests/test_sqlalchemy.py b/ibis/sql/tests/test_sqlalchemy.py
--- a/ibis/sql/tests/test_sqlalchemy.py
+++ b/ibis/sql/tests/test_sqlalchemy.py
@@ -352,8 +352,30 @@ def test_cte_factor_distinct_but_equal(self):
 
         self._compare_sqla(expr, stmt)
 
-    def test_self_reference(self):
-        pass
+    def test_self_reference_join(self):
+        t0 = self.sa_star1.alias('t0')
+        t1 = self.sa_star1.alias('t1')
+
+        case = self._case_self_reference_join()
+
+        table_set = t0.join(t1, t0.c.foo_id == t1.c.bar_id)
+        expected = sa.select([t0]).select_from(table_set)
+        self._compare_sqla(case, expected)
+
+    def test_self_reference_in_not_exists(self):
+        semi, anti = self._case_self_reference_in_exists()
+
+        s1 = self.sa_alltypes.alias('t0')
+        s2 = self.sa_alltypes.alias('t1')
+
+        cond = (sa.exists([L(1)]).select_from(s1)
+                .where(s1.c.string_col == s2.c.string_col))
+
+        ex_semi = sa.select([s1]).where(cond)
+        ex_anti = sa.select([s1]).where(~cond)
+
+        self._compare_sqla(semi, ex_semi)
+        self._compare_sqla(anti, ex_anti)
 
     def test_where_uncorrelated_subquery(self):
         expr = self._case_where_uncorrelated_subquery()