diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2014-03-28 16:32:11 -0400 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2014-03-28 16:32:11 -0400 |
| commit | c01558ae7f4af08acc523786e107ea5e2e214184 (patch) | |
| tree | a235ab825660b00c674f496354b364f58f8b464a /lib/sqlalchemy/orm | |
| parent | 9cdbed37f8c420db0b42fb959813d079622c3f3a (diff) | |
| download | sqlalchemy-c01558ae7f4af08acc523786e107ea5e2e214184.tar.gz | |
- Fixed ORM bug where changing the primary key of an object, then marking
it for DELETE would fail to target the correct row for DELETE.
Then to compound matters, basic "number of rows matched" checks were
not being performed. Both issues are fixed, however note that the
"rows matched" check requires so-called "sane multi-row count"
functionality; the DBAPI's executemany() method must count up the
rows matched by individual statements and SQLAlchemy's dialect must
mark this feature as supported, currently applies to some mysql dialects,
psycopg2, sqlite only. fixes #3006
- Enabled "sane multi-row count" checking for the psycopg2 DBAPI, as
this seems to be supported as of psycopg2 2.0.9.
Diffstat (limited to 'lib/sqlalchemy/orm')
| -rw-r--r-- | lib/sqlalchemy/orm/persistence.py | 26 |
1 files changed, 15 insertions, 11 deletions
diff --git a/lib/sqlalchemy/orm/persistence.py b/lib/sqlalchemy/orm/persistence.py index 35631988b..b4c9c052b 100644 --- a/lib/sqlalchemy/orm/persistence.py +++ b/lib/sqlalchemy/orm/persistence.py @@ -450,7 +450,7 @@ def _collect_delete_commands(base_mapper, uowtransaction, table, for col in mapper._pks_by_table[table]: params[col.key] = \ value = \ - mapper._get_state_attr_by_column( + mapper._get_committed_state_attr_by_column( state, state_dict, col) if value is None: raise orm_exc.FlushError( @@ -679,21 +679,19 @@ def _emit_delete_statements(base_mapper, uowtransaction, cached_connections, connection = cached_connections[connection] - if need_version_id: - # TODO: need test coverage for this [ticket:1761] + expected = len(del_objects) + rows_matched = -1 + if connection.dialect.supports_sane_multi_rowcount: + c = connection.execute(statement, del_objects) + rows_matched = c.rowcount + elif need_version_id: if connection.dialect.supports_sane_rowcount: - rows = 0 + rows_matched = 0 # execute deletes individually so that versioned # rows can be verified for params in del_objects: c = connection.execute(statement, params) - rows += c.rowcount - if rows != len(del_objects): - raise orm_exc.StaleDataError( - "DELETE statement on table '%s' expected to " - "delete %d row(s); %d were matched." % - (table.description, len(del_objects), c.rowcount) - ) + rows_matched += c.rowcount else: util.warn( "Dialect %s does not support deleted rowcount " @@ -704,6 +702,12 @@ def _emit_delete_statements(base_mapper, uowtransaction, cached_connections, else: connection.execute(statement, del_objects) + if rows_matched > -1 and expected != rows_matched: + raise orm_exc.StaleDataError( + "DELETE statement on table '%s' expected to " + "delete %d row(s); %d were matched." % + (table.description, expected, rows_matched) + ) def _finalize_insert_update_commands(base_mapper, uowtransaction, states_to_insert, states_to_update): |
