summaryrefslogtreecommitdiff
path: root/test/sql/test_selectable.py
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2019-08-29 14:45:23 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2019-11-04 13:22:43 -0500
commit29330ec1596f12462c501a65404ff52005b16b6c (patch)
treebe20b85ae3939cdbc4f790fadd4f4372421891d4 /test/sql/test_selectable.py
parentdb47859dca999b9d1679b513fe855e408d7d07c4 (diff)
downloadsqlalchemy-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.py58
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"}),