From e7453f14bc972f1206144f1155ea3dc52469ea95 Mon Sep 17 00:00:00 2001 From: Nikolay Edigaryev Date: Fri, 15 May 2020 17:18:47 -0400 Subject: [PATCH] Ensure "alembic current" won't unnecessarily mutate the database The ``alembic current`` command no longer creates an ``alembic_version`` table in the database if one does not exist already, returning no version as the current version. This allows checking for migrations in parallel without introducing race conditions. Pull request courtesy Nikolay Edigaryev. Fixes: #694 Closes: #695 Pull-request: https://github.com/sqlalchemy/alembic/pull/695 Pull-request-sha: fd3e3b8faf7a41dd4c35daca6c7d224e983ab496 Change-Id: I500ab9ec1fe74b5e20e6aecfe598bce7e9cdef96 --- alembic/command.py | 4 +++- alembic/operations/base.py | 1 - alembic/runtime/migration.py | 4 +++- docs/build/unreleased/694.rst | 10 ++++++++++ tests/test_command.py | 9 +++++++++ 5 files changed, 25 insertions(+), 3 deletions(-) create mode 100644 docs/build/unreleased/694.rst diff --git a/alembic/command.py b/alembic/command.py index 7d19e3c6..c925b18b 100644 --- a/alembic/command.py +++ b/alembic/command.py @@ -511,7 +511,9 @@ def current(config, verbose=False, head_only=False): return [] - with EnvironmentContext(config, script, fn=display_version): + with EnvironmentContext( + config, script, fn=display_version, dont_mutate=True + ): script.run_env() diff --git a/alembic/operations/base.py b/alembic/operations/base.py index 30bd87fe..86b50ca5 100644 --- a/alembic/operations/base.py +++ b/alembic/operations/base.py @@ -127,7 +127,6 @@ class Operations(util.ModuleClsProxy): "args": args, "apply_kw": apply_kw, "doc": fn.__doc__, - "meth": fn.__name__, } ) globals_ = {"op_cls": op_cls} diff --git a/alembic/runtime/migration.py b/alembic/runtime/migration.py index 48408a4f..5c8590d6 100644 --- a/alembic/runtime/migration.py +++ b/alembic/runtime/migration.py @@ -497,7 +497,9 @@ class MigrationContext(object): else: heads = self.get_current_heads() - if not self.as_sql and not heads: + dont_mutate = self.opts.get("dont_mutate", False) + + if not self.as_sql and not heads and not dont_mutate: self._ensure_version_table() head_maintainer = HeadMaintainer(self, heads) diff --git a/docs/build/unreleased/694.rst b/docs/build/unreleased/694.rst new file mode 100644 index 00000000..0d62b28f --- /dev/null +++ b/docs/build/unreleased/694.rst @@ -0,0 +1,10 @@ +.. change:: + :tags: bug, commands + :tickets: 694 + + The ``alembic current`` command no longer creates an ``alembic_version`` + table in the database if one does not exist already, returning no version + as the current version. This allows checking for migrations in parallel + without introducing race conditions. Pull request courtesy Nikolay + Edigaryev. + diff --git a/tests/test_command.py b/tests/test_command.py index 4102f29f..a616e9cc 100644 --- a/tests/test_command.py +++ b/tests/test_command.py @@ -17,6 +17,7 @@ from alembic.script import ScriptDirectory from alembic.testing import assert_raises from alembic.testing import assert_raises_message from alembic.testing import eq_ +from alembic.testing import is_false from alembic.testing import is_true from alembic.testing import mock from alembic.testing.env import _get_staging_directory @@ -33,6 +34,7 @@ from alembic.testing.fixtures import capture_context_buffer from alembic.testing.fixtures import capture_engine_context_buffer from alembic.testing.fixtures import TestBase from alembic.util import compat +from alembic.util.sqla_compat import _connectable_has_table class _BufMixin(object): @@ -201,6 +203,7 @@ finally: class CurrentTest(_BufMixin, TestBase): @classmethod def setup_class(cls): + cls.bind = _sqlite_file_db() cls.env = env = staging_env() cls.cfg = _sqlite_testing_config() cls.a1 = env.generate_revision("a1", "a1") @@ -232,6 +235,12 @@ class CurrentTest(_BufMixin, TestBase): eq_(lines, set(revs)) + def test_doesnt_create_alembic_version(self): + command.current(self.cfg) + engine = self.bind + with engine.connect() as conn: + is_false(_connectable_has_table(conn, "alembic_version", None)) + def test_no_current(self): command.stamp(self.cfg, ()) with self._assert_lines([]): -- 2.47.2