summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2020-07-05 13:44:58 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2020-07-05 14:45:35 -0400
commitc6c9d5f925e4418c10c93c47fef53200dca11f00 (patch)
tree534d8864c52a811fb57af5895ca33a8c4a786bb7 /test
parent9204b6610c9667ba2e4f73440339d2f520631777 (diff)
downloadsqlalchemy-c6c9d5f925e4418c10c93c47fef53200dca11f00.tar.gz
Ensure synchronize_session works with lambda statements
A few places have logic that assumes the top-level statement is the actual UPDATE or DELETE which is not the case with a lambda. Ensure the correct object is used. This fixes issues specific to both "fetch" strategy as well as "evaluate" strategy. Fixes: #5442 Change-Id: Ic9cc01c696c3c338d5bc79688507e6717c4c169b
Diffstat (limited to 'test')
-rw-r--r--test/orm/test_update_delete.py149
1 files changed, 149 insertions, 0 deletions
diff --git a/test/orm/test_update_delete.py b/test/orm/test_update_delete.py
index b0d718315..8ec64c586 100644
--- a/test/orm/test_update_delete.py
+++ b/test/orm/test_update_delete.py
@@ -1,11 +1,13 @@
from sqlalchemy import Boolean
from sqlalchemy import case
from sqlalchemy import column
+from sqlalchemy import delete
from sqlalchemy import event
from sqlalchemy import exc
from sqlalchemy import ForeignKey
from sqlalchemy import func
from sqlalchemy import Integer
+from sqlalchemy import lambda_stmt
from sqlalchemy import or_
from sqlalchemy import select
from sqlalchemy import String
@@ -485,6 +487,62 @@ class UpdateDeleteTest(fixtures.MappedTest):
list(zip([15, 27, 19, 27])),
)
+ def test_update_future_lambda(self):
+ User, users = self.classes.User, self.tables.users
+
+ sess = Session()
+
+ john, jack, jill, jane = (
+ sess.execute(future_select(User).order_by(User.id)).scalars().all()
+ )
+
+ sess.execute(
+ lambda_stmt(
+ lambda: update(User)
+ .where(User.age > 29)
+ .values({"age": User.age - 10})
+ .execution_options(synchronize_session="evaluate")
+ ),
+ )
+
+ eq_([john.age, jack.age, jill.age, jane.age], [25, 37, 29, 27])
+ eq_(
+ sess.execute(future_select(User.age).order_by(User.id)).all(),
+ list(zip([25, 37, 29, 27])),
+ )
+
+ sess.execute(
+ lambda_stmt(
+ lambda: update(User)
+ .where(User.age > 29)
+ .values({User.age: User.age - 10})
+ .execution_options(synchronize_session="evaluate")
+ )
+ )
+ eq_([john.age, jack.age, jill.age, jane.age], [25, 27, 29, 27])
+ eq_(
+ sess.query(User.age).order_by(User.id).all(),
+ list(zip([25, 27, 29, 27])),
+ )
+
+ sess.query(User).filter(User.age > 27).update(
+ {users.c.age_int: User.age - 10}, synchronize_session="evaluate"
+ )
+ eq_([john.age, jack.age, jill.age, jane.age], [25, 27, 19, 27])
+ eq_(
+ sess.query(User.age).order_by(User.id).all(),
+ list(zip([25, 27, 19, 27])),
+ )
+
+ sess.query(User).filter(User.age == 25).update(
+ {User.age: User.age - 10}, synchronize_session="fetch"
+ )
+ eq_([john.age, jack.age, jill.age, jane.age], [15, 27, 19, 27])
+ eq_(
+ sess.query(User.age).order_by(User.id).all(),
+ list(zip([15, 27, 19, 27])),
+ )
+
def test_update_against_table_col(self):
User, users = self.classes.User, self.tables.users
@@ -565,6 +623,52 @@ class UpdateDeleteTest(fixtures.MappedTest):
),
)
+ def test_update_fetch_returning_lambda(self):
+ User = self.classes.User
+
+ sess = Session()
+
+ john, jack, jill, jane = (
+ sess.execute(future_select(User).order_by(User.id)).scalars().all()
+ )
+
+ with self.sql_execution_asserter() as asserter:
+ stmt = lambda_stmt(
+ lambda: update(User)
+ .where(User.age > 29)
+ .values({"age": User.age - 10})
+ )
+ sess.execute(
+ stmt, execution_options={"synchronize_session": "fetch"}
+ )
+
+ # these are simple values, these are now evaluated even with
+ # the "fetch" strategy, new in 1.4, so there is no expiry
+ eq_([john.age, jack.age, jill.age, jane.age], [25, 37, 29, 27])
+
+ if testing.db.dialect.full_returning:
+ asserter.assert_(
+ CompiledSQL(
+ "UPDATE users SET age_int=(users.age_int - %(age_int_1)s) "
+ "WHERE users.age_int > %(age_int_2)s RETURNING users.id",
+ [{"age_int_1": 10, "age_int_2": 29}],
+ dialect="postgresql",
+ ),
+ )
+ else:
+ asserter.assert_(
+ CompiledSQL(
+ "SELECT users.id FROM users "
+ "WHERE users.age_int > :age_int_1",
+ [{"age_int_1": 29}],
+ ),
+ CompiledSQL(
+ "UPDATE users SET age_int=(users.age_int - :age_int_1) "
+ "WHERE users.age_int > :age_int_2",
+ [{"age_int_1": 10, "age_int_2": 29}],
+ ),
+ )
+
def test_delete_fetch_returning(self):
User = self.classes.User
@@ -607,6 +711,51 @@ class UpdateDeleteTest(fixtures.MappedTest):
in_(jill, sess)
not_in_(jane, sess)
+ def test_delete_fetch_returning_lambda(self):
+ User = self.classes.User
+
+ sess = Session()
+
+ john, jack, jill, jane = (
+ sess.execute(future_select(User).order_by(User.id)).scalars().all()
+ )
+
+ in_(john, sess)
+ in_(jack, sess)
+
+ with self.sql_execution_asserter() as asserter:
+ stmt = lambda_stmt(lambda: delete(User).where(User.age > 29))
+ sess.execute(
+ stmt, execution_options={"synchronize_session": "fetch"}
+ )
+
+ if testing.db.dialect.full_returning:
+ asserter.assert_(
+ CompiledSQL(
+ "DELETE FROM users WHERE users.age_int > %(age_int_1)s "
+ "RETURNING users.id",
+ [{"age_int_1": 29}],
+ dialect="postgresql",
+ ),
+ )
+ else:
+ asserter.assert_(
+ CompiledSQL(
+ "SELECT users.id FROM users "
+ "WHERE users.age_int > :age_int_1",
+ [{"age_int_1": 29}],
+ ),
+ CompiledSQL(
+ "DELETE FROM users WHERE users.age_int > :age_int_1",
+ [{"age_int_1": 29}],
+ ),
+ )
+
+ in_(john, sess)
+ not_in_(jack, sess)
+ in_(jill, sess)
+ not_in_(jane, sess)
+
def test_update_with_filter_statement(self):
"""test for [ticket:4556] """