The default is ``'alembic_version'``.
:param version_table_schema: Optional schema to place version
table within.
+ :param version_table_pk: boolean, whether the Alembic version table
+ should use a primary key constraint for the "value" column; this
+ only takes effect when the table is first created.
+ Defaults to True; setting to False should not be necessary and is
+ here for backwards compatibility reasons.
+
+ .. versionadded:: 0.8.10 Added the
+ :paramref:`.EnvironmentContext.configure.version_table_pk`
+ flag and additionally established that the Alembic version table
+ has a primary key constraint by default.
Parameters specific to the autogenerate feature, when
``alembic revision`` is run with the ``--autogenerate`` feature:
import sys
from contextlib import contextmanager
-from sqlalchemy import MetaData, Table, Column, String, literal_column
+from sqlalchemy import MetaData, Table, Column, String, literal_column,\
+ PrimaryKeyConstraint
from sqlalchemy.engine.strategies import MockEngineStrategy
from sqlalchemy.engine import url as sqla_url
version_table, MetaData(),
Column('version_num', String(32), nullable=False),
schema=version_table_schema)
+ if opts.get("version_table_pk", True):
+ self._version.append_constraint(
+ PrimaryKeyConstraint(
+ 'version_num', name="%s_pkc" % version_table
+ )
+ )
self._start_from_rev = opts.get("starting_rev")
self.impl = ddl.DefaultImpl.get_by_dialect(dialect)(
.. changelog::
:version: 0.8.10
+ .. change:: 406
+ :tags: bug, versioning
+ :tickets: 406
+
+ The alembic_version table, when initially created, now establishes a
+ primary key constraint on the "version_num" column, to suit database
+ engines that don't support tables without primary keys. This behavior
+ can be controlled using the parameter
+ :paramref:`.EnvironmentContext.configure.version_table_pk`. Note that
+ this change only applies to the initial creation of the alembic_version
+ table; it does not impact any existing alembic_version table already
+ present.
+
.. change:: 402
:tags: bug, batch
:tickets: 402
from alembic.testing.env import staging_env, _sqlite_testing_config, \
three_rev_fixture, clear_staging_env, _no_sql_testing_config, \
_sqlite_file_db, write_script, env_file_fixture
-from alembic.testing import eq_, assert_raises_message, mock
+from alembic.testing import eq_, assert_raises_message, mock, assert_raises
from alembic import util
from contextlib import contextmanager
import re
+from sqlalchemy import exc as sqla_exc
class _BufMixin(object):
def tearDown(self):
clear_staging_env()
- def _env_fixture(self):
+ def _env_fixture(self, version_table_pk=True):
env_file_fixture("""
from sqlalchemy import MetaData, engine_from_config
connection = engine.connect()
-context.configure(connection=connection, target_metadata=target_metadata)
+context.configure(
+ connection=connection, target_metadata=target_metadata,
+ version_table_pk=%r
+)
try:
with context.begin_transaction():
finally:
connection.close()
-""")
+""" % (version_table_pk, ))
def test_create_rev_plain_db_not_up_to_date(self):
self._env_fixture()
command.revision, self.cfg, autogenerate=True
)
- def test_err_correctly_raised_on_dupe_rows(self):
+ def test_pk_constraint_normally_prevents_dupe_rows(self):
self._env_fixture()
command.revision(self.cfg)
r2 = command.revision(self.cfg)
db = _sqlite_file_db()
command.upgrade(self.cfg, "head")
+ assert_raises(
+ sqla_exc.IntegrityError,
+ db.execute,
+ "insert into alembic_version values ('%s')" % r2.revision
+ )
+
+ def test_err_correctly_raised_on_dupe_rows_no_pk(self):
+ self._env_fixture(version_table_pk=False)
+ command.revision(self.cfg)
+ r2 = command.revision(self.cfg)
+ db = _sqlite_file_db()
+ command.upgrade(self.cfg, "head")
db.execute("insert into alembic_version values ('%s')" % r2.revision)
assert_raises_message(
util.CommandError,
context = self.make_one(dialect_name='sqlite',
opts={'version_table': 'explicit'})
eq_(context._version.name, 'explicit')
+ eq_(context._version.primary_key.name, 'explicit_pkc')
def test_config_explicit_version_table_schema(self):
context = self.make_one(dialect_name='sqlite',
opts={'version_table_schema': 'explicit'})
eq_(context._version.schema, 'explicit')
+ def test_config_explicit_no_pk(self):
+ context = self.make_one(dialect_name='sqlite',
+ opts={'version_table_pk': False})
+ eq_(len(context._version.primary_key), 0)
+
+ def test_config_explicit_w_pk(self):
+ context = self.make_one(dialect_name='sqlite',
+ opts={'version_table_pk': True})
+ eq_(len(context._version.primary_key), 1)
+ eq_(context._version.primary_key.name, "alembic_version_pkc")
+
def test_get_current_revision_doesnt_create_version_table(self):
context = self.make_one(connection=self.connection,
opts={'version_table': 'version_table'})