orm DELETE not using DB-persited PK in the event of PK change

Issue #3006 resolved
Mike Bayer repo owner created an issue
from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class A(Base):
    __tablename__ = 'a'

    id = Column(Integer, primary_key=True)

e = create_engine("sqlite://", echo=True)
#e = create_engine('postgresql://scott:tiger@localhost/test', echo=True)

Base.metadata.drop_all(e)
Base.metadata.create_all(e)

sess = Session(e)
a1 = A(id=1)
sess.add(a1)
sess.commit()

a1.id = 2
sess.delete(a1)
sess.commit()

note that we will turn on "multi rowcount" checking which seems to have been removed, also psycopg2 supports this now:

diff --git a/lib/sqlalchemy/dialects/postgresql/psycopg2.py b/lib/sqlalchemy/dialects/postgresql/psycopg2.py
index 099ddf0..ac17706 100644
--- a/lib/sqlalchemy/dialects/postgresql/psycopg2.py
+++ b/lib/sqlalchemy/dialects/postgresql/psycopg2.py
@@ -347,7 +347,7 @@ class PGDialect_psycopg2(PGDialect):
         supports_unicode_statements = False

     default_paramstyle = 'pyformat'
-    supports_sane_multi_rowcount = False
+    supports_sane_multi_rowcount = False  # set to true based on psycopg2 version
     execution_ctx_cls = PGExecutionContext_psycopg2
     statement_compiler = PGCompiler_psycopg2
     preparer = PGIdentifierPreparer_psycopg2
@@ -393,6 +393,9 @@ class PGDialect_psycopg2(PGDialect):
                             is not None
         self._has_native_json = self.psycopg2_version >= (2, 5)

+        # http://initd.org/psycopg/docs/news.html#what-s-new-in-psycopg-2-0-9
+        self.supports_sane_multi_rowcount = self.psycopg2_version >= (2, 0, 9)
+
     @classmethod
     def dbapi(cls):
         import psycopg2
diff --git a/lib/sqlalchemy/orm/persistence.py b/lib/sqlalchemy/orm/persistence.py
index 3563198..7a9f3d5 100644
--- a/lib/sqlalchemy/orm/persistence.py
+++ b/lib/sqlalchemy/orm/persistence.py
@@ -702,7 +702,16 @@ def _emit_delete_statements(base_mapper, uowtransaction, cached_connections,
                     stacklevel=12)
                 connection.execute(statement, del_objects)
         else:
-            connection.execute(statement, del_objects)
+            c = connection.execute(statement, del_objects)
+            if connection.dialect.supports_sane_multi_rowcount and \
+                c.rowcount != 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)
+                )
+
+


 def _finalize_insert_update_commands(base_mapper, uowtransaction,

Comments (2)

  1. Mike Bayer reporter
    • 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.

    → <<cset c01558ae7f4a>>

  2. Mike Bayer reporter
    • 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.

    Conflicts: test/orm/test_naturalpks.py test/requirements.py

    → <<cset e789a75f9a32>>

  3. Log in to comment