alembic 1.12.1__py3-none-any.whl → 1.13.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- alembic/__init__.py +1 -3
- alembic/autogenerate/__init__.py +10 -10
- alembic/autogenerate/api.py +8 -5
- alembic/autogenerate/compare.py +134 -199
- alembic/autogenerate/render.py +39 -24
- alembic/autogenerate/rewriter.py +26 -13
- alembic/command.py +7 -2
- alembic/config.py +20 -9
- alembic/context.pyi +12 -6
- alembic/ddl/__init__.py +1 -1
- alembic/ddl/_autogen.py +325 -0
- alembic/ddl/base.py +12 -9
- alembic/ddl/impl.py +110 -13
- alembic/ddl/mssql.py +4 -1
- alembic/ddl/mysql.py +9 -6
- alembic/ddl/oracle.py +4 -1
- alembic/ddl/postgresql.py +147 -73
- alembic/ddl/sqlite.py +8 -6
- alembic/op.pyi +46 -8
- alembic/operations/base.py +70 -14
- alembic/operations/batch.py +7 -8
- alembic/operations/ops.py +53 -31
- alembic/operations/schemaobj.py +5 -4
- alembic/operations/toimpl.py +8 -5
- alembic/runtime/environment.py +17 -7
- alembic/runtime/migration.py +27 -11
- alembic/script/base.py +34 -27
- alembic/script/revision.py +30 -17
- alembic/script/write_hooks.py +3 -0
- alembic/templates/async/alembic.ini.mako +3 -3
- alembic/templates/generic/alembic.ini.mako +3 -3
- alembic/templates/multidb/alembic.ini.mako +3 -3
- alembic/testing/requirements.py +12 -0
- alembic/testing/schemacompare.py +9 -0
- alembic/util/__init__.py +31 -31
- alembic/util/compat.py +25 -9
- alembic/util/langhelpers.py +78 -33
- alembic/util/messaging.py +6 -3
- alembic/util/pyfiles.py +7 -3
- alembic/util/sqla_compat.py +52 -26
- {alembic-1.12.1.dist-info → alembic-1.13.1.dist-info}/METADATA +5 -4
- alembic-1.13.1.dist-info/RECORD +83 -0
- {alembic-1.12.1.dist-info → alembic-1.13.1.dist-info}/WHEEL +1 -1
- alembic-1.12.1.dist-info/RECORD +0 -82
- {alembic-1.12.1.dist-info → alembic-1.13.1.dist-info}/LICENSE +0 -0
- {alembic-1.12.1.dist-info → alembic-1.13.1.dist-info}/entry_points.txt +0 -0
- {alembic-1.12.1.dist-info → alembic-1.13.1.dist-info}/top_level.txt +0 -0
alembic/__init__.py
CHANGED
alembic/autogenerate/__init__.py
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
from .api import _render_migration_diffs
|
2
|
-
from .api import compare_metadata
|
3
|
-
from .api import produce_migrations
|
4
|
-
from .api import render_python_code
|
5
|
-
from .api import RevisionContext
|
6
|
-
from .compare import _produce_net_changes
|
7
|
-
from .compare import comparators
|
8
|
-
from .render import render_op_text
|
9
|
-
from .render import renderers
|
10
|
-
from .rewriter import Rewriter
|
1
|
+
from .api import _render_migration_diffs as _render_migration_diffs
|
2
|
+
from .api import compare_metadata as compare_metadata
|
3
|
+
from .api import produce_migrations as produce_migrations
|
4
|
+
from .api import render_python_code as render_python_code
|
5
|
+
from .api import RevisionContext as RevisionContext
|
6
|
+
from .compare import _produce_net_changes as _produce_net_changes
|
7
|
+
from .compare import comparators as comparators
|
8
|
+
from .render import render_op_text as render_op_text
|
9
|
+
from .render import renderers as renderers
|
10
|
+
from .rewriter import Rewriter as Rewriter
|
alembic/autogenerate/api.py
CHANGED
@@ -17,6 +17,7 @@ from . import compare
|
|
17
17
|
from . import render
|
18
18
|
from .. import util
|
19
19
|
from ..operations import ops
|
20
|
+
from ..util import sqla_compat
|
20
21
|
|
21
22
|
"""Provide the 'autogenerate' feature which can produce migration operations
|
22
23
|
automatically."""
|
@@ -27,6 +28,7 @@ if TYPE_CHECKING:
|
|
27
28
|
from sqlalchemy.engine import Inspector
|
28
29
|
from sqlalchemy.sql.schema import MetaData
|
29
30
|
from sqlalchemy.sql.schema import SchemaItem
|
31
|
+
from sqlalchemy.sql.schema import Table
|
30
32
|
|
31
33
|
from ..config import Config
|
32
34
|
from ..operations.ops import DowngradeOps
|
@@ -164,6 +166,7 @@ def compare_metadata(context: MigrationContext, metadata: MetaData) -> Any:
|
|
164
166
|
"""
|
165
167
|
|
166
168
|
migration_script = produce_migrations(context, metadata)
|
169
|
+
assert migration_script.upgrade_ops is not None
|
167
170
|
return migration_script.upgrade_ops.as_diffs()
|
168
171
|
|
169
172
|
|
@@ -330,7 +333,7 @@ class AutogenContext:
|
|
330
333
|
self,
|
331
334
|
migration_context: MigrationContext,
|
332
335
|
metadata: Optional[MetaData] = None,
|
333
|
-
opts: Optional[
|
336
|
+
opts: Optional[Dict[str, Any]] = None,
|
334
337
|
autogenerate: bool = True,
|
335
338
|
) -> None:
|
336
339
|
if (
|
@@ -440,7 +443,7 @@ class AutogenContext:
|
|
440
443
|
def run_object_filters(
|
441
444
|
self,
|
442
445
|
object_: SchemaItem,
|
443
|
-
name:
|
446
|
+
name: sqla_compat._ConstraintName,
|
444
447
|
type_: NameFilterType,
|
445
448
|
reflected: bool,
|
446
449
|
compare_to: Optional[SchemaItem],
|
@@ -464,7 +467,7 @@ class AutogenContext:
|
|
464
467
|
run_filters = run_object_filters
|
465
468
|
|
466
469
|
@util.memoized_property
|
467
|
-
def sorted_tables(self):
|
470
|
+
def sorted_tables(self) -> List[Table]:
|
468
471
|
"""Return an aggregate of the :attr:`.MetaData.sorted_tables`
|
469
472
|
collection(s).
|
470
473
|
|
@@ -480,7 +483,7 @@ class AutogenContext:
|
|
480
483
|
return result
|
481
484
|
|
482
485
|
@util.memoized_property
|
483
|
-
def table_key_to_table(self):
|
486
|
+
def table_key_to_table(self) -> Dict[str, Table]:
|
484
487
|
"""Return an aggregate of the :attr:`.MetaData.tables` dictionaries.
|
485
488
|
|
486
489
|
The :attr:`.MetaData.tables` collection is a dictionary of table key
|
@@ -491,7 +494,7 @@ class AutogenContext:
|
|
491
494
|
objects contain the same table key, an exception is raised.
|
492
495
|
|
493
496
|
"""
|
494
|
-
result = {}
|
497
|
+
result: Dict[str, Table] = {}
|
495
498
|
for m in util.to_list(self.metadata):
|
496
499
|
intersect = set(result).intersection(set(m.tables))
|
497
500
|
if intersect:
|
alembic/autogenerate/compare.py
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
# mypy: allow-untyped-defs, allow-incomplete-defs, allow-untyped-calls
|
2
|
+
# mypy: no-warn-return-any, allow-any-generics
|
3
|
+
|
1
4
|
from __future__ import annotations
|
2
5
|
|
3
6
|
import contextlib
|
@@ -7,12 +10,12 @@ from typing import Any
|
|
7
10
|
from typing import cast
|
8
11
|
from typing import Dict
|
9
12
|
from typing import Iterator
|
10
|
-
from typing import List
|
11
13
|
from typing import Mapping
|
12
14
|
from typing import Optional
|
13
15
|
from typing import Set
|
14
16
|
from typing import Tuple
|
15
17
|
from typing import TYPE_CHECKING
|
18
|
+
from typing import TypeVar
|
16
19
|
from typing import Union
|
17
20
|
|
18
21
|
from sqlalchemy import event
|
@@ -21,10 +24,14 @@ from sqlalchemy import schema as sa_schema
|
|
21
24
|
from sqlalchemy import text
|
22
25
|
from sqlalchemy import types as sqltypes
|
23
26
|
from sqlalchemy.sql import expression
|
27
|
+
from sqlalchemy.sql.schema import ForeignKeyConstraint
|
28
|
+
from sqlalchemy.sql.schema import Index
|
29
|
+
from sqlalchemy.sql.schema import UniqueConstraint
|
24
30
|
from sqlalchemy.util import OrderedSet
|
25
31
|
|
26
|
-
from alembic.ddl.base import _fk_spec
|
27
32
|
from .. import util
|
33
|
+
from ..ddl._autogen import is_index_sig
|
34
|
+
from ..ddl._autogen import is_uq_sig
|
28
35
|
from ..operations import ops
|
29
36
|
from ..util import sqla_compat
|
30
37
|
|
@@ -35,10 +42,7 @@ if TYPE_CHECKING:
|
|
35
42
|
from sqlalchemy.sql.elements import quoted_name
|
36
43
|
from sqlalchemy.sql.elements import TextClause
|
37
44
|
from sqlalchemy.sql.schema import Column
|
38
|
-
from sqlalchemy.sql.schema import ForeignKeyConstraint
|
39
|
-
from sqlalchemy.sql.schema import Index
|
40
45
|
from sqlalchemy.sql.schema import Table
|
41
|
-
from sqlalchemy.sql.schema import UniqueConstraint
|
42
46
|
|
43
47
|
from alembic.autogenerate.api import AutogenContext
|
44
48
|
from alembic.ddl.impl import DefaultImpl
|
@@ -46,6 +50,8 @@ if TYPE_CHECKING:
|
|
46
50
|
from alembic.operations.ops import MigrationScript
|
47
51
|
from alembic.operations.ops import ModifyTableOps
|
48
52
|
from alembic.operations.ops import UpgradeOps
|
53
|
+
from ..ddl._autogen import _constraint_sig
|
54
|
+
|
49
55
|
|
50
56
|
log = logging.getLogger(__name__)
|
51
57
|
|
@@ -429,102 +435,7 @@ def _compare_columns(
|
|
429
435
|
log.info("Detected removed column '%s.%s'", name, cname)
|
430
436
|
|
431
437
|
|
432
|
-
|
433
|
-
const: Union[UniqueConstraint, ForeignKeyConstraint, Index]
|
434
|
-
|
435
|
-
def md_name_to_sql_name(self, context: AutogenContext) -> Optional[str]:
|
436
|
-
return sqla_compat._get_constraint_final_name(
|
437
|
-
self.const, context.dialect
|
438
|
-
)
|
439
|
-
|
440
|
-
def __eq__(self, other):
|
441
|
-
return self.const == other.const
|
442
|
-
|
443
|
-
def __ne__(self, other):
|
444
|
-
return self.const != other.const
|
445
|
-
|
446
|
-
def __hash__(self) -> int:
|
447
|
-
return hash(self.const)
|
448
|
-
|
449
|
-
|
450
|
-
class _uq_constraint_sig(_constraint_sig):
|
451
|
-
is_index = False
|
452
|
-
is_unique = True
|
453
|
-
|
454
|
-
def __init__(self, const: UniqueConstraint, impl: DefaultImpl) -> None:
|
455
|
-
self.const = const
|
456
|
-
self.name = const.name
|
457
|
-
self.sig = ("UNIQUE_CONSTRAINT",) + impl.create_unique_constraint_sig(
|
458
|
-
const
|
459
|
-
)
|
460
|
-
|
461
|
-
@property
|
462
|
-
def column_names(self) -> List[str]:
|
463
|
-
return [col.name for col in self.const.columns]
|
464
|
-
|
465
|
-
|
466
|
-
class _ix_constraint_sig(_constraint_sig):
|
467
|
-
is_index = True
|
468
|
-
|
469
|
-
def __init__(self, const: Index, impl: DefaultImpl) -> None:
|
470
|
-
self.const = const
|
471
|
-
self.name = const.name
|
472
|
-
self.sig = ("INDEX",) + impl.create_index_sig(const)
|
473
|
-
self.is_unique = bool(const.unique)
|
474
|
-
|
475
|
-
def md_name_to_sql_name(self, context: AutogenContext) -> Optional[str]:
|
476
|
-
return sqla_compat._get_constraint_final_name(
|
477
|
-
self.const, context.dialect
|
478
|
-
)
|
479
|
-
|
480
|
-
@property
|
481
|
-
def column_names(self) -> Union[List[quoted_name], List[None]]:
|
482
|
-
return sqla_compat._get_index_column_names(self.const)
|
483
|
-
|
484
|
-
|
485
|
-
class _fk_constraint_sig(_constraint_sig):
|
486
|
-
def __init__(
|
487
|
-
self, const: ForeignKeyConstraint, include_options: bool = False
|
488
|
-
) -> None:
|
489
|
-
self.const = const
|
490
|
-
self.name = const.name
|
491
|
-
|
492
|
-
(
|
493
|
-
self.source_schema,
|
494
|
-
self.source_table,
|
495
|
-
self.source_columns,
|
496
|
-
self.target_schema,
|
497
|
-
self.target_table,
|
498
|
-
self.target_columns,
|
499
|
-
onupdate,
|
500
|
-
ondelete,
|
501
|
-
deferrable,
|
502
|
-
initially,
|
503
|
-
) = _fk_spec(const)
|
504
|
-
|
505
|
-
self.sig: Tuple[Any, ...] = (
|
506
|
-
self.source_schema,
|
507
|
-
self.source_table,
|
508
|
-
tuple(self.source_columns),
|
509
|
-
self.target_schema,
|
510
|
-
self.target_table,
|
511
|
-
tuple(self.target_columns),
|
512
|
-
)
|
513
|
-
if include_options:
|
514
|
-
self.sig += (
|
515
|
-
(None if onupdate.lower() == "no action" else onupdate.lower())
|
516
|
-
if onupdate
|
517
|
-
else None,
|
518
|
-
(None if ondelete.lower() == "no action" else ondelete.lower())
|
519
|
-
if ondelete
|
520
|
-
else None,
|
521
|
-
# convert initially + deferrable into one three-state value
|
522
|
-
"initially_deferrable"
|
523
|
-
if initially and initially.lower() == "deferred"
|
524
|
-
else "deferrable"
|
525
|
-
if deferrable
|
526
|
-
else "not deferrable",
|
527
|
-
)
|
438
|
+
_C = TypeVar("_C", bound=Union[UniqueConstraint, ForeignKeyConstraint, Index])
|
528
439
|
|
529
440
|
|
530
441
|
@comparators.dispatch_for("table")
|
@@ -561,32 +472,31 @@ def _compare_indexes_and_uniques(
|
|
561
472
|
|
562
473
|
if conn_table is not None:
|
563
474
|
# 1b. ... and from connection, if the table exists
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
475
|
+
try:
|
476
|
+
conn_uniques = inspector.get_unique_constraints( # type:ignore[assignment] # noqa
|
477
|
+
tname, schema=schema
|
478
|
+
)
|
479
|
+
supports_unique_constraints = True
|
480
|
+
except NotImplementedError:
|
481
|
+
pass
|
482
|
+
except TypeError:
|
483
|
+
# number of arguments is off for the base
|
484
|
+
# method in SQLAlchemy due to the cache decorator
|
485
|
+
# not being present
|
486
|
+
pass
|
487
|
+
else:
|
488
|
+
conn_uniques = [ # type:ignore[assignment]
|
489
|
+
uq
|
490
|
+
for uq in conn_uniques
|
491
|
+
if autogen_context.run_name_filters(
|
492
|
+
uq["name"],
|
493
|
+
"unique_constraint",
|
494
|
+
{"table_name": tname, "schema_name": schema},
|
568
495
|
)
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
# number of arguments is off for the base
|
574
|
-
# method in SQLAlchemy due to the cache decorator
|
575
|
-
# not being present
|
576
|
-
pass
|
577
|
-
else:
|
578
|
-
conn_uniques = [ # type:ignore[assignment]
|
579
|
-
uq
|
580
|
-
for uq in conn_uniques
|
581
|
-
if autogen_context.run_name_filters(
|
582
|
-
uq["name"],
|
583
|
-
"unique_constraint",
|
584
|
-
{"table_name": tname, "schema_name": schema},
|
585
|
-
)
|
586
|
-
]
|
587
|
-
for uq in conn_uniques:
|
588
|
-
if uq.get("duplicates_index"):
|
589
|
-
unique_constraints_duplicate_unique_indexes = True
|
496
|
+
]
|
497
|
+
for uq in conn_uniques:
|
498
|
+
if uq.get("duplicates_index"):
|
499
|
+
unique_constraints_duplicate_unique_indexes = True
|
590
500
|
try:
|
591
501
|
conn_indexes = inspector.get_indexes( # type:ignore[assignment]
|
592
502
|
tname, schema=schema
|
@@ -639,7 +549,7 @@ def _compare_indexes_and_uniques(
|
|
639
549
|
# 3. give the dialect a chance to omit indexes and constraints that
|
640
550
|
# we know are either added implicitly by the DB or that the DB
|
641
551
|
# can't accurately report on
|
642
|
-
|
552
|
+
impl.correct_for_autogen_constraints(
|
643
553
|
conn_uniques, # type: ignore[arg-type]
|
644
554
|
conn_indexes, # type: ignore[arg-type]
|
645
555
|
metadata_unique_constraints,
|
@@ -651,31 +561,31 @@ def _compare_indexes_and_uniques(
|
|
651
561
|
# Index and UniqueConstraint so we can easily work with them
|
652
562
|
# interchangeably
|
653
563
|
metadata_unique_constraints_sig = {
|
654
|
-
|
564
|
+
impl._create_metadata_constraint_sig(uq)
|
565
|
+
for uq in metadata_unique_constraints
|
655
566
|
}
|
656
567
|
|
657
568
|
metadata_indexes_sig = {
|
658
|
-
|
569
|
+
impl._create_metadata_constraint_sig(ix) for ix in metadata_indexes
|
659
570
|
}
|
660
571
|
|
661
572
|
conn_unique_constraints = {
|
662
|
-
|
573
|
+
impl._create_reflected_constraint_sig(uq) for uq in conn_uniques
|
663
574
|
}
|
664
575
|
|
665
|
-
conn_indexes_sig = {
|
576
|
+
conn_indexes_sig = {
|
577
|
+
impl._create_reflected_constraint_sig(ix) for ix in conn_indexes
|
578
|
+
}
|
666
579
|
|
667
580
|
# 5. index things by name, for those objects that have names
|
668
581
|
metadata_names = {
|
669
582
|
cast(str, c.md_name_to_sql_name(autogen_context)): c
|
670
|
-
for c in metadata_unique_constraints_sig.union(
|
671
|
-
|
672
|
-
)
|
673
|
-
if isinstance(c, _ix_constraint_sig)
|
674
|
-
or sqla_compat._constraint_is_named(c.const, autogen_context.dialect)
|
583
|
+
for c in metadata_unique_constraints_sig.union(metadata_indexes_sig)
|
584
|
+
if c.is_named
|
675
585
|
}
|
676
586
|
|
677
|
-
conn_uniques_by_name: Dict[sqla_compat._ConstraintName,
|
678
|
-
conn_indexes_by_name: Dict[sqla_compat._ConstraintName,
|
587
|
+
conn_uniques_by_name: Dict[sqla_compat._ConstraintName, _constraint_sig]
|
588
|
+
conn_indexes_by_name: Dict[sqla_compat._ConstraintName, _constraint_sig]
|
679
589
|
|
680
590
|
conn_uniques_by_name = {c.name: c for c in conn_unique_constraints}
|
681
591
|
conn_indexes_by_name = {c.name: c for c in conn_indexes_sig}
|
@@ -694,13 +604,12 @@ def _compare_indexes_and_uniques(
|
|
694
604
|
|
695
605
|
# 6. index things by "column signature", to help with unnamed unique
|
696
606
|
# constraints.
|
697
|
-
conn_uniques_by_sig = {uq.
|
607
|
+
conn_uniques_by_sig = {uq.unnamed: uq for uq in conn_unique_constraints}
|
698
608
|
metadata_uniques_by_sig = {
|
699
|
-
uq.
|
609
|
+
uq.unnamed: uq for uq in metadata_unique_constraints_sig
|
700
610
|
}
|
701
|
-
metadata_indexes_by_sig = {ix.sig: ix for ix in metadata_indexes_sig}
|
702
611
|
unnamed_metadata_uniques = {
|
703
|
-
uq.
|
612
|
+
uq.unnamed: uq
|
704
613
|
for uq in metadata_unique_constraints_sig
|
705
614
|
if not sqla_compat._constraint_is_named(
|
706
615
|
uq.const, autogen_context.dialect
|
@@ -715,18 +624,18 @@ def _compare_indexes_and_uniques(
|
|
715
624
|
# 4. The backend may double up indexes as unique constraints and
|
716
625
|
# vice versa (e.g. MySQL, Postgresql)
|
717
626
|
|
718
|
-
def obj_added(obj):
|
719
|
-
if obj
|
627
|
+
def obj_added(obj: _constraint_sig):
|
628
|
+
if is_index_sig(obj):
|
720
629
|
if autogen_context.run_object_filters(
|
721
630
|
obj.const, obj.name, "index", False, None
|
722
631
|
):
|
723
632
|
modify_ops.ops.append(ops.CreateIndexOp.from_index(obj.const))
|
724
633
|
log.info(
|
725
|
-
"Detected added index '%
|
634
|
+
"Detected added index '%r' on '%s'",
|
726
635
|
obj.name,
|
727
|
-
|
636
|
+
obj.column_names,
|
728
637
|
)
|
729
|
-
|
638
|
+
elif is_uq_sig(obj):
|
730
639
|
if not supports_unique_constraints:
|
731
640
|
# can't report unique indexes as added if we don't
|
732
641
|
# detect them
|
@@ -741,13 +650,15 @@ def _compare_indexes_and_uniques(
|
|
741
650
|
ops.AddConstraintOp.from_constraint(obj.const)
|
742
651
|
)
|
743
652
|
log.info(
|
744
|
-
"Detected added unique constraint
|
653
|
+
"Detected added unique constraint %r on '%s'",
|
745
654
|
obj.name,
|
746
|
-
|
655
|
+
obj.column_names,
|
747
656
|
)
|
657
|
+
else:
|
658
|
+
assert False
|
748
659
|
|
749
|
-
def obj_removed(obj):
|
750
|
-
if obj
|
660
|
+
def obj_removed(obj: _constraint_sig):
|
661
|
+
if is_index_sig(obj):
|
751
662
|
if obj.is_unique and not supports_unique_constraints:
|
752
663
|
# many databases double up unique constraints
|
753
664
|
# as unique indexes. without that list we can't
|
@@ -758,10 +669,8 @@ def _compare_indexes_and_uniques(
|
|
758
669
|
obj.const, obj.name, "index", True, None
|
759
670
|
):
|
760
671
|
modify_ops.ops.append(ops.DropIndexOp.from_index(obj.const))
|
761
|
-
log.info(
|
762
|
-
|
763
|
-
)
|
764
|
-
else:
|
672
|
+
log.info("Detected removed index %r on %r", obj.name, tname)
|
673
|
+
elif is_uq_sig(obj):
|
765
674
|
if is_create_table or is_drop_table:
|
766
675
|
# if the whole table is being dropped, we don't need to
|
767
676
|
# consider unique constraint separately
|
@@ -773,33 +682,40 @@ def _compare_indexes_and_uniques(
|
|
773
682
|
ops.DropConstraintOp.from_constraint(obj.const)
|
774
683
|
)
|
775
684
|
log.info(
|
776
|
-
"Detected removed unique constraint
|
685
|
+
"Detected removed unique constraint %r on %r",
|
777
686
|
obj.name,
|
778
687
|
tname,
|
779
688
|
)
|
689
|
+
else:
|
690
|
+
assert False
|
691
|
+
|
692
|
+
def obj_changed(
|
693
|
+
old: _constraint_sig,
|
694
|
+
new: _constraint_sig,
|
695
|
+
msg: str,
|
696
|
+
):
|
697
|
+
if is_index_sig(old):
|
698
|
+
assert is_index_sig(new)
|
780
699
|
|
781
|
-
def obj_changed(old, new, msg):
|
782
|
-
if old.is_index:
|
783
700
|
if autogen_context.run_object_filters(
|
784
701
|
new.const, new.name, "index", False, old.const
|
785
702
|
):
|
786
703
|
log.info(
|
787
|
-
"Detected changed index
|
788
|
-
old.name,
|
789
|
-
tname,
|
790
|
-
", ".join(msg),
|
704
|
+
"Detected changed index %r on %r: %s", old.name, tname, msg
|
791
705
|
)
|
792
706
|
modify_ops.ops.append(ops.DropIndexOp.from_index(old.const))
|
793
707
|
modify_ops.ops.append(ops.CreateIndexOp.from_index(new.const))
|
794
|
-
|
708
|
+
elif is_uq_sig(old):
|
709
|
+
assert is_uq_sig(new)
|
710
|
+
|
795
711
|
if autogen_context.run_object_filters(
|
796
712
|
new.const, new.name, "unique_constraint", False, old.const
|
797
713
|
):
|
798
714
|
log.info(
|
799
|
-
"Detected changed unique constraint
|
715
|
+
"Detected changed unique constraint %r on %r: %s",
|
800
716
|
old.name,
|
801
717
|
tname,
|
802
|
-
|
718
|
+
msg,
|
803
719
|
)
|
804
720
|
modify_ops.ops.append(
|
805
721
|
ops.DropConstraintOp.from_constraint(old.const)
|
@@ -807,18 +723,24 @@ def _compare_indexes_and_uniques(
|
|
807
723
|
modify_ops.ops.append(
|
808
724
|
ops.AddConstraintOp.from_constraint(new.const)
|
809
725
|
)
|
726
|
+
else:
|
727
|
+
assert False
|
810
728
|
|
811
729
|
for removed_name in sorted(set(conn_names).difference(metadata_names)):
|
812
|
-
conn_obj
|
813
|
-
|
814
|
-
|
815
|
-
|
730
|
+
conn_obj = conn_names[removed_name]
|
731
|
+
if (
|
732
|
+
is_uq_sig(conn_obj)
|
733
|
+
and conn_obj.unnamed in unnamed_metadata_uniques
|
734
|
+
):
|
816
735
|
continue
|
817
736
|
elif removed_name in doubled_constraints:
|
818
737
|
conn_uq, conn_idx = doubled_constraints[removed_name]
|
819
738
|
if (
|
820
|
-
|
821
|
-
|
739
|
+
all(
|
740
|
+
conn_idx.unnamed != meta_idx.unnamed
|
741
|
+
for meta_idx in metadata_indexes_sig
|
742
|
+
)
|
743
|
+
and conn_uq.unnamed not in metadata_uniques_by_sig
|
822
744
|
):
|
823
745
|
obj_removed(conn_uq)
|
824
746
|
obj_removed(conn_idx)
|
@@ -830,30 +752,36 @@ def _compare_indexes_and_uniques(
|
|
830
752
|
|
831
753
|
if existing_name in doubled_constraints:
|
832
754
|
conn_uq, conn_idx = doubled_constraints[existing_name]
|
833
|
-
if metadata_obj
|
755
|
+
if is_index_sig(metadata_obj):
|
834
756
|
conn_obj = conn_idx
|
835
757
|
else:
|
836
758
|
conn_obj = conn_uq
|
837
759
|
else:
|
838
760
|
conn_obj = conn_names[existing_name]
|
839
761
|
|
840
|
-
if conn_obj
|
762
|
+
if type(conn_obj) != type(metadata_obj):
|
841
763
|
obj_removed(conn_obj)
|
842
764
|
obj_added(metadata_obj)
|
843
765
|
else:
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
766
|
+
comparison = metadata_obj.compare_to_reflected(conn_obj)
|
767
|
+
|
768
|
+
if comparison.is_different:
|
769
|
+
# constraint are different
|
770
|
+
obj_changed(conn_obj, metadata_obj, comparison.message)
|
771
|
+
elif comparison.is_skip:
|
772
|
+
# constraint cannot be compared, skip them
|
773
|
+
thing = (
|
774
|
+
"index" if is_index_sig(conn_obj) else "unique constraint"
|
849
775
|
)
|
850
|
-
|
851
|
-
|
852
|
-
|
776
|
+
log.info(
|
777
|
+
"Cannot compare %s %r, assuming equal and skipping. %s",
|
778
|
+
thing,
|
779
|
+
conn_obj.name,
|
780
|
+
comparison.message,
|
853
781
|
)
|
854
|
-
|
855
|
-
|
856
|
-
|
782
|
+
else:
|
783
|
+
# constraint are equal
|
784
|
+
assert comparison.is_equal
|
857
785
|
|
858
786
|
for added_name in sorted(set(metadata_names).difference(conn_names)):
|
859
787
|
obj = metadata_names[added_name]
|
@@ -893,7 +821,7 @@ def _correct_for_uq_duplicates_uix(
|
|
893
821
|
}
|
894
822
|
|
895
823
|
unnamed_metadata_uqs = {
|
896
|
-
|
824
|
+
impl._create_metadata_constraint_sig(cons).unnamed
|
897
825
|
for name, cons in metadata_cons_names
|
898
826
|
if name is None
|
899
827
|
}
|
@@ -917,7 +845,9 @@ def _correct_for_uq_duplicates_uix(
|
|
917
845
|
for overlap in uqs_dupe_indexes:
|
918
846
|
if overlap not in metadata_uq_names:
|
919
847
|
if (
|
920
|
-
|
848
|
+
impl._create_reflected_constraint_sig(
|
849
|
+
uqs_dupe_indexes[overlap]
|
850
|
+
).unnamed
|
921
851
|
not in unnamed_metadata_uqs
|
922
852
|
):
|
923
853
|
conn_unique_constraints.discard(uqs_dupe_indexes[overlap])
|
@@ -1243,8 +1173,8 @@ def _compare_foreign_keys(
|
|
1243
1173
|
modify_table_ops: ModifyTableOps,
|
1244
1174
|
schema: Optional[str],
|
1245
1175
|
tname: Union[quoted_name, str],
|
1246
|
-
conn_table:
|
1247
|
-
metadata_table:
|
1176
|
+
conn_table: Table,
|
1177
|
+
metadata_table: Table,
|
1248
1178
|
) -> None:
|
1249
1179
|
# if we're doing CREATE TABLE, all FKs are created
|
1250
1180
|
# inline within the table def
|
@@ -1268,15 +1198,13 @@ def _compare_foreign_keys(
|
|
1268
1198
|
)
|
1269
1199
|
]
|
1270
1200
|
|
1271
|
-
backend_reflects_fk_options = bool(
|
1272
|
-
conn_fks_list and "options" in conn_fks_list[0]
|
1273
|
-
)
|
1274
|
-
|
1275
1201
|
conn_fks = {
|
1276
1202
|
_make_foreign_key(const, conn_table) # type: ignore[arg-type]
|
1277
1203
|
for const in conn_fks_list
|
1278
1204
|
}
|
1279
1205
|
|
1206
|
+
impl = autogen_context.migration_context.impl
|
1207
|
+
|
1280
1208
|
# give the dialect a chance to correct the FKs to match more
|
1281
1209
|
# closely
|
1282
1210
|
autogen_context.migration_context.impl.correct_for_autogen_foreignkeys(
|
@@ -1284,17 +1212,24 @@ def _compare_foreign_keys(
|
|
1284
1212
|
)
|
1285
1213
|
|
1286
1214
|
metadata_fks_sig = {
|
1287
|
-
|
1288
|
-
for fk in metadata_fks
|
1215
|
+
impl._create_metadata_constraint_sig(fk) for fk in metadata_fks
|
1289
1216
|
}
|
1290
1217
|
|
1291
1218
|
conn_fks_sig = {
|
1292
|
-
|
1293
|
-
for fk in conn_fks
|
1219
|
+
impl._create_reflected_constraint_sig(fk) for fk in conn_fks
|
1294
1220
|
}
|
1295
1221
|
|
1296
|
-
|
1297
|
-
|
1222
|
+
# check if reflected FKs include options, indicating the backend
|
1223
|
+
# can reflect FK options
|
1224
|
+
if conn_fks_list and "options" in conn_fks_list[0]:
|
1225
|
+
conn_fks_by_sig = {c.unnamed: c for c in conn_fks_sig}
|
1226
|
+
metadata_fks_by_sig = {c.unnamed: c for c in metadata_fks_sig}
|
1227
|
+
else:
|
1228
|
+
# otherwise compare by sig without options added
|
1229
|
+
conn_fks_by_sig = {c.unnamed_no_options: c for c in conn_fks_sig}
|
1230
|
+
metadata_fks_by_sig = {
|
1231
|
+
c.unnamed_no_options: c for c in metadata_fks_sig
|
1232
|
+
}
|
1298
1233
|
|
1299
1234
|
metadata_fks_by_name = {
|
1300
1235
|
c.name: c for c in metadata_fks_sig if c.name is not None
|
@@ -1306,7 +1241,7 @@ def _compare_foreign_keys(
|
|
1306
1241
|
obj.const, obj.name, "foreign_key_constraint", False, compare_to
|
1307
1242
|
):
|
1308
1243
|
modify_table_ops.ops.append(
|
1309
|
-
ops.CreateForeignKeyOp.from_constraint(const.const)
|
1244
|
+
ops.CreateForeignKeyOp.from_constraint(const.const) # type: ignore[has-type] # noqa: E501
|
1310
1245
|
)
|
1311
1246
|
|
1312
1247
|
log.info(
|