summaryrefslogtreecommitdiff
path: root/test/orm/test_subquery_relations.py
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2014-01-21 20:10:23 -0500
committerMike Bayer <mike_mp@zzzcomputing.com>2014-01-21 20:10:23 -0500
commit07fb90c6cc14de6d02cf4be592c57d56831f59f7 (patch)
tree050ef65db988559c60f7aa40f2d0bfe24947e548 /test/orm/test_subquery_relations.py
parent560fd1d5ed643a1b0f95296f3b840c1963bbe67f (diff)
parentee1f4d21037690ad996c5eacf7e1200e92f2fbaa (diff)
downloadsqlalchemy-ticket_2501.tar.gz
Merge branch 'master' into ticket_2501ticket_2501
Conflicts: lib/sqlalchemy/orm/mapper.py
Diffstat (limited to 'test/orm/test_subquery_relations.py')
-rw-r--r--test/orm/test_subquery_relations.py253
1 files changed, 249 insertions, 4 deletions
diff --git a/test/orm/test_subquery_relations.py b/test/orm/test_subquery_relations.py
index a6cc37691..f36820e70 100644
--- a/test/orm/test_subquery_relations.py
+++ b/test/orm/test_subquery_relations.py
@@ -10,6 +10,7 @@ from sqlalchemy.testing import eq_, assert_raises, \
assert_raises_message
from sqlalchemy.testing.assertsql import CompiledSQL
from sqlalchemy.testing import fixtures
+from sqlalchemy.testing.entities import ComparableEntity
from test.orm import _fixtures
import sqlalchemy as sa
@@ -632,7 +633,6 @@ class EagerTest(_fixtures.FixtureTest, testing.AssertsCompiledSQL):
], q.all())
self.assert_sql_count(testing.db, go, 6)
- @testing.fails_on('maxdb', 'FIXME: unknown')
def test_limit(self):
"""Limit operations combined with lazy-load relationships."""
@@ -706,7 +706,6 @@ class EagerTest(_fixtures.FixtureTest, testing.AssertsCompiledSQL):
eq_([User(id=7, address=Address(id=1))], l)
self.assert_sql_count(testing.db, go, 2)
- @testing.fails_on('maxdb', 'FIXME: unknown')
def test_many_to_one(self):
users, Address, addresses, User = (self.tables.users,
self.classes.Address,
@@ -1144,7 +1143,6 @@ class SelfReferentialTest(fixtures.MappedTest):
Column('parent_id', Integer, ForeignKey('nodes.id')),
Column('data', String(30)))
- @testing.fails_on('maxdb', 'FIXME: unknown')
def test_basic(self):
nodes = self.tables.nodes
@@ -1309,7 +1307,6 @@ class SelfReferentialTest(fixtures.MappedTest):
]), d)
self.assert_sql_count(testing.db, go, 3)
- @testing.fails_on('maxdb', 'FIXME: unknown')
def test_no_depth(self):
"""no join depth is set, so no eager loading occurs."""
@@ -1563,3 +1560,251 @@ class CyclicalInheritingEagerTestTwo(fixtures.DeclarativeMappedTest,
d = session.query(Director).options(subqueryload('*')).first()
assert len(list(session)) == 3
+
+class SubqueryloadDistinctTest(fixtures.DeclarativeMappedTest,
+ testing.AssertsCompiledSQL):
+ __dialect__ = 'default'
+
+ run_inserts = 'once'
+ run_deletes = None
+
+ @classmethod
+ def setup_classes(cls):
+ Base = cls.DeclarativeBasic
+
+ class Director(Base):
+ __tablename__ = 'director'
+ id = Column(Integer, primary_key=True,
+ test_needs_autoincrement=True)
+ name = Column(String(50))
+
+ class DirectorPhoto(Base):
+ __tablename__ = 'director_photo'
+ id = Column(Integer, primary_key=True,
+ test_needs_autoincrement=True)
+ path = Column(String(255))
+ director_id = Column(Integer, ForeignKey('director.id'))
+ director = relationship(Director, backref="photos")
+
+ class Movie(Base):
+ __tablename__ = 'movie'
+ id = Column(Integer, primary_key=True, test_needs_autoincrement=True)
+ director_id = Column(Integer, ForeignKey('director.id'))
+ director = relationship(Director, backref="movies")
+ title = Column(String(50))
+ credits = relationship("Credit", backref="movie")
+
+ class Credit(Base):
+ __tablename__ = 'credit'
+ id = Column(Integer, primary_key=True, test_needs_autoincrement=True)
+ movie_id = Column(Integer, ForeignKey('movie.id'))
+
+ @classmethod
+ def insert_data(cls):
+ Movie = cls.classes.Movie
+ Director = cls.classes.Director
+ DirectorPhoto = cls.classes.DirectorPhoto
+ Credit = cls.classes.Credit
+
+ d = Director(name='Woody Allen')
+ d.photos = [DirectorPhoto(path='/1.jpg'),
+ DirectorPhoto(path='/2.jpg')]
+ d.movies = [Movie(title='Manhattan', credits=[Credit(), Credit()]),
+ Movie(title='Sweet and Lowdown', credits=[Credit()])]
+ sess = create_session()
+ sess.add_all([d])
+ sess.flush()
+
+ def test_distinct_strategy_opt_m2o(self):
+ self._run_test_m2o(True, None)
+ self._run_test_m2o(False, None)
+
+ def test_distinct_unrelated_opt_m2o(self):
+ self._run_test_m2o(None, True)
+ self._run_test_m2o(None, False)
+
+ def _run_test_m2o(self,
+ director_strategy_level,
+ photo_strategy_level):
+
+ # test where the innermost is m2o, e.g.
+ # Movie->director
+
+ Movie = self.classes.Movie
+ Director = self.classes.Director
+
+ Movie.director.property.distinct_target_key = director_strategy_level
+ Director.photos.property.distinct_target_key = photo_strategy_level
+
+ # the DISTINCT is controlled by
+ # only the Movie->director relationship, *not* the
+ # Director.photos
+ expect_distinct = director_strategy_level in (True, None)
+
+ s = create_session()
+
+ q = (
+ s.query(Movie)
+ .options(
+ subqueryload(Movie.director)
+ .subqueryload(Director.photos)
+ )
+ )
+ ctx = q._compile_context()
+
+ q2 = ctx.attributes[
+ ('subquery', (inspect(Movie), inspect(Movie).attrs.director))
+ ]
+ self.assert_compile(
+ q2,
+ 'SELECT director.id AS director_id, '
+ 'director.name AS director_name, '
+ 'anon_1.movie_director_id AS anon_1_movie_director_id '
+ 'FROM (SELECT%s movie.director_id AS movie_director_id '
+ 'FROM movie) AS anon_1 '
+ 'JOIN director ON director.id = anon_1.movie_director_id '
+ 'ORDER BY anon_1.movie_director_id' % (
+ " DISTINCT" if expect_distinct else "")
+ )
+
+ ctx2 = q2._compile_context()
+ result = s.execute(q2)
+ rows = result.fetchall()
+
+ if expect_distinct:
+ eq_(rows, [
+ (1, 'Woody Allen', 1),
+ ])
+ else:
+ eq_(rows, [
+ (1, 'Woody Allen', 1), (1, 'Woody Allen', 1),
+ ])
+
+ q3 = ctx2.attributes[
+ ('subquery', (inspect(Director), inspect(Director).attrs.photos))
+ ]
+
+ self.assert_compile(
+ q3,
+ 'SELECT director_photo.id AS director_photo_id, '
+ 'director_photo.path AS director_photo_path, '
+ 'director_photo.director_id AS director_photo_director_id, '
+ 'director_1.id AS director_1_id '
+ 'FROM (SELECT%s movie.director_id AS movie_director_id '
+ 'FROM movie) AS anon_1 '
+ 'JOIN director AS director_1 ON director_1.id = anon_1.movie_director_id '
+ 'JOIN director_photo ON director_1.id = director_photo.director_id '
+ 'ORDER BY director_1.id' % (
+ " DISTINCT" if expect_distinct else "")
+ )
+ result = s.execute(q3)
+ rows = result.fetchall()
+ if expect_distinct:
+ eq_(set(tuple(t) for t in rows), set([
+ (1, u'/1.jpg', 1, 1),
+ (2, u'/2.jpg', 1, 1),
+ ]))
+ else:
+ # oracle might not order the way we expect here
+ eq_(set(tuple(t) for t in rows), set([
+ (1, u'/1.jpg', 1, 1),
+ (2, u'/2.jpg', 1, 1),
+ (1, u'/1.jpg', 1, 1),
+ (2, u'/2.jpg', 1, 1),
+ ]))
+
+
+ movies = q.all()
+
+ # check number of persistent objects in session
+ eq_(len(list(s)), 5)
+
+ def test_cant_do_distinct_in_joins(self):
+ """the DISTINCT feature here works when the m2o is in the innermost
+ mapper, but when we are just joining along relationships outside
+ of that, we can still have dupes, and there's no solution to that.
+
+ """
+ Movie = self.classes.Movie
+ Credit = self.classes.Credit
+
+ s = create_session()
+
+ q = (
+ s.query(Credit)
+ .options(
+ subqueryload(Credit.movie)
+ .subqueryload(Movie.director)
+ )
+ )
+
+ ctx = q._compile_context()
+
+ q2 = ctx.attributes[
+ ('subquery', (inspect(Credit), Credit.movie.property))
+ ]
+ ctx2 = q2._compile_context()
+ q3 = ctx2.attributes[
+ ('subquery', (inspect(Movie), Movie.director.property))
+ ]
+
+ result = s.execute(q3)
+ eq_(
+ result.fetchall(),
+ [
+ (1, 'Woody Allen', 1), (1, 'Woody Allen', 1),
+ ]
+ )
+
+
+class JoinedNoLoadConflictTest(fixtures.DeclarativeMappedTest):
+ """test for [ticket:2887]"""
+
+ @classmethod
+ def setup_classes(cls):
+ Base = cls.DeclarativeBasic
+
+ class Parent(ComparableEntity, Base):
+ __tablename__ = 'parent'
+
+ id = Column(Integer, primary_key=True, test_needs_autoincrement=True)
+ name = Column(String(20))
+
+ children = relationship('Child',
+ back_populates='parent',
+ lazy='noload'
+ )
+
+ class Child(ComparableEntity, Base):
+ __tablename__ = 'child'
+
+ id = Column(Integer, primary_key=True, test_needs_autoincrement=True)
+ name = Column(String(20))
+ parent_id = Column(Integer, ForeignKey('parent.id'))
+
+ parent = relationship('Parent', back_populates='children', lazy='joined')
+
+ @classmethod
+ def insert_data(cls):
+ Parent = cls.classes.Parent
+ Child = cls.classes.Child
+
+ s = Session()
+ s.add(Parent(name='parent', children=[Child(name='c1')]))
+ s.commit()
+
+ def test_subqueryload_on_joined_noload(self):
+ Parent = self.classes.Parent
+ Child = self.classes.Child
+
+ s = Session()
+
+ # here we have Parent->subqueryload->Child->joinedload->parent->noload->children.
+ # the actual subqueryload has to emit *after* we've started populating
+ # Parent->subqueryload->child.
+ parent = s.query(Parent).options([subqueryload('children')]).first()
+ eq_(
+ parent.children,
+ [Child(name='c1')]
+ )
+