summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorHajime Nakagami <nakagami@gmail.com>2013-04-20 17:10:23 +0900
committerHajime Nakagami <nakagami@gmail.com>2013-04-20 17:10:23 +0900
commitfbcdba12f88d88c509fc34eb8aab3f501d1b705b (patch)
treef1ca2029cfd147478447d3cb98bae587a8ccb3c2 /test
parentaf9ed1ff16fce148af46fbff33286fc30e33773d (diff)
parent7f0ee900b6c35a9bff214f9ebb02c3fb98d1f7e1 (diff)
downloadsqlalchemy-fbcdba12f88d88c509fc34eb8aab3f501d1b705b.tar.gz
merge from default
Diffstat (limited to 'test')
-rw-r--r--test/dialect/test_mssql.py2
-rw-r--r--test/engine/test_reconnect.py24
-rw-r--r--test/orm/test_instrumentation.py14
-rw-r--r--test/orm/test_session.py144
-rw-r--r--test/orm/test_subquery_relations.py53
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):