diff --git a/alembic/runtime/migration.py b/alembic/runtime/migration.py index 0a1e646d..49eef713 100644 --- a/alembic/runtime/migration.py +++ b/alembic/runtime/migration.py @@ -676,7 +676,11 @@ def _delete_version(self, version): == literal_column("'%s'" % version) ) ) - if not self.context.as_sql and ret.rowcount != 1: + if ( + not self.context.as_sql + and self.context.dialect.supports_sane_rowcount + and ret.rowcount != 1 + ): raise util.CommandError( "Online migration expected to match one " "row when deleting '%s' in '%s'; " @@ -697,7 +701,11 @@ def _update_version(self, from_, to_): == literal_column("'%s'" % from_) ) ) - if not self.context.as_sql and ret.rowcount != 1: + if ( + not self.context.as_sql + and self.context.dialect.supports_sane_rowcount + and ret.rowcount != 1 + ): raise util.CommandError( "Online migration expected to match one " "row when updating '%s' to '%s' in '%s'; " diff --git a/docs/build/unreleased/rowcount.rst b/docs/build/unreleased/rowcount.rst new file mode 100644 index 00000000..8e7830a0 --- /dev/null +++ b/docs/build/unreleased/rowcount.rst @@ -0,0 +1,8 @@ +.. change:: + :tags: bug, environment + + The check for matched rowcount when the alembic_version table is updated or + deleted from is now conditional based on whether or not the dialect + supports the concept of "rowcount" for UPDATE or DELETE rows matched. Some + third party dialects do not support this concept. Pull request courtesy Ke + Zhu. diff --git a/tests/test_version_table.py b/tests/test_version_table.py index 34bdfabf..1801346c 100644 --- a/tests/test_version_table.py +++ b/tests/test_version_table.py @@ -262,6 +262,14 @@ def test_update_no_match(self): _up("x", "b"), ) + def test_update_no_match_no_sane_rowcount(self): + self.updater.update_to_step(_up(None, "a", True)) + self.updater.heads.add("x") + with mock.patch.object( + self.connection.dialect, "supports_sane_rowcount", False + ): + self.updater.update_to_step(_up("x", "b")) + def test_update_multi_match(self): self.connection.execute(version_table.insert(), version_num="a") self.connection.execute(version_table.insert(), version_num="a") @@ -275,6 +283,16 @@ def test_update_multi_match(self): _up("a", "b"), ) + def test_update_multi_match_no_sane_rowcount(self): + self.connection.execute(version_table.insert(), version_num="a") + self.connection.execute(version_table.insert(), version_num="a") + + self.updater.heads.add("a") + with mock.patch.object( + self.connection.dialect, "supports_sane_rowcount", False + ): + self.updater.update_to_step(_up("a", "b")) + def test_delete_no_match(self): self.updater.update_to_step(_up(None, "a", True)) @@ -287,6 +305,15 @@ def test_delete_no_match(self): _down("x", None, True), ) + def test_delete_no_matchno_sane_rowcount(self): + self.updater.update_to_step(_up(None, "a", True)) + + self.updater.heads.add("x") + with mock.patch.object( + self.connection.dialect, "supports_sane_rowcount", False + ): + self.updater.update_to_step(_down("x", None, True)) + def test_delete_multi_match(self): self.connection.execute(version_table.insert(), version_num="a") self.connection.execute(version_table.insert(), version_num="a") @@ -299,3 +326,13 @@ def test_delete_multi_match(self): self.updater.update_to_step, _down("a", None, True), ) + + def test_delete_multi_match_no_sane_rowcount(self): + self.connection.execute(version_table.insert(), version_num="a") + self.connection.execute(version_table.insert(), version_num="a") + + self.updater.heads.add("a") + with mock.patch.object( + self.connection.dialect, "supports_sane_rowcount", False + ): + self.updater.update_to_step(_down("a", None, True))