diff options
author | Hajime Nakagami <nakagami@gmail.com> | 2013-04-20 17:10:23 +0900 |
---|---|---|
committer | Hajime Nakagami <nakagami@gmail.com> | 2013-04-20 17:10:23 +0900 |
commit | fbcdba12f88d88c509fc34eb8aab3f501d1b705b (patch) | |
tree | f1ca2029cfd147478447d3cb98bae587a8ccb3c2 /test | |
parent | af9ed1ff16fce148af46fbff33286fc30e33773d (diff) | |
parent | 7f0ee900b6c35a9bff214f9ebb02c3fb98d1f7e1 (diff) | |
download | sqlalchemy-fbcdba12f88d88c509fc34eb8aab3f501d1b705b.tar.gz |
merge from default
Diffstat (limited to 'test')
-rw-r--r-- | test/dialect/test_mssql.py | 2 | ||||
-rw-r--r-- | test/engine/test_reconnect.py | 24 | ||||
-rw-r--r-- | test/orm/test_instrumentation.py | 14 | ||||
-rw-r--r-- | test/orm/test_session.py | 144 | ||||
-rw-r--r-- | test/orm/test_subquery_relations.py | 53 |
5 files changed, 234 insertions, 3 deletions
diff --git a/test/dialect/test_mssql.py b/test/dialect/test_mssql.py index 0dfda9015..f1cd3fe85 100644 --- a/test/dialect/test_mssql.py +++ b/test/dialect/test_mssql.py @@ -1949,7 +1949,7 @@ class TypeRoundTripTest(fixtures.TestBase, AssertsExecutionResults, ComparesTabl engine.execute(tbl.delete()) class MonkeyPatchedBinaryTest(fixtures.TestBase): - __only_on__ = 'mssql' + __only_on__ = 'mssql+pymssql' def test_unicode(self): module = __import__('pymssql') diff --git a/test/engine/test_reconnect.py b/test/engine/test_reconnect.py index 86f646f33..9aecb81a9 100644 --- a/test/engine/test_reconnect.py +++ b/test/engine/test_reconnect.py @@ -51,6 +51,7 @@ class MockCursor(object): def __init__(self, parent): self.explode = parent.explode self.description = () + self.closed = False def execute(self, *args, **kwargs): if self.explode == 'execute': raise MockDisconnect("Lost the DB connection on execute") @@ -60,10 +61,20 @@ class MockCursor(object): elif self.explode in ('rollback', 'rollback_no_disconnect'): raise MockError( "something broke on execute but we didn't lose the connection") + elif args and "select" in args[0]: + self.description = [('foo', None, None, None, None, None)] else: return + def fetchall(self): + if self.closed: + raise MockError("cursor closed") + return [] + def fetchone(self): + if self.closed: + raise MockError("cursor closed") + return None def close(self): - pass + self.closed = True db, dbapi = None, None class MockReconnectTest(fixtures.TestBase): @@ -294,6 +305,17 @@ class MockReconnectTest(fixtures.TestBase): conn.execute, select([1]) ) + def test_check_disconnect_no_cursor(self): + conn = db.connect() + result = conn.execute("select 1") + result.cursor.close() + conn.close() + assert_raises_message( + tsa.exc.DBAPIError, + "cursor closed", + list, result + ) + class CursorErrTest(fixtures.TestBase): def setup(self): diff --git a/test/orm/test_instrumentation.py b/test/orm/test_instrumentation.py index 3b548f0cd..3f8fc67b6 100644 --- a/test/orm/test_instrumentation.py +++ b/test/orm/test_instrumentation.py @@ -445,6 +445,20 @@ class MapperInitTest(fixtures.ORMTest): # C is not mapped in the current implementation assert_raises(sa.orm.exc.UnmappedClassError, class_mapper, C) + def test_del_warning(self): + class A(object): + def __del__(self): + pass + + assert_raises_message( + sa.exc.SAWarning, + r"__del__\(\) method on class " + "<class 'test.orm.test_instrumentation.A'> will cause " + "unreachable cycles and memory leaks, as SQLAlchemy " + "instrumentation often creates reference cycles. " + "Please remove this method.", + mapper, A, self.fixture() + ) class OnLoadTest(fixtures.ORMTest): """Check that Events.load is not hit in regular attributes operations.""" diff --git a/test/orm/test_session.py b/test/orm/test_session.py index 5c8968842..7c2e8a3b8 100644 --- a/test/orm/test_session.py +++ b/test/orm/test_session.py @@ -857,6 +857,150 @@ class SessionStateWFixtureTest(_fixtures.FixtureTest): assert sa.orm.attributes.instance_state(a).session_id is None +class NoCyclesOnTransientDetachedTest(_fixtures.FixtureTest): + """Test the instance_state._strong_obj link that it + is present only on persistent/pending objects and never + transient/detached. + + """ + run_inserts = None + + def setup(self): + mapper(self.classes.User, self.tables.users) + + def _assert_modified(self, u1): + assert sa.orm.attributes.instance_state(u1).modified + + def _assert_not_modified(self, u1): + assert not sa.orm.attributes.instance_state(u1).modified + + def _assert_cycle(self, u1): + assert sa.orm.attributes.instance_state(u1)._strong_obj is not None + + def _assert_no_cycle(self, u1): + assert sa.orm.attributes.instance_state(u1)._strong_obj is None + + def _persistent_fixture(self): + User = self.classes.User + u1 = User() + u1.name = "ed" + sess = Session() + sess.add(u1) + sess.flush() + return sess, u1 + + def test_transient(self): + User = self.classes.User + u1 = User() + u1.name = 'ed' + self._assert_no_cycle(u1) + self._assert_modified(u1) + + def test_transient_to_pending(self): + User = self.classes.User + u1 = User() + u1.name = 'ed' + self._assert_modified(u1) + self._assert_no_cycle(u1) + sess = Session() + sess.add(u1) + self._assert_cycle(u1) + sess.flush() + self._assert_no_cycle(u1) + self._assert_not_modified(u1) + + def test_dirty_persistent_to_detached_via_expunge(self): + sess, u1 = self._persistent_fixture() + u1.name = 'edchanged' + self._assert_cycle(u1) + sess.expunge(u1) + self._assert_no_cycle(u1) + + def test_dirty_persistent_to_detached_via_close(self): + sess, u1 = self._persistent_fixture() + u1.name = 'edchanged' + self._assert_cycle(u1) + sess.close() + self._assert_no_cycle(u1) + + def test_clean_persistent_to_detached_via_close(self): + sess, u1 = self._persistent_fixture() + self._assert_no_cycle(u1) + self._assert_not_modified(u1) + sess.close() + u1.name = 'edchanged' + self._assert_modified(u1) + self._assert_no_cycle(u1) + + def test_detached_to_dirty_deleted(self): + sess, u1 = self._persistent_fixture() + sess.expunge(u1) + u1.name = 'edchanged' + self._assert_no_cycle(u1) + sess.delete(u1) + self._assert_cycle(u1) + + def test_detached_to_dirty_persistent(self): + sess, u1 = self._persistent_fixture() + sess.expunge(u1) + u1.name = 'edchanged' + self._assert_modified(u1) + self._assert_no_cycle(u1) + sess.add(u1) + self._assert_cycle(u1) + self._assert_modified(u1) + + def test_detached_to_clean_persistent(self): + sess, u1 = self._persistent_fixture() + sess.expunge(u1) + self._assert_no_cycle(u1) + self._assert_not_modified(u1) + sess.add(u1) + self._assert_no_cycle(u1) + self._assert_not_modified(u1) + + def test_move_persistent_clean(self): + sess, u1 = self._persistent_fixture() + sess.close() + s2 = Session() + s2.add(u1) + self._assert_no_cycle(u1) + self._assert_not_modified(u1) + + def test_move_persistent_dirty(self): + sess, u1 = self._persistent_fixture() + u1.name = 'edchanged' + self._assert_cycle(u1) + self._assert_modified(u1) + sess.close() + self._assert_no_cycle(u1) + s2 = Session() + s2.add(u1) + self._assert_cycle(u1) + self._assert_modified(u1) + + @testing.requires.predictable_gc + def test_move_gc_session_persistent_dirty(self): + sess, u1 = self._persistent_fixture() + u1.name = 'edchanged' + self._assert_cycle(u1) + self._assert_modified(u1) + del sess + gc_collect() + self._assert_cycle(u1) + s2 = Session() + s2.add(u1) + self._assert_cycle(u1) + self._assert_modified(u1) + + def test_persistent_dirty_to_expired(self): + sess, u1 = self._persistent_fixture() + u1.name = 'edchanged' + self._assert_cycle(u1) + self._assert_modified(u1) + sess.expire(u1) + self._assert_no_cycle(u1) + self._assert_not_modified(u1) class WeakIdentityMapTest(_fixtures.FixtureTest): run_inserts = None diff --git a/test/orm/test_subquery_relations.py b/test/orm/test_subquery_relations.py index 80dd73e98..3ee94cae9 100644 --- a/test/orm/test_subquery_relations.py +++ b/test/orm/test_subquery_relations.py @@ -1036,7 +1036,7 @@ class BaseRelationFromJoinedSubclassTest(_Polymorphic): sess.add_all([e1, e2]) sess.flush() - def test_correct_subquery(self): + def test_correct_subquery_nofrom(self): sess = create_session() # use Person.paperwork here just to give the least # amount of context @@ -1083,6 +1083,57 @@ class BaseRelationFromJoinedSubclassTest(_Polymorphic): ) ) + def test_correct_subquery_existingfrom(self): + sess = create_session() + # use Person.paperwork here just to give the least + # amount of context + q = sess.query(Engineer).\ + filter(Engineer.primary_language == 'java').\ + join(Engineer.paperwork).\ + filter(Paperwork.description == "tps report #2").\ + options(subqueryload(Person.paperwork)) + def go(): + eq_(q.one().paperwork, + [Paperwork(description="tps report #1"), + Paperwork(description="tps report #2")], + + ) + self.assert_sql_execution( + testing.db, + go, + CompiledSQL( + "SELECT people.person_id AS people_person_id, " + "people.name AS people_name, people.type AS people_type, " + "engineers.engineer_id AS engineers_engineer_id, " + "engineers.primary_language AS engineers_primary_language " + "FROM people JOIN engineers " + "ON people.person_id = engineers.engineer_id " + "JOIN paperwork ON people.person_id = paperwork.person_id " + "WHERE engineers.primary_language = :primary_language_1 " + "AND paperwork.description = :description_1", + {"primary_language_1": "java", + "description_1": "tps report #2"} + ), + CompiledSQL( + "SELECT paperwork.paperwork_id AS paperwork_paperwork_id, " + "paperwork.description AS paperwork_description, " + "paperwork.person_id AS paperwork_person_id, " + "anon_1.people_person_id AS anon_1_people_person_id " + "FROM (SELECT people.person_id AS people_person_id " + "FROM people JOIN engineers ON people.person_id = " + "engineers.engineer_id JOIN paperwork " + "ON people.person_id = paperwork.person_id " + "WHERE engineers.primary_language = :primary_language_1 AND " + "paperwork.description = :description_1) AS anon_1 " + "JOIN paperwork ON anon_1.people_person_id = " + "paperwork.person_id " + "ORDER BY anon_1.people_person_id, paperwork.paperwork_id", + {"primary_language_1": "java", + "description_1": "tps report #2"} + ) + ) + + class SelfReferentialTest(fixtures.MappedTest): |