summaryrefslogtreecommitdiff
path: root/test/sql
diff options
context:
space:
mode:
Diffstat (limited to 'test/sql')
-rw-r--r--test/sql/test_compare.py515
-rw-r--r--test/sql/test_external_traversal.py (renamed from test/sql/test_generative.py)1
-rw-r--r--test/sql/test_operators.py3
-rw-r--r--test/sql/test_selectable.py58
-rw-r--r--test/sql/test_utils.py2
5 files changed, 427 insertions, 152 deletions
diff --git a/test/sql/test_compare.py b/test/sql/test_compare.py
index d48a8ed33..5d21960b7 100644
--- a/test/sql/test_compare.py
+++ b/test/sql/test_compare.py
@@ -32,6 +32,7 @@ from sqlalchemy.sql import operators
from sqlalchemy.sql import True_
from sqlalchemy.sql import type_coerce
from sqlalchemy.sql import visitors
+from sqlalchemy.sql.base import HasCacheKey
from sqlalchemy.sql.elements import _label_reference
from sqlalchemy.sql.elements import _textual_label_reference
from sqlalchemy.sql.elements import Annotated
@@ -46,13 +47,13 @@ from sqlalchemy.sql.functions import FunctionElement
from sqlalchemy.sql.functions import GenericFunction
from sqlalchemy.sql.functions import ReturnTypeFromArgs
from sqlalchemy.sql.selectable import _OffsetLimitParam
+from sqlalchemy.sql.selectable import AliasedReturnsRows
from sqlalchemy.sql.selectable import FromGrouping
from sqlalchemy.sql.selectable import Selectable
from sqlalchemy.sql.selectable import SelectStatementGrouping
-from sqlalchemy.testing import assert_raises_message
+from sqlalchemy.sql.visitors import InternalTraversal
from sqlalchemy.testing import eq_
from sqlalchemy.testing import fixtures
-from sqlalchemy.testing import is_
from sqlalchemy.testing import is_false
from sqlalchemy.testing import is_true
from sqlalchemy.testing import ne_
@@ -63,8 +64,17 @@ meta = MetaData()
meta2 = MetaData()
table_a = Table("a", meta, Column("a", Integer), Column("b", String))
+table_b_like_a = Table("b2", meta, Column("a", Integer), Column("b", String))
+
table_a_2 = Table("a", meta2, Column("a", Integer), Column("b", String))
+table_a_2_fs = Table(
+ "a", meta2, Column("a", Integer), Column("b", String), schema="fs"
+)
+table_a_2_bs = Table(
+ "a", meta2, Column("a", Integer), Column("b", String), schema="bs"
+)
+
table_b = Table("b", meta, Column("a", Integer), Column("b", Integer))
table_c = Table("c", meta, Column("x", Integer), Column("y", Integer))
@@ -72,8 +82,18 @@ table_c = Table("c", meta, Column("x", Integer), Column("y", Integer))
table_d = Table("d", meta, Column("y", Integer), Column("z", Integer))
-class CompareAndCopyTest(fixtures.TestBase):
+class MyEntity(HasCacheKey):
+ def __init__(self, name, element):
+ self.name = name
+ self.element = element
+
+ _cache_key_traversal = [
+ ("name", InternalTraversal.dp_string),
+ ("element", InternalTraversal.dp_clauseelement),
+ ]
+
+class CoreFixtures(object):
# lambdas which return a tuple of ColumnElement objects.
# must return at least two objects that should compare differently.
# to test more varieties of "difference" additional objects can be added.
@@ -100,11 +120,47 @@ class CompareAndCopyTest(fixtures.TestBase):
text("select a, b, c from table").columns(
a=Integer, b=String, c=Integer
),
+ text("select a, b, c from table where foo=:bar").bindparams(
+ bindparam("bar", Integer)
+ ),
+ text("select a, b, c from table where foo=:foo").bindparams(
+ bindparam("foo", Integer)
+ ),
+ text("select a, b, c from table where foo=:bar").bindparams(
+ bindparam("bar", String)
+ ),
),
lambda: (
column("q") == column("x"),
column("q") == column("y"),
column("z") == column("x"),
+ column("z") + column("x"),
+ column("z") - column("x"),
+ column("x") - column("z"),
+ column("z") > column("x"),
+ # note these two are mathematically equivalent but for now they
+ # are considered to be different
+ column("z") >= column("x"),
+ column("x") <= column("z"),
+ column("q").between(5, 6),
+ column("q").between(5, 6, symmetric=True),
+ column("q").like("somstr"),
+ column("q").like("somstr", escape="\\"),
+ column("q").like("somstr", escape="X"),
+ ),
+ lambda: (
+ table_a.c.a,
+ table_a.c.a._annotate({"orm": True}),
+ table_a.c.a._annotate({"orm": True})._annotate({"bar": False}),
+ table_a.c.a._annotate(
+ {"orm": True, "parententity": MyEntity("a", table_a)}
+ ),
+ table_a.c.a._annotate(
+ {"orm": True, "parententity": MyEntity("b", table_a)}
+ ),
+ table_a.c.a._annotate(
+ {"orm": True, "parententity": MyEntity("b", select([table_a]))}
+ ),
),
lambda: (
cast(column("q"), Integer),
@@ -226,6 +282,58 @@ class CompareAndCopyTest(fixtures.TestBase):
.correlate_except(table_b),
),
lambda: (
+ select([table_a.c.a]).cte(),
+ select([table_a.c.a]).cte(recursive=True),
+ select([table_a.c.a]).cte(name="some_cte", recursive=True),
+ select([table_a.c.a]).cte(name="some_cte"),
+ select([table_a.c.a]).cte(name="some_cte").alias("other_cte"),
+ select([table_a.c.a])
+ .cte(name="some_cte")
+ .union_all(select([table_a.c.a])),
+ select([table_a.c.a])
+ .cte(name="some_cte")
+ .union_all(select([table_a.c.b])),
+ select([table_a.c.a]).lateral(),
+ select([table_a.c.a]).lateral(name="bar"),
+ table_a.tablesample(func.bernoulli(1)),
+ table_a.tablesample(func.bernoulli(1), seed=func.random()),
+ table_a.tablesample(func.bernoulli(1), seed=func.other_random()),
+ table_a.tablesample(func.hoho(1)),
+ table_a.tablesample(func.bernoulli(1), name="bar"),
+ table_a.tablesample(
+ func.bernoulli(1), name="bar", seed=func.random()
+ ),
+ ),
+ lambda: (
+ select([table_a.c.a]),
+ select([table_a.c.a]).prefix_with("foo"),
+ select([table_a.c.a]).prefix_with("foo", dialect="mysql"),
+ select([table_a.c.a]).prefix_with("foo", dialect="postgresql"),
+ select([table_a.c.a]).prefix_with("bar"),
+ select([table_a.c.a]).suffix_with("bar"),
+ ),
+ lambda: (
+ select([table_a_2.c.a]),
+ select([table_a_2_fs.c.a]),
+ select([table_a_2_bs.c.a]),
+ ),
+ lambda: (
+ select([table_a.c.a]),
+ select([table_a.c.a]).with_hint(None, "some hint"),
+ select([table_a.c.a]).with_hint(None, "some other hint"),
+ select([table_a.c.a]).with_hint(table_a, "some hint"),
+ select([table_a.c.a])
+ .with_hint(table_a, "some hint")
+ .with_hint(None, "some other hint"),
+ select([table_a.c.a]).with_hint(table_a, "some other hint"),
+ select([table_a.c.a]).with_hint(
+ table_a, "some hint", dialect_name="mysql"
+ ),
+ select([table_a.c.a]).with_hint(
+ table_a, "some hint", dialect_name="postgresql"
+ ),
+ ),
+ lambda: (
table_a.join(table_b, table_a.c.a == table_b.c.a),
table_a.join(
table_b, and_(table_a.c.a == table_b.c.a, table_a.c.b == 1)
@@ -273,12 +381,202 @@ class CompareAndCopyTest(fixtures.TestBase):
table("a", column("x"), column("y", Integer)),
table("a", column("q"), column("y", Integer)),
),
- lambda: (
- Table("a", MetaData(), Column("q", Integer), Column("b", String)),
- Table("b", MetaData(), Column("q", Integer), Column("b", String)),
- ),
+ lambda: (table_a, table_b),
]
+ def _complex_fixtures():
+ def one():
+ a1 = table_a.alias()
+ a2 = table_b_like_a.alias()
+
+ stmt = (
+ select([table_a.c.a, a1.c.b, a2.c.b])
+ .where(table_a.c.b == a1.c.b)
+ .where(a1.c.b == a2.c.b)
+ .where(a1.c.a == 5)
+ )
+
+ return stmt
+
+ def one_diff():
+ a1 = table_b_like_a.alias()
+ a2 = table_a.alias()
+
+ stmt = (
+ select([table_a.c.a, a1.c.b, a2.c.b])
+ .where(table_a.c.b == a1.c.b)
+ .where(a1.c.b == a2.c.b)
+ .where(a1.c.a == 5)
+ )
+
+ return stmt
+
+ def two():
+ inner = one().subquery()
+
+ stmt = select([table_b.c.a, inner.c.a, inner.c.b]).select_from(
+ table_b.join(inner, table_b.c.b == inner.c.b)
+ )
+
+ return stmt
+
+ def three():
+
+ a1 = table_a.alias()
+ a2 = table_a.alias()
+ ex = exists().where(table_b.c.b == a1.c.a)
+
+ stmt = (
+ select([a1.c.a, a2.c.a])
+ .select_from(a1.join(a2, a1.c.b == a2.c.b))
+ .where(ex)
+ )
+ return stmt
+
+ return [one(), one_diff(), two(), three()]
+
+ fixtures.append(_complex_fixtures)
+
+
+class CacheKeyFixture(object):
+ def _run_cache_key_fixture(self, fixture):
+ case_a = fixture()
+ case_b = fixture()
+
+ for a, b in itertools.combinations_with_replacement(
+ range(len(case_a)), 2
+ ):
+ if a == b:
+ a_key = case_a[a]._generate_cache_key()
+ b_key = case_b[b]._generate_cache_key()
+ eq_(a_key.key, b_key.key)
+
+ for a_param, b_param in zip(
+ a_key.bindparams, b_key.bindparams
+ ):
+ assert a_param.compare(b_param, compare_values=False)
+ else:
+ a_key = case_a[a]._generate_cache_key()
+ b_key = case_b[b]._generate_cache_key()
+
+ if a_key.key == b_key.key:
+ for a_param, b_param in zip(
+ a_key.bindparams, b_key.bindparams
+ ):
+ if not a_param.compare(b_param, compare_values=True):
+ break
+ else:
+ # this fails unconditionally since we could not
+ # find bound parameter values that differed.
+ # Usually we intended to get two distinct keys here
+ # so the failure will be more descriptive using the
+ # ne_() assertion.
+ ne_(a_key.key, b_key.key)
+ else:
+ ne_(a_key.key, b_key.key)
+
+ # ClauseElement-specific test to ensure the cache key
+ # collected all the bound parameters
+ if isinstance(case_a[a], ClauseElement) and isinstance(
+ case_b[b], ClauseElement
+ ):
+ assert_a_params = []
+ assert_b_params = []
+ visitors.traverse_depthfirst(
+ case_a[a], {}, {"bindparam": assert_a_params.append}
+ )
+ visitors.traverse_depthfirst(
+ case_b[b], {}, {"bindparam": assert_b_params.append}
+ )
+
+ # note we're asserting the order of the params as well as
+ # if there are dupes or not. ordering has to be deterministic
+ # and matches what a traversal would provide.
+ # regular traverse_depthfirst does produce dupes in cases like
+ # select([some_alias]).
+ # select_from(join(some_alias, other_table))
+ # where a bound parameter is inside of some_alias. the
+ # cache key case is more minimalistic
+ eq_(
+ sorted(a_key.bindparams, key=lambda b: b.key),
+ sorted(
+ util.unique_list(assert_a_params), key=lambda b: b.key
+ ),
+ )
+ eq_(
+ sorted(b_key.bindparams, key=lambda b: b.key),
+ sorted(
+ util.unique_list(assert_b_params), key=lambda b: b.key
+ ),
+ )
+
+
+class CacheKeyTest(CacheKeyFixture, CoreFixtures, fixtures.TestBase):
+ def test_cache_key(self):
+ for fixture in self.fixtures:
+ self._run_cache_key_fixture(fixture)
+
+ def test_cache_key_unknown_traverse(self):
+ class Foobar1(ClauseElement):
+ _traverse_internals = [
+ ("key", InternalTraversal.dp_anon_name),
+ ("type_", InternalTraversal.dp_unknown_structure),
+ ]
+
+ def __init__(self, key, type_):
+ self.key = key
+ self.type_ = type_
+
+ f1 = Foobar1("foo", String())
+ eq_(f1._generate_cache_key(), None)
+
+ def test_cache_key_no_method(self):
+ class Foobar1(ClauseElement):
+ pass
+
+ class Foobar2(ColumnElement):
+ pass
+
+ # the None for cache key will prevent objects
+ # which contain these elements from being cached.
+ f1 = Foobar1()
+ eq_(f1._generate_cache_key(), None)
+
+ f2 = Foobar2()
+ eq_(f2._generate_cache_key(), None)
+
+ s1 = select([column("q"), Foobar2()])
+
+ eq_(s1._generate_cache_key(), None)
+
+ def test_get_children_no_method(self):
+ class Foobar1(ClauseElement):
+ pass
+
+ class Foobar2(ColumnElement):
+ pass
+
+ f1 = Foobar1()
+ eq_(f1.get_children(), [])
+
+ f2 = Foobar2()
+ eq_(f2.get_children(), [])
+
+ def test_copy_internals_no_method(self):
+ class Foobar1(ClauseElement):
+ pass
+
+ class Foobar2(ColumnElement):
+ pass
+
+ f1 = Foobar1()
+ f2 = Foobar2()
+
+ f1._copy_internals()
+ f2._copy_internals()
+
+
+class CompareAndCopyTest(CoreFixtures, fixtures.TestBase):
@classmethod
def setup_class(cls):
# TODO: we need to get dialects here somehow, perhaps in test_suite?
@@ -293,7 +591,10 @@ class CompareAndCopyTest(fixtures.TestBase):
cls
for cls in class_hierarchy(ClauseElement)
if issubclass(cls, (ColumnElement, Selectable))
- and "__init__" in cls.__dict__
+ and (
+ "__init__" in cls.__dict__
+ or issubclass(cls, AliasedReturnsRows)
+ )
and not issubclass(cls, (Annotated))
and "orm" not in cls.__module__
and "compiler" not in cls.__module__
@@ -318,123 +619,16 @@ class CompareAndCopyTest(fixtures.TestBase):
):
if a == b:
is_true(
- case_a[a].compare(
- case_b[b], arbitrary_expression=True
- ),
+ case_a[a].compare(case_b[b], compare_annotations=True),
"%r != %r" % (case_a[a], case_b[b]),
)
else:
is_false(
- case_a[a].compare(
- case_b[b], arbitrary_expression=True
- ),
+ case_a[a].compare(case_b[b], compare_annotations=True),
"%r == %r" % (case_a[a], case_b[b]),
)
- def test_cache_key(self):
- def assert_params_append(assert_params):
- def append(param):
- if param._value_required_for_cache:
- assert_params.append(param)
- else:
- is_(param.value, None)
-
- return append
-
- for fixture in self.fixtures:
- case_a = fixture()
- case_b = fixture()
-
- for a, b in itertools.combinations_with_replacement(
- range(len(case_a)), 2
- ):
-
- assert_a_params = []
- assert_b_params = []
-
- visitors.traverse_depthfirst(
- case_a[a],
- {},
- {"bindparam": assert_params_append(assert_a_params)},
- )
- visitors.traverse_depthfirst(
- case_b[b],
- {},
- {"bindparam": assert_params_append(assert_b_params)},
- )
- if assert_a_params:
- assert_raises_message(
- NotImplementedError,
- "bindparams collection argument required ",
- case_a[a]._cache_key,
- )
- if assert_b_params:
- assert_raises_message(
- NotImplementedError,
- "bindparams collection argument required ",
- case_b[b]._cache_key,
- )
-
- if not assert_a_params and not assert_b_params:
- if a == b:
- eq_(case_a[a]._cache_key(), case_b[b]._cache_key())
- else:
- ne_(case_a[a]._cache_key(), case_b[b]._cache_key())
-
- def test_cache_key_gather_bindparams(self):
- for fixture in self.fixtures:
- case_a = fixture()
- case_b = fixture()
-
- # in the "bindparams" case, the cache keys for bound parameters
- # with only different values will be the same, but the params
- # themselves are gathered into a collection.
- for a, b in itertools.combinations_with_replacement(
- range(len(case_a)), 2
- ):
- a_params = {"bindparams": []}
- b_params = {"bindparams": []}
- if a == b:
- a_key = case_a[a]._cache_key(**a_params)
- b_key = case_b[b]._cache_key(**b_params)
- eq_(a_key, b_key)
-
- if a_params["bindparams"]:
- for a_param, b_param in zip(
- a_params["bindparams"], b_params["bindparams"]
- ):
- assert a_param.compare(b_param)
- else:
- a_key = case_a[a]._cache_key(**a_params)
- b_key = case_b[b]._cache_key(**b_params)
-
- if a_key == b_key:
- for a_param, b_param in zip(
- a_params["bindparams"], b_params["bindparams"]
- ):
- if not a_param.compare(b_param):
- break
- else:
- assert False, "Bound parameters are all the same"
- else:
- ne_(a_key, b_key)
-
- assert_a_params = []
- assert_b_params = []
- visitors.traverse_depthfirst(
- case_a[a], {}, {"bindparam": assert_a_params.append}
- )
- visitors.traverse_depthfirst(
- case_b[b], {}, {"bindparam": assert_b_params.append}
- )
-
- # note we're asserting the order of the params as well as
- # if there are dupes or not. ordering has to be deterministic
- # and matches what a traversal would provide.
- eq_(a_params["bindparams"], assert_a_params)
- eq_(b_params["bindparams"], assert_b_params)
-
def test_compare_col_identity(self):
stmt1 = (
select([table_a.c.a, table_b.c.b])
@@ -473,8 +667,9 @@ class CompareAndCopyTest(fixtures.TestBase):
assert case_a[0].compare(case_b[0])
- clone = case_a[0]._clone()
- clone._copy_internals()
+ clone = visitors.replacement_traverse(
+ case_a[0], {}, lambda elem: None
+ )
assert clone.compare(case_b[0])
@@ -511,6 +706,37 @@ class CompareAndCopyTest(fixtures.TestBase):
class CompareClausesTest(fixtures.TestBase):
+ def test_compare_metadata_tables(self):
+ # metadata Table objects cache on their own identity, not their
+ # structure. This is mainly to reduce the size of cache keys
+ # as well as reduce computational overhead, as Table objects have
+ # very large internal state and they are also generally global
+ # objects.
+
+ t1 = Table("a", MetaData(), Column("q", Integer), Column("p", Integer))
+ t2 = Table("a", MetaData(), Column("q", Integer), Column("p", Integer))
+
+ ne_(t1._generate_cache_key(), t2._generate_cache_key())
+
+ eq_(t1._generate_cache_key().key, (t1,))
+
+ def test_compare_adhoc_tables(self):
+ # non-metadata tables compare on their structure. these objects are
+ # not commonly used.
+
+ # note this test is a bit redundant as we have a similar test
+ # via the fixtures also
+ t1 = table("a", Column("q", Integer), Column("p", Integer))
+ t2 = table("a", Column("q", Integer), Column("p", Integer))
+ t3 = table("b", Column("q", Integer), Column("p", Integer))
+ t4 = table("a", Column("q", Integer), Column("x", Integer))
+
+ eq_(t1._generate_cache_key(), t2._generate_cache_key())
+
+ ne_(t1._generate_cache_key(), t3._generate_cache_key())
+ ne_(t1._generate_cache_key(), t4._generate_cache_key())
+ ne_(t3._generate_cache_key(), t4._generate_cache_key())
+
def test_compare_comparison_associative(self):
l1 = table_c.c.x == table_d.c.y
@@ -521,6 +747,15 @@ class CompareClausesTest(fixtures.TestBase):
is_true(l1.compare(l2))
is_false(l1.compare(l3))
+ def test_compare_comparison_non_commutative_inverses(self):
+ l1 = table_c.c.x >= table_d.c.y
+ l2 = table_d.c.y < table_c.c.x
+ l3 = table_d.c.y <= table_c.c.x
+
+ # we're not doing this kind of commutativity right now.
+ is_false(l1.compare(l2))
+ is_false(l1.compare(l3))
+
def test_compare_clauselist_associative(self):
l1 = and_(table_c.c.x == table_d.c.y, table_c.c.y == table_d.c.z)
@@ -624,3 +859,45 @@ class CompareClausesTest(fixtures.TestBase):
use_proxies=True,
)
)
+
+ def test_compare_annotated_clears_mapping(self):
+ t = table("t", column("x"), column("y"))
+ x_a = t.c.x._annotate({"foo": True})
+ x_b = t.c.x._annotate({"foo": True})
+
+ is_true(x_a.compare(x_b, compare_annotations=True))
+ is_false(
+ x_a.compare(x_b._annotate({"bar": True}), compare_annotations=True)
+ )
+
+ s1 = select([t.c.x])._annotate({"foo": True})
+ s2 = select([t.c.x])._annotate({"foo": True})
+
+ is_true(s1.compare(s2, compare_annotations=True))
+
+ is_false(
+ s1.compare(s2._annotate({"bar": True}), compare_annotations=True)
+ )
+
+ def test_compare_annotated_wo_annotations(self):
+ t = table("t", column("x"), column("y"))
+ x_a = t.c.x._annotate({})
+ x_b = t.c.x._annotate({"foo": True})
+
+ is_true(t.c.x.compare(x_a))
+ is_true(x_b.compare(x_a))
+
+ is_true(x_a.compare(t.c.x))
+ is_false(x_a.compare(t.c.y))
+ is_false(t.c.y.compare(x_a))
+ is_true((t.c.x == 5).compare(x_a == 5))
+ is_false((t.c.y == 5).compare(x_a == 5))
+
+ s = select([t]).subquery()
+ x_p = s.c.x
+ is_false(x_a.compare(x_p))
+ is_false(t.c.x.compare(x_p))
+ x_p_a = x_p._annotate({})
+ is_true(x_p_a.compare(x_p))
+ is_true(x_p.compare(x_p_a))
+ is_false(x_p_a.compare(x_a))
diff --git a/test/sql/test_generative.py b/test/sql/test_external_traversal.py
index 8d347a522..8bfe5cf6f 100644
--- a/test/sql/test_generative.py
+++ b/test/sql/test_external_traversal.py
@@ -55,6 +55,7 @@ class TraversalTest(fixtures.TestBase, AssertsExecutionResults):
# identity semantics.
class A(ClauseElement):
__visit_name__ = "a"
+ _traverse_internals = []
def __init__(self, expr):
self.expr = expr
diff --git a/test/sql/test_operators.py b/test/sql/test_operators.py
index 637f1f8a5..06cfdc4b5 100644
--- a/test/sql/test_operators.py
+++ b/test/sql/test_operators.py
@@ -118,11 +118,14 @@ class DefaultColumnComparatorTest(fixtures.TestBase):
)
)
+ modifiers = operator(left, right).modifiers
+
assert operator(left, right).compare(
BinaryExpression(
coercions.expect(roles.WhereHavingRole, left),
coercions.expect(roles.WhereHavingRole, right),
operator,
+ modifiers=modifiers,
)
)
diff --git a/test/sql/test_selectable.py b/test/sql/test_selectable.py
index 2bc7ccc93..184e4a99c 100644
--- a/test/sql/test_selectable.py
+++ b/test/sql/test_selectable.py
@@ -1070,7 +1070,7 @@ class SelectableTest(
s4 = s3.with_only_columns([table2.c.b])
self.assert_compile(s4, "SELECT t2.b FROM t2")
- def test_from_list_warning_against_existing(self):
+ def test_from_list_against_existing_one(self):
c1 = Column("c1", Integer)
s = select([c1])
@@ -1081,7 +1081,7 @@ class SelectableTest(
self.assert_compile(s, "SELECT t.c1 FROM t")
- def test_from_list_recovers_after_warning(self):
+ def test_from_list_against_existing_two(self):
c1 = Column("c1", Integer)
c2 = Column("c2", Integer)
@@ -1090,18 +1090,11 @@ class SelectableTest(
# force a compile.
eq_(str(s), "SELECT c1")
- @testing.emits_warning()
- def go():
- return Table("t", MetaData(), c1, c2)
-
- t = go()
+ t = Table("t", MetaData(), c1, c2)
eq_(c1._from_objects, [t])
eq_(c2._from_objects, [t])
- # 's' has been baked. Can't afford
- # not caching select._froms.
- # hopefully the warning will clue the user
self.assert_compile(s, "SELECT t.c1 FROM t")
self.assert_compile(select([c1]), "SELECT t.c1 FROM t")
self.assert_compile(select([c2]), "SELECT t.c2 FROM t")
@@ -1124,6 +1117,26 @@ class SelectableTest(
"foo",
)
+ def test_whereclause_adapted(self):
+ table1 = table("t1", column("a"))
+
+ s1 = select([table1]).subquery()
+
+ s2 = select([s1]).where(s1.c.a == 5)
+
+ assert s2._whereclause.left.table is s1
+
+ ta = select([table1]).subquery()
+
+ s3 = sql_util.ClauseAdapter(ta).traverse(s2)
+
+ assert s1 not in s3._froms
+
+ # these are new assumptions with the newer approach that
+ # actively swaps out whereclause and others
+ assert s3._whereclause.left.table is not s1
+ assert s3._whereclause.left.table in s3._froms
+
class RefreshForNewColTest(fixtures.TestBase):
def test_join_uninit(self):
@@ -2241,25 +2254,6 @@ class AnnotationsTest(fixtures.TestBase):
annot = obj._annotate({})
ne_(set([obj]), set([annot]))
- def test_compare(self):
- t = table("t", column("x"), column("y"))
- x_a = t.c.x._annotate({})
- assert t.c.x.compare(x_a)
- assert x_a.compare(t.c.x)
- assert not x_a.compare(t.c.y)
- assert not t.c.y.compare(x_a)
- assert (t.c.x == 5).compare(x_a == 5)
- assert not (t.c.y == 5).compare(x_a == 5)
-
- s = select([t]).subquery()
- x_p = s.c.x
- assert not x_a.compare(x_p)
- assert not t.c.x.compare(x_p)
- x_p_a = x_p._annotate({})
- assert x_p_a.compare(x_p)
- assert x_p.compare(x_p_a)
- assert not x_p_a.compare(x_a)
-
def test_proxy_set_iteration_includes_annotated(self):
from sqlalchemy.schema import Column
@@ -2542,13 +2536,13 @@ class AnnotationsTest(fixtures.TestBase):
):
# the columns clause isn't changed at all
assert sel._raw_columns[0].table is a1
- assert sel._froms[0] is sel._froms[1].left
+ assert sel._froms[0].element is sel._froms[1].left.element
eq_(str(s), str(sel))
# when we are modifying annotations sets only
- # partially, each element is copied unconditionally
- # when encountered.
+ # partially, elements are copied uniquely based on id().
+ # this is new as of 1.4, previously they'd be copied every time
for sel in (
sql_util._deep_deannotate(s, {"foo": "bar"}),
sql_util._deep_annotate(s, {"foo": "bar"}),
diff --git a/test/sql/test_utils.py b/test/sql/test_utils.py
index 988d5331e..48d6de6db 100644
--- a/test/sql/test_utils.py
+++ b/test/sql/test_utils.py
@@ -7,6 +7,6 @@ from sqlalchemy.testing import fixtures
class MiscTest(fixtures.TestBase):
def test_column_element_no_visit(self):
class MyElement(ColumnElement):
- pass
+ _traverse_internals = []
eq_(sql_util.find_tables(MyElement(), check_columns=True), [])