diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2019-08-29 14:45:23 -0400 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2019-11-04 13:22:43 -0500 |
| commit | 29330ec1596f12462c501a65404ff52005b16b6c (patch) | |
| tree | be20b85ae3939cdbc4f790fadd4f4372421891d4 /test/sql/test_selectable.py | |
| parent | db47859dca999b9d1679b513fe855e408d7d07c4 (diff) | |
| download | sqlalchemy-29330ec1596f12462c501a65404ff52005b16b6c.tar.gz | |
Add anonymizing context to cache keys, comparison; convert traversal
Created new visitor system called "internal traversal" that
applies a data driven approach to the concept of a class that
defines its own traversal steps, in contrast to the existing
style of traversal now known as "external traversal" where
the visitor class defines the traversal, i.e. the SQLCompiler.
The internal traversal system now implements get_children(),
_copy_internals(), compare() and _cache_key() for most Core elements.
Core elements with special needs like Select still implement
some of these methods directly however most of these methods
are no longer explicitly implemented.
The data-driven system is also applied to ORM elements that
take part in SQL expressions so that these objects, like mappers,
aliasedclass, query options, etc. can all participate in the
cache key process.
Still not considered is that this approach to defining traversibility
will be used to create some kind of generic introspection system
that works across Core / ORM. It's also not clear if
real statement caching using the _cache_key() method is feasible,
if it is shown that running _cache_key() is nearly as expensive as
compiling in any case. Because it is data driven, it is more
straightforward to optimize using inlined code, as is the case now,
as well as potentially using C code to speed it up.
In addition, the caching sytem now accommodates for anonymous
name labels, which is essential so that constructs which have
anonymous labels can be cacheable, that is, their position
within a statement in relation to other anonymous names causes
them to generate an integer counter relative to that construct
which will be the same every time. Gathering of bound parameters
from any cache key generation is also now required as there is
no use case for a cache key that does not extract bound parameter
values.
Applies-to: #4639
Change-Id: I0660584def8627cad566719ee98d3be045db4b8d
Diffstat (limited to 'test/sql/test_selectable.py')
| -rw-r--r-- | test/sql/test_selectable.py | 58 |
1 files changed, 26 insertions, 32 deletions
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"}), |
