From 03cb8ce548a99b97852c25f2fec26ed611afbb5c Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Sat, 22 Sep 2012 16:03:57 -0400 Subject: [PATCH] - [bug] Extra logic has been added to the "flush" that occurs within Session.commit(), such that the extra state added by an after_flush() or after_flush_postexec() hook is also flushed in a subsequent flush, before the "commit" completes. Subsequent calls to flush() will continue until the after_flush hooks stop adding new state. An "overflow" counter of 100 is also in place, in the event of a broken after_flush() hook adding new content each time. [ticket:2566] --- CHANGES | 15 +++++++++-- lib/sqlalchemy/orm/session.py | 10 +++++++- test/orm/test_transaction.py | 47 +++++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index 205e035edf..3b600d5bee 100644 --- a/CHANGES +++ b/CHANGES @@ -612,8 +612,8 @@ underneath "0.7.xx". needs to be modified to use column.in_(select) explicitly. [ticket:2277] - - [feature] updated support for the mxodbc - driver; mxodbc 3.2.1 is recommended for full + - [feature] updated support for the mxodbc + driver; mxodbc 3.2.1 is recommended for full compatibility. - postgresql @@ -743,6 +743,17 @@ are also present in 0.8. "version" column, when using the "version" feature. Tests courtesy Daniel Miller. [ticket:2539] + - [bug] Extra logic has been added to the "flush" + that occurs within Session.commit(), such that the + extra state added by an after_flush() or + after_flush_postexec() hook is also flushed in a + subsequent flush, before the "commit" completes. + Subsequent calls to flush() will continue until + the after_flush hooks stop adding new state. + An "overflow" counter of 100 is also in place, + in the event of a broken after_flush() hook + adding new content each time. [ticket:2566] + - sql - [bug] Fixed CTE bug whereby positional bound parameters present in the CTEs themselves diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py index 36ff5fc84e..5e5cf2246a 100644 --- a/lib/sqlalchemy/orm/session.py +++ b/lib/sqlalchemy/orm/session.py @@ -294,7 +294,15 @@ class SessionTransaction(object): subtransaction.commit() if not self.session._flushing: - self.session.flush() + for _flush_guard in xrange(100): + if self.session._is_clean(): + break + self.session.flush() + else: + raise exc.FlushError( + "Over 100 subsequent flushes have occurred within " + "session.commit() - is an after_flush() hook " + "creating new objects?") if self._parent is None and self.session.twophase: try: diff --git a/test/orm/test_transaction.py b/test/orm/test_transaction.py index edae617d68..fdbf6ae1f2 100644 --- a/test/orm/test_transaction.py +++ b/test/orm/test_transaction.py @@ -297,6 +297,53 @@ class SessionTransactionTest(FixtureTest): eq_(len(sess.query(User).all()), 1) + def test_continue_flushing_on_commit(self): + """test that post-flush actions get flushed also if + we're in commit()""" + users, User = self.tables.users, self.classes.User + + mapper(User, users) + sess = Session() + + to_flush = [User(name='ed'), User(name='jack'), User(name='wendy')] + @event.listens_for(sess, "after_flush_postexec") + def add_another_user(session, ctx): + if to_flush: + session.add(to_flush.pop(0)) + + x = [1] + @event.listens_for(sess, "after_commit") + def add_another_user(session): + x[0] += 1 + + sess.add(to_flush.pop()) + sess.commit() + eq_(x, [2]) + eq_( + sess.scalar(select([func.count(users.c.id)])), 3 + ) + + def test_continue_flushing_guard(self): + users, User = self.tables.users, self.classes.User + + mapper(User, users) + sess = Session() + + @event.listens_for(sess, "after_flush_postexec") + def add_another_user(session, ctx): + session.add(User(name='x')) + sess.add(User(name='x')) + assert_raises_message( + orm_exc.FlushError, + "Over 100 subsequent flushes have occurred", + sess.commit + ) + + + + + + def test_error_on_using_inactive_session_commands(self): users, User = self.tables.users, self.classes.User -- 2.47.3