1
0
mirror of https://gitlab.com/MoonTestUse1/AdministrationItDepartmens.git synced 2025-08-14 00:25:46 +02:00

Все подряд

This commit is contained in:
MoonTestUse1
2024-12-31 02:37:57 +06:00
parent 8e53bb6cb2
commit d5780b2eab
3258 changed files with 1087440 additions and 268 deletions

View File

@@ -0,0 +1,7 @@
from .test_autogen_comments import * # noqa
from .test_autogen_computed import * # noqa
from .test_autogen_diffs import * # noqa
from .test_autogen_fks import * # noqa
from .test_autogen_identity import * # noqa
from .test_environment import * # noqa
from .test_op import * # noqa

View File

@@ -0,0 +1,335 @@
from __future__ import annotations
from typing import Any
from typing import Dict
from typing import Set
from sqlalchemy import CHAR
from sqlalchemy import CheckConstraint
from sqlalchemy import Column
from sqlalchemy import event
from sqlalchemy import ForeignKey
from sqlalchemy import Index
from sqlalchemy import inspect
from sqlalchemy import Integer
from sqlalchemy import MetaData
from sqlalchemy import Numeric
from sqlalchemy import String
from sqlalchemy import Table
from sqlalchemy import Text
from sqlalchemy import text
from sqlalchemy import UniqueConstraint
from ... import autogenerate
from ... import util
from ...autogenerate import api
from ...ddl.base import _fk_spec
from ...migration import MigrationContext
from ...operations import ops
from ...testing import config
from ...testing import eq_
from ...testing.env import clear_staging_env
from ...testing.env import staging_env
names_in_this_test: Set[Any] = set()
@event.listens_for(Table, "after_parent_attach")
def new_table(table, parent):
names_in_this_test.add(table.name)
def _default_include_object(obj, name, type_, reflected, compare_to):
if type_ == "table":
return name in names_in_this_test
else:
return True
_default_object_filters: Any = _default_include_object
_default_name_filters: Any = None
class ModelOne:
__requires__ = ("unique_constraint_reflection",)
schema: Any = None
@classmethod
def _get_db_schema(cls):
schema = cls.schema
m = MetaData(schema=schema)
Table(
"user",
m,
Column("id", Integer, primary_key=True),
Column("name", String(50)),
Column("a1", Text),
Column("pw", String(50)),
Index("pw_idx", "pw"),
)
Table(
"address",
m,
Column("id", Integer, primary_key=True),
Column("email_address", String(100), nullable=False),
)
Table(
"order",
m,
Column("order_id", Integer, primary_key=True),
Column(
"amount",
Numeric(8, 2),
nullable=False,
server_default=text("0"),
),
CheckConstraint("amount >= 0", name="ck_order_amount"),
)
Table(
"extra",
m,
Column("x", CHAR),
Column("uid", Integer, ForeignKey("user.id")),
)
return m
@classmethod
def _get_model_schema(cls):
schema = cls.schema
m = MetaData(schema=schema)
Table(
"user",
m,
Column("id", Integer, primary_key=True),
Column("name", String(50), nullable=False),
Column("a1", Text, server_default="x"),
)
Table(
"address",
m,
Column("id", Integer, primary_key=True),
Column("email_address", String(100), nullable=False),
Column("street", String(50)),
UniqueConstraint("email_address", name="uq_email"),
)
Table(
"order",
m,
Column("order_id", Integer, primary_key=True),
Column(
"amount",
Numeric(10, 2),
nullable=True,
server_default=text("0"),
),
Column("user_id", Integer, ForeignKey("user.id")),
CheckConstraint("amount > -1", name="ck_order_amount"),
)
Table(
"item",
m,
Column("id", Integer, primary_key=True),
Column("description", String(100)),
Column("order_id", Integer, ForeignKey("order.order_id")),
CheckConstraint("len(description) > 5"),
)
return m
class _ComparesFKs:
def _assert_fk_diff(
self,
diff,
type_,
source_table,
source_columns,
target_table,
target_columns,
name=None,
conditional_name=None,
source_schema=None,
onupdate=None,
ondelete=None,
initially=None,
deferrable=None,
):
# the public API for ForeignKeyConstraint was not very rich
# in 0.7, 0.8, so here we use the well-known but slightly
# private API to get at its elements
(
fk_source_schema,
fk_source_table,
fk_source_columns,
fk_target_schema,
fk_target_table,
fk_target_columns,
fk_onupdate,
fk_ondelete,
fk_deferrable,
fk_initially,
) = _fk_spec(diff[1])
eq_(diff[0], type_)
eq_(fk_source_table, source_table)
eq_(fk_source_columns, source_columns)
eq_(fk_target_table, target_table)
eq_(fk_source_schema, source_schema)
eq_(fk_onupdate, onupdate)
eq_(fk_ondelete, ondelete)
eq_(fk_initially, initially)
eq_(fk_deferrable, deferrable)
eq_([elem.column.name for elem in diff[1].elements], target_columns)
if conditional_name is not None:
if conditional_name == "servergenerated":
fks = inspect(self.bind).get_foreign_keys(source_table)
server_fk_name = fks[0]["name"]
eq_(diff[1].name, server_fk_name)
else:
eq_(diff[1].name, conditional_name)
else:
eq_(diff[1].name, name)
class AutogenTest(_ComparesFKs):
def _flatten_diffs(self, diffs):
for d in diffs:
if isinstance(d, list):
yield from self._flatten_diffs(d)
else:
yield d
@classmethod
def _get_bind(cls):
return config.db
configure_opts: Dict[Any, Any] = {}
@classmethod
def setup_class(cls):
staging_env()
cls.bind = cls._get_bind()
cls.m1 = cls._get_db_schema()
cls.m1.create_all(cls.bind)
cls.m2 = cls._get_model_schema()
@classmethod
def teardown_class(cls):
cls.m1.drop_all(cls.bind)
clear_staging_env()
def setUp(self):
self.conn = conn = self.bind.connect()
ctx_opts = {
"compare_type": True,
"compare_server_default": True,
"target_metadata": self.m2,
"upgrade_token": "upgrades",
"downgrade_token": "downgrades",
"alembic_module_prefix": "op.",
"sqlalchemy_module_prefix": "sa.",
"include_object": _default_object_filters,
"include_name": _default_name_filters,
}
if self.configure_opts:
ctx_opts.update(self.configure_opts)
self.context = context = MigrationContext.configure(
connection=conn, opts=ctx_opts
)
self.autogen_context = api.AutogenContext(context, self.m2)
def tearDown(self):
self.conn.close()
def _update_context(
self, object_filters=None, name_filters=None, include_schemas=None
):
if include_schemas is not None:
self.autogen_context.opts["include_schemas"] = include_schemas
if object_filters is not None:
self.autogen_context._object_filters = [object_filters]
if name_filters is not None:
self.autogen_context._name_filters = [name_filters]
return self.autogen_context
class AutogenFixtureTest(_ComparesFKs):
def _fixture(
self,
m1,
m2,
include_schemas=False,
opts=None,
object_filters=_default_object_filters,
name_filters=_default_name_filters,
return_ops=False,
max_identifier_length=None,
):
if max_identifier_length:
dialect = self.bind.dialect
existing_length = dialect.max_identifier_length
dialect.max_identifier_length = (
dialect._user_defined_max_identifier_length
) = max_identifier_length
try:
self._alembic_metadata, model_metadata = m1, m2
for m in util.to_list(self._alembic_metadata):
m.create_all(self.bind)
with self.bind.connect() as conn:
ctx_opts = {
"compare_type": True,
"compare_server_default": True,
"target_metadata": model_metadata,
"upgrade_token": "upgrades",
"downgrade_token": "downgrades",
"alembic_module_prefix": "op.",
"sqlalchemy_module_prefix": "sa.",
"include_object": object_filters,
"include_name": name_filters,
"include_schemas": include_schemas,
}
if opts:
ctx_opts.update(opts)
self.context = context = MigrationContext.configure(
connection=conn, opts=ctx_opts
)
autogen_context = api.AutogenContext(context, model_metadata)
uo = ops.UpgradeOps(ops=[])
autogenerate._produce_net_changes(autogen_context, uo)
if return_ops:
return uo
else:
return uo.as_diffs()
finally:
if max_identifier_length:
dialect = self.bind.dialect
dialect.max_identifier_length = (
dialect._user_defined_max_identifier_length
) = existing_length
def setUp(self):
staging_env()
self.bind = config.db
def tearDown(self):
if hasattr(self, "_alembic_metadata"):
for m in util.to_list(self._alembic_metadata):
m.drop_all(self.bind)
clear_staging_env()

View File

@@ -0,0 +1,242 @@
from sqlalchemy import Column
from sqlalchemy import Float
from sqlalchemy import MetaData
from sqlalchemy import String
from sqlalchemy import Table
from ._autogen_fixtures import AutogenFixtureTest
from ...testing import eq_
from ...testing import mock
from ...testing import TestBase
class AutogenerateCommentsTest(AutogenFixtureTest, TestBase):
__backend__ = True
__requires__ = ("comments",)
def test_existing_table_comment_no_change(self):
m1 = MetaData()
m2 = MetaData()
Table(
"some_table",
m1,
Column("test", String(10), primary_key=True),
comment="this is some table",
)
Table(
"some_table",
m2,
Column("test", String(10), primary_key=True),
comment="this is some table",
)
diffs = self._fixture(m1, m2)
eq_(diffs, [])
def test_add_table_comment(self):
m1 = MetaData()
m2 = MetaData()
Table("some_table", m1, Column("test", String(10), primary_key=True))
Table(
"some_table",
m2,
Column("test", String(10), primary_key=True),
comment="this is some table",
)
diffs = self._fixture(m1, m2)
eq_(diffs[0][0], "add_table_comment")
eq_(diffs[0][1].comment, "this is some table")
eq_(diffs[0][2], None)
def test_remove_table_comment(self):
m1 = MetaData()
m2 = MetaData()
Table(
"some_table",
m1,
Column("test", String(10), primary_key=True),
comment="this is some table",
)
Table("some_table", m2, Column("test", String(10), primary_key=True))
diffs = self._fixture(m1, m2)
eq_(diffs[0][0], "remove_table_comment")
eq_(diffs[0][1].comment, None)
def test_alter_table_comment(self):
m1 = MetaData()
m2 = MetaData()
Table(
"some_table",
m1,
Column("test", String(10), primary_key=True),
comment="this is some table",
)
Table(
"some_table",
m2,
Column("test", String(10), primary_key=True),
comment="this is also some table",
)
diffs = self._fixture(m1, m2)
eq_(diffs[0][0], "add_table_comment")
eq_(diffs[0][1].comment, "this is also some table")
eq_(diffs[0][2], "this is some table")
def test_existing_column_comment_no_change(self):
m1 = MetaData()
m2 = MetaData()
Table(
"some_table",
m1,
Column("test", String(10), primary_key=True),
Column("amount", Float, comment="the amount"),
)
Table(
"some_table",
m2,
Column("test", String(10), primary_key=True),
Column("amount", Float, comment="the amount"),
)
diffs = self._fixture(m1, m2)
eq_(diffs, [])
def test_add_column_comment(self):
m1 = MetaData()
m2 = MetaData()
Table(
"some_table",
m1,
Column("test", String(10), primary_key=True),
Column("amount", Float),
)
Table(
"some_table",
m2,
Column("test", String(10), primary_key=True),
Column("amount", Float, comment="the amount"),
)
diffs = self._fixture(m1, m2)
eq_(
diffs,
[
[
(
"modify_comment",
None,
"some_table",
"amount",
{
"existing_nullable": True,
"existing_type": mock.ANY,
"existing_server_default": False,
},
None,
"the amount",
)
]
],
)
def test_remove_column_comment(self):
m1 = MetaData()
m2 = MetaData()
Table(
"some_table",
m1,
Column("test", String(10), primary_key=True),
Column("amount", Float, comment="the amount"),
)
Table(
"some_table",
m2,
Column("test", String(10), primary_key=True),
Column("amount", Float),
)
diffs = self._fixture(m1, m2)
eq_(
diffs,
[
[
(
"modify_comment",
None,
"some_table",
"amount",
{
"existing_nullable": True,
"existing_type": mock.ANY,
"existing_server_default": False,
},
"the amount",
None,
)
]
],
)
def test_alter_column_comment(self):
m1 = MetaData()
m2 = MetaData()
Table(
"some_table",
m1,
Column("test", String(10), primary_key=True),
Column("amount", Float, comment="the amount"),
)
Table(
"some_table",
m2,
Column("test", String(10), primary_key=True),
Column("amount", Float, comment="the adjusted amount"),
)
diffs = self._fixture(m1, m2)
eq_(
diffs,
[
[
(
"modify_comment",
None,
"some_table",
"amount",
{
"existing_nullable": True,
"existing_type": mock.ANY,
"existing_server_default": False,
},
"the amount",
"the adjusted amount",
)
]
],
)

View File

@@ -0,0 +1,203 @@
import sqlalchemy as sa
from sqlalchemy import Column
from sqlalchemy import Integer
from sqlalchemy import MetaData
from sqlalchemy import Table
from ._autogen_fixtures import AutogenFixtureTest
from ... import testing
from ...testing import config
from ...testing import eq_
from ...testing import exclusions
from ...testing import is_
from ...testing import is_true
from ...testing import mock
from ...testing import TestBase
class AutogenerateComputedTest(AutogenFixtureTest, TestBase):
__requires__ = ("computed_columns",)
__backend__ = True
def test_add_computed_column(self):
m1 = MetaData()
m2 = MetaData()
Table("user", m1, Column("id", Integer, primary_key=True))
Table(
"user",
m2,
Column("id", Integer, primary_key=True),
Column("foo", Integer, sa.Computed("5")),
)
diffs = self._fixture(m1, m2)
eq_(diffs[0][0], "add_column")
eq_(diffs[0][2], "user")
eq_(diffs[0][3].name, "foo")
c = diffs[0][3].computed
is_true(isinstance(c, sa.Computed))
is_(c.persisted, None)
eq_(str(c.sqltext), "5")
def test_remove_computed_column(self):
m1 = MetaData()
m2 = MetaData()
Table(
"user",
m1,
Column("id", Integer, primary_key=True),
Column("foo", Integer, sa.Computed("5")),
)
Table("user", m2, Column("id", Integer, primary_key=True))
diffs = self._fixture(m1, m2)
eq_(diffs[0][0], "remove_column")
eq_(diffs[0][2], "user")
c = diffs[0][3]
eq_(c.name, "foo")
if config.requirements.computed_reflects_normally.enabled:
is_true(isinstance(c.computed, sa.Computed))
else:
is_(c.computed, None)
if config.requirements.computed_reflects_as_server_default.enabled:
is_true(isinstance(c.server_default, sa.DefaultClause))
eq_(str(c.server_default.arg.text), "5")
elif config.requirements.computed_reflects_normally.enabled:
is_true(isinstance(c.computed, sa.Computed))
else:
is_(c.computed, None)
@testing.combinations(
lambda: (None, sa.Computed("bar*5")),
(lambda: (sa.Computed("bar*5"), None)),
lambda: (
sa.Computed("bar*5"),
sa.Computed("bar * 42", persisted=True),
),
lambda: (sa.Computed("bar*5"), sa.Computed("bar * 42")),
)
@config.requirements.computed_reflects_normally
def test_cant_change_computed_warning(self, test_case):
arg_before, arg_after = testing.resolve_lambda(test_case, **locals())
m1 = MetaData()
m2 = MetaData()
arg_before = [] if arg_before is None else [arg_before]
arg_after = [] if arg_after is None else [arg_after]
Table(
"user",
m1,
Column("id", Integer, primary_key=True),
Column("bar", Integer),
Column("foo", Integer, *arg_before),
)
Table(
"user",
m2,
Column("id", Integer, primary_key=True),
Column("bar", Integer),
Column("foo", Integer, *arg_after),
)
with mock.patch("alembic.util.warn") as mock_warn:
diffs = self._fixture(m1, m2)
eq_(
mock_warn.mock_calls,
[mock.call("Computed default on user.foo cannot be modified")],
)
eq_(list(diffs), [])
@testing.combinations(
lambda: (None, None),
lambda: (sa.Computed("5"), sa.Computed("5")),
lambda: (sa.Computed("bar*5"), sa.Computed("bar*5")),
(
lambda: (sa.Computed("bar*5"), None),
config.requirements.computed_doesnt_reflect_as_server_default,
),
)
def test_computed_unchanged(self, test_case):
arg_before, arg_after = testing.resolve_lambda(test_case, **locals())
m1 = MetaData()
m2 = MetaData()
arg_before = [] if arg_before is None else [arg_before]
arg_after = [] if arg_after is None else [arg_after]
Table(
"user",
m1,
Column("id", Integer, primary_key=True),
Column("bar", Integer),
Column("foo", Integer, *arg_before),
)
Table(
"user",
m2,
Column("id", Integer, primary_key=True),
Column("bar", Integer),
Column("foo", Integer, *arg_after),
)
with mock.patch("alembic.util.warn") as mock_warn:
diffs = self._fixture(m1, m2)
eq_(mock_warn.mock_calls, [])
eq_(list(diffs), [])
@config.requirements.computed_reflects_as_server_default
def test_remove_computed_default_on_computed(self):
"""Asserts the current behavior which is that on PG and Oracle,
the GENERATED ALWAYS AS is reflected as a server default which we can't
tell is actually "computed", so these come out as a modification to
the server default.
"""
m1 = MetaData()
m2 = MetaData()
Table(
"user",
m1,
Column("id", Integer, primary_key=True),
Column("bar", Integer),
Column("foo", Integer, sa.Computed("bar + 42")),
)
Table(
"user",
m2,
Column("id", Integer, primary_key=True),
Column("bar", Integer),
Column("foo", Integer),
)
diffs = self._fixture(m1, m2)
eq_(diffs[0][0][0], "modify_default")
eq_(diffs[0][0][2], "user")
eq_(diffs[0][0][3], "foo")
old = diffs[0][0][-2]
new = diffs[0][0][-1]
is_(new, None)
is_true(isinstance(old, sa.DefaultClause))
if exclusions.against(config, "postgresql"):
eq_(str(old.arg.text), "(bar + 42)")
elif exclusions.against(config, "oracle"):
eq_(str(old.arg.text), '"BAR"+42')

View File

@@ -0,0 +1,273 @@
from sqlalchemy import BigInteger
from sqlalchemy import Column
from sqlalchemy import Integer
from sqlalchemy import MetaData
from sqlalchemy import Table
from sqlalchemy.testing import in_
from ._autogen_fixtures import AutogenFixtureTest
from ... import testing
from ...testing import config
from ...testing import eq_
from ...testing import is_
from ...testing import TestBase
class AlterColumnTest(AutogenFixtureTest, TestBase):
__backend__ = True
@testing.combinations((True,), (False,))
@config.requirements.comments
def test_all_existings_filled(self, pk):
m1 = MetaData()
m2 = MetaData()
Table("a", m1, Column("x", Integer, primary_key=pk))
Table("a", m2, Column("x", Integer, comment="x", primary_key=pk))
alter_col = self._assert_alter_col(m1, m2, pk)
eq_(alter_col.modify_comment, "x")
@testing.combinations((True,), (False,))
@config.requirements.comments
def test_all_existings_filled_in_notnull(self, pk):
m1 = MetaData()
m2 = MetaData()
Table("a", m1, Column("x", Integer, nullable=False, primary_key=pk))
Table(
"a",
m2,
Column("x", Integer, nullable=False, comment="x", primary_key=pk),
)
self._assert_alter_col(m1, m2, pk, nullable=False)
@testing.combinations((True,), (False,))
@config.requirements.comments
def test_all_existings_filled_in_comment(self, pk):
m1 = MetaData()
m2 = MetaData()
Table("a", m1, Column("x", Integer, comment="old", primary_key=pk))
Table("a", m2, Column("x", Integer, comment="new", primary_key=pk))
alter_col = self._assert_alter_col(m1, m2, pk)
eq_(alter_col.existing_comment, "old")
@testing.combinations((True,), (False,))
@config.requirements.comments
def test_all_existings_filled_in_server_default(self, pk):
m1 = MetaData()
m2 = MetaData()
Table(
"a", m1, Column("x", Integer, server_default="5", primary_key=pk)
)
Table(
"a",
m2,
Column(
"x", Integer, server_default="5", comment="new", primary_key=pk
),
)
alter_col = self._assert_alter_col(m1, m2, pk)
in_("5", alter_col.existing_server_default.arg.text)
def _assert_alter_col(self, m1, m2, pk, nullable=None):
ops = self._fixture(m1, m2, return_ops=True)
modify_table = ops.ops[-1]
alter_col = modify_table.ops[0]
if nullable is None:
eq_(alter_col.existing_nullable, not pk)
else:
eq_(alter_col.existing_nullable, nullable)
assert alter_col.existing_type._compare_type_affinity(Integer())
return alter_col
class AutoincrementTest(AutogenFixtureTest, TestBase):
__backend__ = True
__requires__ = ("integer_subtype_comparisons",)
def test_alter_column_autoincrement_none(self):
m1 = MetaData()
m2 = MetaData()
Table("a", m1, Column("x", Integer, nullable=False))
Table("a", m2, Column("x", Integer, nullable=True))
ops = self._fixture(m1, m2, return_ops=True)
assert "autoincrement" not in ops.ops[0].ops[0].kw
def test_alter_column_autoincrement_pk_false(self):
m1 = MetaData()
m2 = MetaData()
Table(
"a",
m1,
Column("x", Integer, primary_key=True, autoincrement=False),
)
Table(
"a",
m2,
Column("x", BigInteger, primary_key=True, autoincrement=False),
)
ops = self._fixture(m1, m2, return_ops=True)
is_(ops.ops[0].ops[0].kw["autoincrement"], False)
def test_alter_column_autoincrement_pk_implicit_true(self):
m1 = MetaData()
m2 = MetaData()
Table("a", m1, Column("x", Integer, primary_key=True))
Table("a", m2, Column("x", BigInteger, primary_key=True))
ops = self._fixture(m1, m2, return_ops=True)
is_(ops.ops[0].ops[0].kw["autoincrement"], True)
def test_alter_column_autoincrement_pk_explicit_true(self):
m1 = MetaData()
m2 = MetaData()
Table(
"a", m1, Column("x", Integer, primary_key=True, autoincrement=True)
)
Table(
"a",
m2,
Column("x", BigInteger, primary_key=True, autoincrement=True),
)
ops = self._fixture(m1, m2, return_ops=True)
is_(ops.ops[0].ops[0].kw["autoincrement"], True)
def test_alter_column_autoincrement_nonpk_false(self):
m1 = MetaData()
m2 = MetaData()
Table(
"a",
m1,
Column("id", Integer, primary_key=True),
Column("x", Integer, autoincrement=False),
)
Table(
"a",
m2,
Column("id", Integer, primary_key=True),
Column("x", BigInteger, autoincrement=False),
)
ops = self._fixture(m1, m2, return_ops=True)
is_(ops.ops[0].ops[0].kw["autoincrement"], False)
def test_alter_column_autoincrement_nonpk_implicit_false(self):
m1 = MetaData()
m2 = MetaData()
Table(
"a",
m1,
Column("id", Integer, primary_key=True),
Column("x", Integer),
)
Table(
"a",
m2,
Column("id", Integer, primary_key=True),
Column("x", BigInteger),
)
ops = self._fixture(m1, m2, return_ops=True)
assert "autoincrement" not in ops.ops[0].ops[0].kw
def test_alter_column_autoincrement_nonpk_explicit_true(self):
m1 = MetaData()
m2 = MetaData()
Table(
"a",
m1,
Column("id", Integer, primary_key=True, autoincrement=False),
Column("x", Integer, autoincrement=True),
)
Table(
"a",
m2,
Column("id", Integer, primary_key=True, autoincrement=False),
Column("x", BigInteger, autoincrement=True),
)
ops = self._fixture(m1, m2, return_ops=True)
is_(ops.ops[0].ops[0].kw["autoincrement"], True)
def test_alter_column_autoincrement_compositepk_false(self):
m1 = MetaData()
m2 = MetaData()
Table(
"a",
m1,
Column("id", Integer, primary_key=True),
Column("x", Integer, primary_key=True, autoincrement=False),
)
Table(
"a",
m2,
Column("id", Integer, primary_key=True),
Column("x", BigInteger, primary_key=True, autoincrement=False),
)
ops = self._fixture(m1, m2, return_ops=True)
is_(ops.ops[0].ops[0].kw["autoincrement"], False)
def test_alter_column_autoincrement_compositepk_implicit_false(self):
m1 = MetaData()
m2 = MetaData()
Table(
"a",
m1,
Column("id", Integer, primary_key=True),
Column("x", Integer, primary_key=True),
)
Table(
"a",
m2,
Column("id", Integer, primary_key=True),
Column("x", BigInteger, primary_key=True),
)
ops = self._fixture(m1, m2, return_ops=True)
assert "autoincrement" not in ops.ops[0].ops[0].kw
@config.requirements.autoincrement_on_composite_pk
def test_alter_column_autoincrement_compositepk_explicit_true(self):
m1 = MetaData()
m2 = MetaData()
Table(
"a",
m1,
Column("id", Integer, primary_key=True, autoincrement=False),
Column("x", Integer, primary_key=True, autoincrement=True),
# on SQLA 1.0 and earlier, this being present
# trips the "add KEY for the primary key" so that the
# AUTO_INCREMENT keyword is accepted by MySQL. SQLA 1.1 and
# greater the columns are just reorganized.
mysql_engine="InnoDB",
)
Table(
"a",
m2,
Column("id", Integer, primary_key=True, autoincrement=False),
Column("x", BigInteger, primary_key=True, autoincrement=True),
)
ops = self._fixture(m1, m2, return_ops=True)
is_(ops.ops[0].ops[0].kw["autoincrement"], True)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,226 @@
import sqlalchemy as sa
from sqlalchemy import Column
from sqlalchemy import Integer
from sqlalchemy import MetaData
from sqlalchemy import Table
from alembic.util import sqla_compat
from ._autogen_fixtures import AutogenFixtureTest
from ... import testing
from ...testing import config
from ...testing import eq_
from ...testing import is_true
from ...testing import TestBase
class AutogenerateIdentityTest(AutogenFixtureTest, TestBase):
__requires__ = ("identity_columns",)
__backend__ = True
def test_add_identity_column(self):
m1 = MetaData()
m2 = MetaData()
Table("user", m1, Column("other", sa.Text))
Table(
"user",
m2,
Column("other", sa.Text),
Column(
"id",
Integer,
sa.Identity(start=5, increment=7),
primary_key=True,
),
)
diffs = self._fixture(m1, m2)
eq_(diffs[0][0], "add_column")
eq_(diffs[0][2], "user")
eq_(diffs[0][3].name, "id")
i = diffs[0][3].identity
is_true(isinstance(i, sa.Identity))
eq_(i.start, 5)
eq_(i.increment, 7)
def test_remove_identity_column(self):
m1 = MetaData()
m2 = MetaData()
Table(
"user",
m1,
Column(
"id",
Integer,
sa.Identity(start=2, increment=3),
primary_key=True,
),
)
Table("user", m2)
diffs = self._fixture(m1, m2)
eq_(diffs[0][0], "remove_column")
eq_(diffs[0][2], "user")
c = diffs[0][3]
eq_(c.name, "id")
is_true(isinstance(c.identity, sa.Identity))
eq_(c.identity.start, 2)
eq_(c.identity.increment, 3)
def test_no_change_identity_column(self):
m1 = MetaData()
m2 = MetaData()
for m in (m1, m2):
id_ = sa.Identity(start=2)
Table("user", m, Column("id", Integer, id_))
diffs = self._fixture(m1, m2)
eq_(diffs, [])
def test_dialect_kwargs_changes(self):
m1 = MetaData()
m2 = MetaData()
if sqla_compat.identity_has_dialect_kwargs:
args = {"oracle_on_null": True, "oracle_order": True}
else:
args = {"on_null": True, "order": True}
Table("user", m1, Column("id", Integer, sa.Identity(start=2)))
id_ = sa.Identity(start=2, **args)
Table("user", m2, Column("id", Integer, id_))
diffs = self._fixture(m1, m2)
if config.db.name == "oracle":
is_true(len(diffs), 1)
eq_(diffs[0][0][0], "modify_default")
else:
eq_(diffs, [])
@testing.combinations(
(None, dict(start=2)),
(dict(start=2), None),
(dict(start=2), dict(start=2, increment=7)),
(dict(always=False), dict(always=True)),
(
dict(start=1, minvalue=0, maxvalue=100, cycle=True),
dict(start=1, minvalue=0, maxvalue=100, cycle=False),
),
(
dict(start=10, increment=3, maxvalue=9999),
dict(start=10, increment=1, maxvalue=3333),
),
)
@config.requirements.identity_columns_alter
def test_change_identity(self, before, after):
arg_before = (sa.Identity(**before),) if before else ()
arg_after = (sa.Identity(**after),) if after else ()
m1 = MetaData()
m2 = MetaData()
Table(
"user",
m1,
Column("id", Integer, *arg_before),
Column("other", sa.Text),
)
Table(
"user",
m2,
Column("id", Integer, *arg_after),
Column("other", sa.Text),
)
diffs = self._fixture(m1, m2)
eq_(len(diffs[0]), 1)
diffs = diffs[0][0]
eq_(diffs[0], "modify_default")
eq_(diffs[2], "user")
eq_(diffs[3], "id")
old = diffs[5]
new = diffs[6]
def check(kw, idt):
if kw:
is_true(isinstance(idt, sa.Identity))
for k, v in kw.items():
eq_(getattr(idt, k), v)
else:
is_true(idt in (None, False))
check(before, old)
check(after, new)
def test_add_identity_to_column(self):
m1 = MetaData()
m2 = MetaData()
Table(
"user",
m1,
Column("id", Integer),
Column("other", sa.Text),
)
Table(
"user",
m2,
Column("id", Integer, sa.Identity(start=2, maxvalue=1000)),
Column("other", sa.Text),
)
diffs = self._fixture(m1, m2)
eq_(len(diffs[0]), 1)
diffs = diffs[0][0]
eq_(diffs[0], "modify_default")
eq_(diffs[2], "user")
eq_(diffs[3], "id")
eq_(diffs[5], None)
added = diffs[6]
is_true(isinstance(added, sa.Identity))
eq_(added.start, 2)
eq_(added.maxvalue, 1000)
def test_remove_identity_from_column(self):
m1 = MetaData()
m2 = MetaData()
Table(
"user",
m1,
Column("id", Integer, sa.Identity(start=2, maxvalue=1000)),
Column("other", sa.Text),
)
Table(
"user",
m2,
Column("id", Integer),
Column("other", sa.Text),
)
diffs = self._fixture(m1, m2)
eq_(len(diffs[0]), 1)
diffs = diffs[0][0]
eq_(diffs[0], "modify_default")
eq_(diffs[2], "user")
eq_(diffs[3], "id")
eq_(diffs[6], None)
removed = diffs[5]
is_true(isinstance(removed, sa.Identity))

View File

@@ -0,0 +1,364 @@
import io
from ...migration import MigrationContext
from ...testing import assert_raises
from ...testing import config
from ...testing import eq_
from ...testing import is_
from ...testing import is_false
from ...testing import is_not_
from ...testing import is_true
from ...testing import ne_
from ...testing.fixtures import TestBase
class MigrationTransactionTest(TestBase):
__backend__ = True
conn = None
def _fixture(self, opts):
self.conn = conn = config.db.connect()
if opts.get("as_sql", False):
self.context = MigrationContext.configure(
dialect=conn.dialect, opts=opts
)
self.context.output_buffer = (
self.context.impl.output_buffer
) = io.StringIO()
else:
self.context = MigrationContext.configure(
connection=conn, opts=opts
)
return self.context
def teardown_method(self):
if self.conn:
self.conn.close()
def test_proxy_transaction_rollback(self):
context = self._fixture(
{"transaction_per_migration": True, "transactional_ddl": True}
)
is_false(self.conn.in_transaction())
proxy = context.begin_transaction(_per_migration=True)
is_true(self.conn.in_transaction())
proxy.rollback()
is_false(self.conn.in_transaction())
def test_proxy_transaction_commit(self):
context = self._fixture(
{"transaction_per_migration": True, "transactional_ddl": True}
)
proxy = context.begin_transaction(_per_migration=True)
is_true(self.conn.in_transaction())
proxy.commit()
is_false(self.conn.in_transaction())
def test_proxy_transaction_contextmanager_commit(self):
context = self._fixture(
{"transaction_per_migration": True, "transactional_ddl": True}
)
proxy = context.begin_transaction(_per_migration=True)
is_true(self.conn.in_transaction())
with proxy:
pass
is_false(self.conn.in_transaction())
def test_proxy_transaction_contextmanager_rollback(self):
context = self._fixture(
{"transaction_per_migration": True, "transactional_ddl": True}
)
proxy = context.begin_transaction(_per_migration=True)
is_true(self.conn.in_transaction())
def go():
with proxy:
raise Exception("hi")
assert_raises(Exception, go)
is_false(self.conn.in_transaction())
def test_proxy_transaction_contextmanager_explicit_rollback(self):
context = self._fixture(
{"transaction_per_migration": True, "transactional_ddl": True}
)
proxy = context.begin_transaction(_per_migration=True)
is_true(self.conn.in_transaction())
with proxy:
is_true(self.conn.in_transaction())
proxy.rollback()
is_false(self.conn.in_transaction())
is_false(self.conn.in_transaction())
def test_proxy_transaction_contextmanager_explicit_commit(self):
context = self._fixture(
{"transaction_per_migration": True, "transactional_ddl": True}
)
proxy = context.begin_transaction(_per_migration=True)
is_true(self.conn.in_transaction())
with proxy:
is_true(self.conn.in_transaction())
proxy.commit()
is_false(self.conn.in_transaction())
is_false(self.conn.in_transaction())
def test_transaction_per_migration_transactional_ddl(self):
context = self._fixture(
{"transaction_per_migration": True, "transactional_ddl": True}
)
is_false(self.conn.in_transaction())
with context.begin_transaction():
is_false(self.conn.in_transaction())
with context.begin_transaction(_per_migration=True):
is_true(self.conn.in_transaction())
is_false(self.conn.in_transaction())
is_false(self.conn.in_transaction())
def test_transaction_per_migration_non_transactional_ddl(self):
context = self._fixture(
{"transaction_per_migration": True, "transactional_ddl": False}
)
is_false(self.conn.in_transaction())
with context.begin_transaction():
is_false(self.conn.in_transaction())
with context.begin_transaction(_per_migration=True):
is_true(self.conn.in_transaction())
is_false(self.conn.in_transaction())
is_false(self.conn.in_transaction())
def test_transaction_per_all_transactional_ddl(self):
context = self._fixture({"transactional_ddl": True})
is_false(self.conn.in_transaction())
with context.begin_transaction():
is_true(self.conn.in_transaction())
with context.begin_transaction(_per_migration=True):
is_true(self.conn.in_transaction())
is_true(self.conn.in_transaction())
is_false(self.conn.in_transaction())
def test_transaction_per_all_non_transactional_ddl(self):
context = self._fixture({"transactional_ddl": False})
is_false(self.conn.in_transaction())
with context.begin_transaction():
is_false(self.conn.in_transaction())
with context.begin_transaction(_per_migration=True):
is_true(self.conn.in_transaction())
is_false(self.conn.in_transaction())
is_false(self.conn.in_transaction())
def test_transaction_per_all_sqlmode(self):
context = self._fixture({"as_sql": True})
context.execute("step 1")
with context.begin_transaction():
context.execute("step 2")
with context.begin_transaction(_per_migration=True):
context.execute("step 3")
context.execute("step 4")
context.execute("step 5")
if context.impl.transactional_ddl:
self._assert_impl_steps(
"step 1",
"BEGIN",
"step 2",
"step 3",
"step 4",
"COMMIT",
"step 5",
)
else:
self._assert_impl_steps(
"step 1", "step 2", "step 3", "step 4", "step 5"
)
def test_transaction_per_migration_sqlmode(self):
context = self._fixture(
{"as_sql": True, "transaction_per_migration": True}
)
context.execute("step 1")
with context.begin_transaction():
context.execute("step 2")
with context.begin_transaction(_per_migration=True):
context.execute("step 3")
context.execute("step 4")
context.execute("step 5")
if context.impl.transactional_ddl:
self._assert_impl_steps(
"step 1",
"step 2",
"BEGIN",
"step 3",
"COMMIT",
"step 4",
"step 5",
)
else:
self._assert_impl_steps(
"step 1", "step 2", "step 3", "step 4", "step 5"
)
@config.requirements.autocommit_isolation
def test_autocommit_block(self):
context = self._fixture({"transaction_per_migration": True})
is_false(self.conn.in_transaction())
with context.begin_transaction():
is_false(self.conn.in_transaction())
with context.begin_transaction(_per_migration=True):
is_true(self.conn.in_transaction())
with context.autocommit_block():
# in 1.x, self.conn is separate due to the
# execution_options call. however for future they are the
# same connection and there is a "transaction" block
# despite autocommit
if self.is_sqlalchemy_future:
is_(context.connection, self.conn)
else:
is_not_(context.connection, self.conn)
is_false(self.conn.in_transaction())
eq_(
context.connection._execution_options[
"isolation_level"
],
"AUTOCOMMIT",
)
ne_(
context.connection._execution_options.get(
"isolation_level", None
),
"AUTOCOMMIT",
)
is_true(self.conn.in_transaction())
is_false(self.conn.in_transaction())
is_false(self.conn.in_transaction())
@config.requirements.autocommit_isolation
def test_autocommit_block_no_transaction(self):
context = self._fixture({"transaction_per_migration": True})
is_false(self.conn.in_transaction())
with context.autocommit_block():
is_true(context.connection.in_transaction())
# in 1.x, self.conn is separate due to the execution_options
# call. however for future they are the same connection and there
# is a "transaction" block despite autocommit
if self.is_sqlalchemy_future:
is_(context.connection, self.conn)
else:
is_not_(context.connection, self.conn)
is_false(self.conn.in_transaction())
eq_(
context.connection._execution_options["isolation_level"],
"AUTOCOMMIT",
)
ne_(
context.connection._execution_options.get("isolation_level", None),
"AUTOCOMMIT",
)
is_false(self.conn.in_transaction())
def test_autocommit_block_transactional_ddl_sqlmode(self):
context = self._fixture(
{
"transaction_per_migration": True,
"transactional_ddl": True,
"as_sql": True,
}
)
with context.begin_transaction():
context.execute("step 1")
with context.begin_transaction(_per_migration=True):
context.execute("step 2")
with context.autocommit_block():
context.execute("step 3")
context.execute("step 4")
context.execute("step 5")
self._assert_impl_steps(
"step 1",
"BEGIN",
"step 2",
"COMMIT",
"step 3",
"BEGIN",
"step 4",
"COMMIT",
"step 5",
)
def test_autocommit_block_nontransactional_ddl_sqlmode(self):
context = self._fixture(
{
"transaction_per_migration": True,
"transactional_ddl": False,
"as_sql": True,
}
)
with context.begin_transaction():
context.execute("step 1")
with context.begin_transaction(_per_migration=True):
context.execute("step 2")
with context.autocommit_block():
context.execute("step 3")
context.execute("step 4")
context.execute("step 5")
self._assert_impl_steps(
"step 1", "step 2", "step 3", "step 4", "step 5"
)
def _assert_impl_steps(self, *steps):
to_check = self.context.output_buffer.getvalue()
self.context.impl.output_buffer = buf = io.StringIO()
for step in steps:
if step == "BEGIN":
self.context.impl.emit_begin()
elif step == "COMMIT":
self.context.impl.emit_commit()
else:
self.context.impl._exec(step)
eq_(to_check, buf.getvalue())

View File

@@ -0,0 +1,42 @@
"""Test against the builders in the op.* module."""
from sqlalchemy import Column
from sqlalchemy import event
from sqlalchemy import Integer
from sqlalchemy import String
from sqlalchemy import Table
from sqlalchemy.sql import text
from ...testing.fixtures import AlterColRoundTripFixture
from ...testing.fixtures import TestBase
@event.listens_for(Table, "after_parent_attach")
def _add_cols(table, metadata):
if table.name == "tbl_with_auto_appended_column":
table.append_column(Column("bat", Integer))
class BackendAlterColumnTest(AlterColRoundTripFixture, TestBase):
__backend__ = True
def test_rename_column(self):
self._run_alter_col({}, {"name": "newname"})
def test_modify_type_int_str(self):
self._run_alter_col({"type": Integer()}, {"type": String(50)})
def test_add_server_default_int(self):
self._run_alter_col({"type": Integer}, {"server_default": text("5")})
def test_modify_server_default_int(self):
self._run_alter_col(
{"type": Integer, "server_default": text("2")},
{"server_default": text("5")},
)
def test_modify_nullable_to_non(self):
self._run_alter_col({}, {"nullable": False})
def test_modify_non_nullable_to_nullable(self):
self._run_alter_col({"nullable": False}, {"nullable": True})