From: Mike Bayer Date: Tue, 14 Mar 2017 17:38:12 +0000 (-0400) Subject: Enable sane_multi_rowcount for cx_Oracle X-Git-Tag: rel_1_2_0b1~156 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=596e322543df6ff380243c9cb0cf9997252329f6;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git Enable sane_multi_rowcount for cx_Oracle Also add some tests to test_rowcount. Change-Id: Idaa18fdc4fcfeb615725531c37de77decf76a783 Fixes: #3932 --- diff --git a/doc/build/changelog/changelog_12.rst b/doc/build/changelog/changelog_12.rst index 503c59a268..abf7035cad 100644 --- a/doc/build/changelog/changelog_12.rst +++ b/doc/build/changelog/changelog_12.rst @@ -13,6 +13,20 @@ .. changelog:: :version: 1.2.0b1 + .. change:: 3932 + :tags: bug, oracle + :tickets: 3932 + + The cx_Oracle dialect now supports "sane multi rowcount", that is, + when a series of parameter sets are executed via DBAPI + ``cursor.executemany()``, we can make use of ``cursor.rowcount`` to + verify the number of rows matched. This has an impact within the + ORM when detecting concurrent modification scenarios, in that + some simple conditions can now be detected even when the ORM + is batching statements, as well as when the more strict versioning + feature is used, the ORM can still use statement batching. The + flag is enabled for cx_Oracle assuming at least version 5.0, which + is now commonplace. .. change:: 3276 :tags: bug, oracle diff --git a/lib/sqlalchemy/dialects/oracle/base.py b/lib/sqlalchemy/dialects/oracle/base.py index 39bc5e1d31..8cafb36568 100644 --- a/lib/sqlalchemy/dialects/oracle/base.py +++ b/lib/sqlalchemy/dialects/oracle/base.py @@ -957,8 +957,6 @@ class OracleDialect(default.DefaultDialect): supports_unicode_statements = False supports_unicode_binds = False max_identifier_length = 30 - supports_sane_rowcount = True - supports_sane_multi_rowcount = False supports_simple_order_by_label = False diff --git a/lib/sqlalchemy/dialects/oracle/cx_oracle.py b/lib/sqlalchemy/dialects/oracle/cx_oracle.py index 9b3e3b8a12..6789cd49ff 100644 --- a/lib/sqlalchemy/dialects/oracle/cx_oracle.py +++ b/lib/sqlalchemy/dialects/oracle/cx_oracle.py @@ -653,6 +653,9 @@ class OracleDialect_cx_oracle(OracleDialect): execution_ctx_cls = OracleExecutionContext_cx_oracle statement_compiler = OracleCompiler_cx_oracle + supports_sane_rowcount = True + supports_sane_multi_rowcount = True + driver = "cx_oracle" colspecs = colspecs = { @@ -723,6 +726,8 @@ class OracleDialect_cx_oracle(OracleDialect): self._cx_oracle_binary_types = types("BFILE", "CLOB", "NCLOB", "BLOB") self.supports_unicode_binds = self.cx_oracle_ver >= (5, 0) + self.supports_sane_multi_rowcount = self.cx_oracle_ver >= (5, 0) + self.coerce_to_unicode = ( self.cx_oracle_ver >= (5, 0) and coerce_to_unicode diff --git a/test/sql/test_rowcount.py b/test/sql/test_rowcount.py index 0ab5589ab3..16087b94cc 100644 --- a/test/sql/test_rowcount.py +++ b/test/sql/test_rowcount.py @@ -57,14 +57,12 @@ class FoundRowsTest(fixtures.TestBase, AssertsExecutionResults): # WHERE matches 3, 3 rows changed department = employees_table.c.department r = employees_table.update(department == 'C').execute(department='Z') - print("expecting 3, dialect reports %s" % r.rowcount) assert r.rowcount == 3 def test_update_rowcount2(self): # WHERE matches 3, 0 rows changed department = employees_table.c.department r = employees_table.update(department == 'C').execute(department='C') - print("expecting 3, dialect reports %s" % r.rowcount) assert r.rowcount == 3 def test_raw_sql_rowcount(self): @@ -87,5 +85,35 @@ class FoundRowsTest(fixtures.TestBase, AssertsExecutionResults): # WHERE matches 3, 3 rows deleted department = employees_table.c.department r = employees_table.delete(department == 'C').execute() - print("expecting 3, dialect reports %s" % r.rowcount) assert r.rowcount == 3 + + @testing.requires.sane_multi_rowcount + def test_multi_update_rowcount(self): + stmt = employees_table.update().\ + where(employees_table.c.name == bindparam('emp_name')).\ + values(department="C") + + r = testing.db.execute( + stmt, + [{"emp_name": "Bob"}, {"emp_name": "Cynthia"}, + {"emp_name": "nonexistent"}] + ) + + eq_( + r.rowcount, 2 + ) + + @testing.requires.sane_multi_rowcount + def test_multi_delete_rowcount(self): + stmt = employees_table.delete().\ + where(employees_table.c.name == bindparam('emp_name')) + + r = testing.db.execute( + stmt, + [{"emp_name": "Bob"}, {"emp_name": "Cynthia"}, + {"emp_name": "nonexistent"}] + ) + + eq_( + r.rowcount, 2 + )