summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2011-09-05 19:12:12 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2011-09-05 19:12:12 -0400
commit8c0e82238c39ee068a5fe143d06c5b681127d62f (patch)
tree35d6401fb245f01a778cbce76378978f06c39db4 /test
parent56e374776dfc5e6d371a27e24dc2646c844f4c0f (diff)
downloadsqlalchemy-8c0e82238c39ee068a5fe143d06c5b681127d62f.tar.gz
- Fixed bug regarding calculation of "from" list
for a select() element. The "from" calc is now delayed, so that if the construct uses a Column object that is not yet attached to a Table, but is later associated with a Table, it generates SQL using the table as a FROM. This change impacted fairly deeply the mechanics of how the FROM list as well as the "correlates" collection is calculated, as some "clause adaption" schemes (these are used very heavily in the ORM) were relying upon the fact that the "froms" collection would typically be cached before the adaption completed. The rework allows it such that the "froms" collection can be cleared and re-generated at any time. [ticket:2261] - RelationshipProperty.Comparator._criterion_exists() adds an "_orm_adapt" annotation to the correlates target, to work with the change in [ticket:2261]. It's not clear if the change to correlation+adaption mechanics will affect end user code yet. - FromClause now uses group_expirable_memoized_property for late-generated values like primary key, _columns, etc. The Select class adds some tokens to this object and has the nice effect that FromClause doesn't need to know about Select's names anymore. An additional change might be to have Select use a different group_expirable_memoized_property so that it's collection of attribute names are specific to Select though this isn't really necessary right now.
Diffstat (limited to 'test')
-rw-r--r--test/orm/inheritance/test_query.py7
-rw-r--r--test/orm/test_selectable.py1
-rw-r--r--test/sql/test_selectable.py117
3 files changed, 114 insertions, 11 deletions
diff --git a/test/orm/inheritance/test_query.py b/test/orm/inheritance/test_query.py
index 2560d2284..94dde71f8 100644
--- a/test/orm/inheritance/test_query.py
+++ b/test/orm/inheritance/test_query.py
@@ -186,13 +186,6 @@ def _produce_test(select_type):
eq_(sess.query(Person).all(), all_employees)
self.assert_sql_count(testing.db, go, {'':14, 'Polymorphic':9}.get(select_type, 10))
- def test_foo(self):
- sess = create_session()
-
- def go():
- eq_(sess.query(Person).options(subqueryload(Engineer.machines)).all(), all_employees)
- self.assert_sql_count(testing.db, go, {'':14, 'Unions':8, 'Polymorphic':7}.get(select_type, 8))
-
def test_primary_eager_aliasing(self):
sess = create_session()
diff --git a/test/orm/test_selectable.py b/test/orm/test_selectable.py
index 7e4a92de1..97849f845 100644
--- a/test/orm/test_selectable.py
+++ b/test/orm/test_selectable.py
@@ -60,7 +60,6 @@ class SelectableNoFromsTest(fixtures.MappedTest, AssertsCompiledSQL):
subset_select = select([common.c.id, common.c.data]).alias()
subset_mapper = mapper(Subset, subset_select)
-
sess = Session(bind=testing.db)
sess.add(Subset(data=1))
sess.flush()
diff --git a/test/sql/test_selectable.py b/test/sql/test_selectable.py
index 82d018af1..9c1f44e1a 100644
--- a/test/sql/test_selectable.py
+++ b/test/sql/test_selectable.py
@@ -64,16 +64,16 @@ class SelectableTest(fixtures.TestBase, AssertsExecutionResults, AssertsCompiled
assert s1.corresponding_column(scalar_select) is s1.c.foo
assert s2.corresponding_column(scalar_select) is s2.c.foo
-
+
def test_label_grouped_still_corresponds(self):
label = select([table1.c.col1]).label('foo')
label2 = label.self_group()
-
+
s1 = select([label])
s2 = select([label2])
assert s1.corresponding_column(label) is s1.c.foo
assert s2.corresponding_column(label) is s2.c.foo
-
+
def test_direct_correspondence_on_labels(self):
# this test depends on labels being part
# of the proxy set to get the right result
@@ -376,6 +376,117 @@ class SelectableTest(fixtures.TestBase, AssertsExecutionResults, AssertsCompiled
[s.c.col1, s.corresponding_column(c2)]
)
+ def test_from_list_deferred_constructor(self):
+ c1 = Column('c1', Integer)
+ c2 = Column('c2', Integer)
+
+ s = select([c1])
+
+ t = Table('t', MetaData(), c1, c2)
+
+ eq_(c1._from_objects, [t])
+ eq_(c2._from_objects, [t])
+
+ self.assert_compile(select([c1]),
+ "SELECT t.c1 FROM t")
+ self.assert_compile(select([c2]),
+ "SELECT t.c2 FROM t")
+
+ def test_from_list_deferred_whereclause(self):
+ c1 = Column('c1', Integer)
+ c2 = Column('c2', Integer)
+
+ s = select([c1]).where(c1==5)
+
+ t = Table('t', MetaData(), c1, c2)
+
+ eq_(c1._from_objects, [t])
+ eq_(c2._from_objects, [t])
+
+ self.assert_compile(select([c1]),
+ "SELECT t.c1 FROM t")
+ self.assert_compile(select([c2]),
+ "SELECT t.c2 FROM t")
+
+ def test_from_list_deferred_fromlist(self):
+ m = MetaData()
+ t1 = Table('t1', m, Column('x', Integer))
+
+ c1 = Column('c1', Integer)
+ s = select([c1]).where(c1==5).select_from(t1)
+
+ t2 = Table('t2', MetaData(), c1)
+
+ eq_(c1._from_objects, [t2])
+
+ self.assert_compile(select([c1]),
+ "SELECT t2.c1 FROM t2")
+
+ def test_from_list_deferred_cloning(self):
+ c1 = Column('c1', Integer)
+ c2 = Column('c2', Integer)
+
+ s = select([c1])
+ s2 = select([c2])
+ s3 = sql_util.ClauseAdapter(s).traverse(s2)
+
+ # the adaptation process needs the full
+ # FROM list so can't avoid the warning on
+ # this one
+ assert_raises_message(
+ exc.SAWarning,
+ r"\<class 'sqlalchemy.schema.Table'\> being associated ",
+ Table, 't', MetaData(), c1
+ )
+
+ def test_from_list_warning_against_existing(self):
+ c1 = Column('c1', Integer)
+ s = select([c1])
+
+ # force a compile.
+ eq_(str(s), "SELECT c1")
+
+ # this will emit a warning
+ assert_raises_message(
+ exc.SAWarning,
+ r"\<class 'sqlalchemy.schema.Table'\> being associated "
+ r"with \<class 'sqlalchemy.schema.Column'\> object after "
+ r"the \<class 'sqlalchemy.schema.Column'\> has already "
+ r"been used in a SQL generation; previously "
+ r"generated constructs may contain stale state.",
+ Table, 't', MetaData(), c1
+ )
+
+ def test_from_list_recovers_after_warning(self):
+ c1 = Column('c1', Integer)
+ c2 = Column('c2', Integer)
+
+ s = select([c1])
+
+ # force a compile.
+ eq_(str(s), "SELECT c1")
+
+ @testing.emits_warning()
+ def go():
+ return Table('t', MetaData(), c1, c2)
+ t = go()
+
+ 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")
+ self.assert_compile(select([c1]), "SELECT t.c1 FROM t")
+ self.assert_compile(select([c2]), "SELECT t.c2 FROM t")
+
+ def test_label_gen_resets_on_table(self):
+ c1 = Column('c1', Integer)
+ eq_(c1._label, "c1")
+ Table('t1', MetaData(), c1)
+ eq_(c1._label, "t1_c1")
+
class AnonLabelTest(fixtures.TestBase):
"""Test behaviors that we hope to change with [ticket:2168]."""