alembic 1.12.0__py3-none-any.whl → 1.13.0__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/api.py +14 -6
- alembic/autogenerate/compare.py +129 -195
- alembic/autogenerate/render.py +42 -32
- alembic/autogenerate/rewriter.py +19 -19
- alembic/command.py +11 -9
- alembic/config.py +1 -1
- alembic/context.pyi +12 -5
- alembic/ddl/_autogen.py +323 -0
- alembic/ddl/impl.py +167 -41
- alembic/ddl/mssql.py +1 -4
- alembic/ddl/mysql.py +4 -3
- alembic/ddl/postgresql.py +157 -70
- alembic/op.pyi +9 -11
- alembic/operations/__init__.py +2 -0
- alembic/operations/base.py +10 -11
- alembic/operations/ops.py +14 -14
- alembic/operations/toimpl.py +5 -5
- alembic/runtime/environment.py +7 -5
- alembic/runtime/migration.py +4 -4
- alembic/script/base.py +25 -17
- alembic/script/revision.py +30 -25
- 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 -4
- alembic/testing/schemacompare.py +9 -0
- alembic/testing/suite/test_autogen_identity.py +23 -38
- alembic/util/compat.py +0 -1
- alembic/util/sqla_compat.py +58 -30
- {alembic-1.12.0.dist-info → alembic-1.13.0.dist-info}/METADATA +8 -6
- {alembic-1.12.0.dist-info → alembic-1.13.0.dist-info}/RECORD +36 -35
- {alembic-1.12.0.dist-info → alembic-1.13.0.dist-info}/WHEEL +1 -1
- {alembic-1.12.0.dist-info → alembic-1.13.0.dist-info}/LICENSE +0 -0
- {alembic-1.12.0.dist-info → alembic-1.13.0.dist-info}/entry_points.txt +0 -0
- {alembic-1.12.0.dist-info → alembic-1.13.0.dist-info}/top_level.txt +0 -0
alembic/__init__.py
CHANGED
alembic/autogenerate/api.py
CHANGED
@@ -2,9 +2,9 @@ from __future__ import annotations
|
|
2
2
|
|
3
3
|
import contextlib
|
4
4
|
from typing import Any
|
5
|
-
from typing import Callable
|
6
5
|
from typing import Dict
|
7
6
|
from typing import Iterator
|
7
|
+
from typing import List
|
8
8
|
from typing import Optional
|
9
9
|
from typing import Sequence
|
10
10
|
from typing import Set
|
@@ -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."""
|
@@ -34,10 +35,12 @@ if TYPE_CHECKING:
|
|
34
35
|
from ..operations.ops import UpgradeOps
|
35
36
|
from ..runtime.environment import NameFilterParentNames
|
36
37
|
from ..runtime.environment import NameFilterType
|
38
|
+
from ..runtime.environment import ProcessRevisionDirectiveFn
|
37
39
|
from ..runtime.environment import RenderItemFn
|
38
40
|
from ..runtime.migration import MigrationContext
|
39
41
|
from ..script.base import Script
|
40
42
|
from ..script.base import ScriptDirectory
|
43
|
+
from ..script.revision import _GetRevArg
|
41
44
|
|
42
45
|
|
43
46
|
def compare_metadata(context: MigrationContext, metadata: MetaData) -> Any:
|
@@ -438,7 +441,7 @@ class AutogenContext:
|
|
438
441
|
def run_object_filters(
|
439
442
|
self,
|
440
443
|
object_: SchemaItem,
|
441
|
-
name:
|
444
|
+
name: sqla_compat._ConstraintName,
|
442
445
|
type_: NameFilterType,
|
443
446
|
reflected: bool,
|
444
447
|
compare_to: Optional[SchemaItem],
|
@@ -507,12 +510,17 @@ class RevisionContext:
|
|
507
510
|
"""Maintains configuration and state that's specific to a revision
|
508
511
|
file generation operation."""
|
509
512
|
|
513
|
+
generated_revisions: List[MigrationScript]
|
514
|
+
process_revision_directives: Optional[ProcessRevisionDirectiveFn]
|
515
|
+
|
510
516
|
def __init__(
|
511
517
|
self,
|
512
518
|
config: Config,
|
513
519
|
script_directory: ScriptDirectory,
|
514
520
|
command_args: Dict[str, Any],
|
515
|
-
process_revision_directives: Optional[
|
521
|
+
process_revision_directives: Optional[
|
522
|
+
ProcessRevisionDirectiveFn
|
523
|
+
] = None,
|
516
524
|
) -> None:
|
517
525
|
self.config = config
|
518
526
|
self.script_directory = script_directory
|
@@ -555,18 +563,18 @@ class RevisionContext:
|
|
555
563
|
)
|
556
564
|
|
557
565
|
def run_autogenerate(
|
558
|
-
self, rev:
|
566
|
+
self, rev: _GetRevArg, migration_context: MigrationContext
|
559
567
|
) -> None:
|
560
568
|
self._run_environment(rev, migration_context, True)
|
561
569
|
|
562
570
|
def run_no_autogenerate(
|
563
|
-
self, rev:
|
571
|
+
self, rev: _GetRevArg, migration_context: MigrationContext
|
564
572
|
) -> None:
|
565
573
|
self._run_environment(rev, migration_context, False)
|
566
574
|
|
567
575
|
def _run_environment(
|
568
576
|
self,
|
569
|
-
rev:
|
577
|
+
rev: _GetRevArg,
|
570
578
|
migration_context: MigrationContext,
|
571
579
|
autogenerate: bool,
|
572
580
|
) -> None:
|
alembic/autogenerate/compare.py
CHANGED
@@ -7,12 +7,12 @@ from typing import Any
|
|
7
7
|
from typing import cast
|
8
8
|
from typing import Dict
|
9
9
|
from typing import Iterator
|
10
|
-
from typing import List
|
11
10
|
from typing import Mapping
|
12
11
|
from typing import Optional
|
13
12
|
from typing import Set
|
14
13
|
from typing import Tuple
|
15
14
|
from typing import TYPE_CHECKING
|
15
|
+
from typing import TypeVar
|
16
16
|
from typing import Union
|
17
17
|
|
18
18
|
from sqlalchemy import event
|
@@ -21,10 +21,14 @@ from sqlalchemy import schema as sa_schema
|
|
21
21
|
from sqlalchemy import text
|
22
22
|
from sqlalchemy import types as sqltypes
|
23
23
|
from sqlalchemy.sql import expression
|
24
|
+
from sqlalchemy.sql.schema import ForeignKeyConstraint
|
25
|
+
from sqlalchemy.sql.schema import Index
|
26
|
+
from sqlalchemy.sql.schema import UniqueConstraint
|
24
27
|
from sqlalchemy.util import OrderedSet
|
25
28
|
|
26
|
-
from alembic.ddl.base import _fk_spec
|
27
29
|
from .. import util
|
30
|
+
from ..ddl._autogen import is_index_sig
|
31
|
+
from ..ddl._autogen import is_uq_sig
|
28
32
|
from ..operations import ops
|
29
33
|
from ..util import sqla_compat
|
30
34
|
|
@@ -35,10 +39,7 @@ if TYPE_CHECKING:
|
|
35
39
|
from sqlalchemy.sql.elements import quoted_name
|
36
40
|
from sqlalchemy.sql.elements import TextClause
|
37
41
|
from sqlalchemy.sql.schema import Column
|
38
|
-
from sqlalchemy.sql.schema import ForeignKeyConstraint
|
39
|
-
from sqlalchemy.sql.schema import Index
|
40
42
|
from sqlalchemy.sql.schema import Table
|
41
|
-
from sqlalchemy.sql.schema import UniqueConstraint
|
42
43
|
|
43
44
|
from alembic.autogenerate.api import AutogenContext
|
44
45
|
from alembic.ddl.impl import DefaultImpl
|
@@ -46,6 +47,8 @@ if TYPE_CHECKING:
|
|
46
47
|
from alembic.operations.ops import MigrationScript
|
47
48
|
from alembic.operations.ops import ModifyTableOps
|
48
49
|
from alembic.operations.ops import UpgradeOps
|
50
|
+
from ..ddl._autogen import _constraint_sig
|
51
|
+
|
49
52
|
|
50
53
|
log = logging.getLogger(__name__)
|
51
54
|
|
@@ -429,102 +432,7 @@ def _compare_columns(
|
|
429
432
|
log.info("Detected removed column '%s.%s'", name, cname)
|
430
433
|
|
431
434
|
|
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
|
-
)
|
435
|
+
_C = TypeVar("_C", bound=Union[UniqueConstraint, ForeignKeyConstraint, Index])
|
528
436
|
|
529
437
|
|
530
438
|
@comparators.dispatch_for("table")
|
@@ -561,32 +469,31 @@ def _compare_indexes_and_uniques(
|
|
561
469
|
|
562
470
|
if conn_table is not None:
|
563
471
|
# 1b. ... and from connection, if the table exists
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
472
|
+
try:
|
473
|
+
conn_uniques = inspector.get_unique_constraints( # type:ignore[assignment] # noqa
|
474
|
+
tname, schema=schema
|
475
|
+
)
|
476
|
+
supports_unique_constraints = True
|
477
|
+
except NotImplementedError:
|
478
|
+
pass
|
479
|
+
except TypeError:
|
480
|
+
# number of arguments is off for the base
|
481
|
+
# method in SQLAlchemy due to the cache decorator
|
482
|
+
# not being present
|
483
|
+
pass
|
484
|
+
else:
|
485
|
+
conn_uniques = [ # type:ignore[assignment]
|
486
|
+
uq
|
487
|
+
for uq in conn_uniques
|
488
|
+
if autogen_context.run_name_filters(
|
489
|
+
uq["name"],
|
490
|
+
"unique_constraint",
|
491
|
+
{"table_name": tname, "schema_name": schema},
|
568
492
|
)
|
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
|
493
|
+
]
|
494
|
+
for uq in conn_uniques:
|
495
|
+
if uq.get("duplicates_index"):
|
496
|
+
unique_constraints_duplicate_unique_indexes = True
|
590
497
|
try:
|
591
498
|
conn_indexes = inspector.get_indexes( # type:ignore[assignment]
|
592
499
|
tname, schema=schema
|
@@ -639,7 +546,7 @@ def _compare_indexes_and_uniques(
|
|
639
546
|
# 3. give the dialect a chance to omit indexes and constraints that
|
640
547
|
# we know are either added implicitly by the DB or that the DB
|
641
548
|
# can't accurately report on
|
642
|
-
|
549
|
+
impl.correct_for_autogen_constraints(
|
643
550
|
conn_uniques, # type: ignore[arg-type]
|
644
551
|
conn_indexes, # type: ignore[arg-type]
|
645
552
|
metadata_unique_constraints,
|
@@ -651,18 +558,21 @@ def _compare_indexes_and_uniques(
|
|
651
558
|
# Index and UniqueConstraint so we can easily work with them
|
652
559
|
# interchangeably
|
653
560
|
metadata_unique_constraints_sig = {
|
654
|
-
|
561
|
+
impl._create_metadata_constraint_sig(uq)
|
562
|
+
for uq in metadata_unique_constraints
|
655
563
|
}
|
656
564
|
|
657
565
|
metadata_indexes_sig = {
|
658
|
-
|
566
|
+
impl._create_metadata_constraint_sig(ix) for ix in metadata_indexes
|
659
567
|
}
|
660
568
|
|
661
569
|
conn_unique_constraints = {
|
662
|
-
|
570
|
+
impl._create_reflected_constraint_sig(uq) for uq in conn_uniques
|
663
571
|
}
|
664
572
|
|
665
|
-
conn_indexes_sig = {
|
573
|
+
conn_indexes_sig = {
|
574
|
+
impl._create_reflected_constraint_sig(ix) for ix in conn_indexes
|
575
|
+
}
|
666
576
|
|
667
577
|
# 5. index things by name, for those objects that have names
|
668
578
|
metadata_names = {
|
@@ -670,12 +580,11 @@ def _compare_indexes_and_uniques(
|
|
670
580
|
for c in metadata_unique_constraints_sig.union(
|
671
581
|
metadata_indexes_sig # type:ignore[arg-type]
|
672
582
|
)
|
673
|
-
if
|
674
|
-
or sqla_compat._constraint_is_named(c.const, autogen_context.dialect)
|
583
|
+
if c.is_named
|
675
584
|
}
|
676
585
|
|
677
|
-
conn_uniques_by_name: Dict[sqla_compat._ConstraintName,
|
678
|
-
conn_indexes_by_name: Dict[sqla_compat._ConstraintName,
|
586
|
+
conn_uniques_by_name: Dict[sqla_compat._ConstraintName, _constraint_sig]
|
587
|
+
conn_indexes_by_name: Dict[sqla_compat._ConstraintName, _constraint_sig]
|
679
588
|
|
680
589
|
conn_uniques_by_name = {c.name: c for c in conn_unique_constraints}
|
681
590
|
conn_indexes_by_name = {c.name: c for c in conn_indexes_sig}
|
@@ -694,13 +603,12 @@ def _compare_indexes_and_uniques(
|
|
694
603
|
|
695
604
|
# 6. index things by "column signature", to help with unnamed unique
|
696
605
|
# constraints.
|
697
|
-
conn_uniques_by_sig = {uq.
|
606
|
+
conn_uniques_by_sig = {uq.unnamed: uq for uq in conn_unique_constraints}
|
698
607
|
metadata_uniques_by_sig = {
|
699
|
-
uq.
|
608
|
+
uq.unnamed: uq for uq in metadata_unique_constraints_sig
|
700
609
|
}
|
701
|
-
metadata_indexes_by_sig = {ix.sig: ix for ix in metadata_indexes_sig}
|
702
610
|
unnamed_metadata_uniques = {
|
703
|
-
uq.
|
611
|
+
uq.unnamed: uq
|
704
612
|
for uq in metadata_unique_constraints_sig
|
705
613
|
if not sqla_compat._constraint_is_named(
|
706
614
|
uq.const, autogen_context.dialect
|
@@ -715,18 +623,18 @@ def _compare_indexes_and_uniques(
|
|
715
623
|
# 4. The backend may double up indexes as unique constraints and
|
716
624
|
# vice versa (e.g. MySQL, Postgresql)
|
717
625
|
|
718
|
-
def obj_added(obj):
|
719
|
-
if obj
|
626
|
+
def obj_added(obj: _constraint_sig):
|
627
|
+
if is_index_sig(obj):
|
720
628
|
if autogen_context.run_object_filters(
|
721
629
|
obj.const, obj.name, "index", False, None
|
722
630
|
):
|
723
631
|
modify_ops.ops.append(ops.CreateIndexOp.from_index(obj.const))
|
724
632
|
log.info(
|
725
|
-
"Detected added index '%
|
633
|
+
"Detected added index '%r' on '%s'",
|
726
634
|
obj.name,
|
727
|
-
|
635
|
+
obj.column_names,
|
728
636
|
)
|
729
|
-
|
637
|
+
elif is_uq_sig(obj):
|
730
638
|
if not supports_unique_constraints:
|
731
639
|
# can't report unique indexes as added if we don't
|
732
640
|
# detect them
|
@@ -741,13 +649,15 @@ def _compare_indexes_and_uniques(
|
|
741
649
|
ops.AddConstraintOp.from_constraint(obj.const)
|
742
650
|
)
|
743
651
|
log.info(
|
744
|
-
"Detected added unique constraint
|
652
|
+
"Detected added unique constraint %r on '%s'",
|
745
653
|
obj.name,
|
746
|
-
|
654
|
+
obj.column_names,
|
747
655
|
)
|
656
|
+
else:
|
657
|
+
assert False
|
748
658
|
|
749
|
-
def obj_removed(obj):
|
750
|
-
if obj
|
659
|
+
def obj_removed(obj: _constraint_sig):
|
660
|
+
if is_index_sig(obj):
|
751
661
|
if obj.is_unique and not supports_unique_constraints:
|
752
662
|
# many databases double up unique constraints
|
753
663
|
# as unique indexes. without that list we can't
|
@@ -758,10 +668,8 @@ def _compare_indexes_and_uniques(
|
|
758
668
|
obj.const, obj.name, "index", True, None
|
759
669
|
):
|
760
670
|
modify_ops.ops.append(ops.DropIndexOp.from_index(obj.const))
|
761
|
-
log.info(
|
762
|
-
|
763
|
-
)
|
764
|
-
else:
|
671
|
+
log.info("Detected removed index %r on %r", obj.name, tname)
|
672
|
+
elif is_uq_sig(obj):
|
765
673
|
if is_create_table or is_drop_table:
|
766
674
|
# if the whole table is being dropped, we don't need to
|
767
675
|
# consider unique constraint separately
|
@@ -773,33 +681,40 @@ def _compare_indexes_and_uniques(
|
|
773
681
|
ops.DropConstraintOp.from_constraint(obj.const)
|
774
682
|
)
|
775
683
|
log.info(
|
776
|
-
"Detected removed unique constraint
|
684
|
+
"Detected removed unique constraint %r on %r",
|
777
685
|
obj.name,
|
778
686
|
tname,
|
779
687
|
)
|
688
|
+
else:
|
689
|
+
assert False
|
690
|
+
|
691
|
+
def obj_changed(
|
692
|
+
old: _constraint_sig,
|
693
|
+
new: _constraint_sig,
|
694
|
+
msg: str,
|
695
|
+
):
|
696
|
+
if is_index_sig(old):
|
697
|
+
assert is_index_sig(new)
|
780
698
|
|
781
|
-
def obj_changed(old, new, msg):
|
782
|
-
if old.is_index:
|
783
699
|
if autogen_context.run_object_filters(
|
784
700
|
new.const, new.name, "index", False, old.const
|
785
701
|
):
|
786
702
|
log.info(
|
787
|
-
"Detected changed index
|
788
|
-
old.name,
|
789
|
-
tname,
|
790
|
-
", ".join(msg),
|
703
|
+
"Detected changed index %r on %r: %s", old.name, tname, msg
|
791
704
|
)
|
792
705
|
modify_ops.ops.append(ops.DropIndexOp.from_index(old.const))
|
793
706
|
modify_ops.ops.append(ops.CreateIndexOp.from_index(new.const))
|
794
|
-
|
707
|
+
elif is_uq_sig(old):
|
708
|
+
assert is_uq_sig(new)
|
709
|
+
|
795
710
|
if autogen_context.run_object_filters(
|
796
711
|
new.const, new.name, "unique_constraint", False, old.const
|
797
712
|
):
|
798
713
|
log.info(
|
799
|
-
"Detected changed unique constraint
|
714
|
+
"Detected changed unique constraint %r on %r: %s",
|
800
715
|
old.name,
|
801
716
|
tname,
|
802
|
-
|
717
|
+
msg,
|
803
718
|
)
|
804
719
|
modify_ops.ops.append(
|
805
720
|
ops.DropConstraintOp.from_constraint(old.const)
|
@@ -807,18 +722,24 @@ def _compare_indexes_and_uniques(
|
|
807
722
|
modify_ops.ops.append(
|
808
723
|
ops.AddConstraintOp.from_constraint(new.const)
|
809
724
|
)
|
725
|
+
else:
|
726
|
+
assert False
|
810
727
|
|
811
728
|
for removed_name in sorted(set(conn_names).difference(metadata_names)):
|
812
|
-
conn_obj
|
813
|
-
|
814
|
-
|
815
|
-
|
729
|
+
conn_obj = conn_names[removed_name]
|
730
|
+
if (
|
731
|
+
is_uq_sig(conn_obj)
|
732
|
+
and conn_obj.unnamed in unnamed_metadata_uniques
|
733
|
+
):
|
816
734
|
continue
|
817
735
|
elif removed_name in doubled_constraints:
|
818
736
|
conn_uq, conn_idx = doubled_constraints[removed_name]
|
819
737
|
if (
|
820
|
-
|
821
|
-
|
738
|
+
all(
|
739
|
+
conn_idx.unnamed != meta_idx.unnamed
|
740
|
+
for meta_idx in metadata_indexes_sig
|
741
|
+
)
|
742
|
+
and conn_uq.unnamed not in metadata_uniques_by_sig
|
822
743
|
):
|
823
744
|
obj_removed(conn_uq)
|
824
745
|
obj_removed(conn_idx)
|
@@ -830,30 +751,36 @@ def _compare_indexes_and_uniques(
|
|
830
751
|
|
831
752
|
if existing_name in doubled_constraints:
|
832
753
|
conn_uq, conn_idx = doubled_constraints[existing_name]
|
833
|
-
if metadata_obj
|
754
|
+
if is_index_sig(metadata_obj):
|
834
755
|
conn_obj = conn_idx
|
835
756
|
else:
|
836
757
|
conn_obj = conn_uq
|
837
758
|
else:
|
838
759
|
conn_obj = conn_names[existing_name]
|
839
760
|
|
840
|
-
if conn_obj
|
761
|
+
if type(conn_obj) != type(metadata_obj):
|
841
762
|
obj_removed(conn_obj)
|
842
763
|
obj_added(metadata_obj)
|
843
764
|
else:
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
765
|
+
comparison = metadata_obj.compare_to_reflected(conn_obj)
|
766
|
+
|
767
|
+
if comparison.is_different:
|
768
|
+
# constraint are different
|
769
|
+
obj_changed(conn_obj, metadata_obj, comparison.message)
|
770
|
+
elif comparison.is_skip:
|
771
|
+
# constraint cannot be compared, skip them
|
772
|
+
thing = (
|
773
|
+
"index" if is_index_sig(conn_obj) else "unique constraint"
|
849
774
|
)
|
850
|
-
|
851
|
-
|
852
|
-
|
775
|
+
log.info(
|
776
|
+
"Cannot compare %s %r, assuming equal and skipping. %s",
|
777
|
+
thing,
|
778
|
+
conn_obj.name,
|
779
|
+
comparison.message,
|
853
780
|
)
|
854
|
-
|
855
|
-
|
856
|
-
|
781
|
+
else:
|
782
|
+
# constraint are equal
|
783
|
+
assert comparison.is_equal
|
857
784
|
|
858
785
|
for added_name in sorted(set(metadata_names).difference(conn_names)):
|
859
786
|
obj = metadata_names[added_name]
|
@@ -893,7 +820,7 @@ def _correct_for_uq_duplicates_uix(
|
|
893
820
|
}
|
894
821
|
|
895
822
|
unnamed_metadata_uqs = {
|
896
|
-
|
823
|
+
impl._create_metadata_constraint_sig(cons).unnamed
|
897
824
|
for name, cons in metadata_cons_names
|
898
825
|
if name is None
|
899
826
|
}
|
@@ -917,7 +844,9 @@ def _correct_for_uq_duplicates_uix(
|
|
917
844
|
for overlap in uqs_dupe_indexes:
|
918
845
|
if overlap not in metadata_uq_names:
|
919
846
|
if (
|
920
|
-
|
847
|
+
impl._create_reflected_constraint_sig(
|
848
|
+
uqs_dupe_indexes[overlap]
|
849
|
+
).unnamed
|
921
850
|
not in unnamed_metadata_uqs
|
922
851
|
):
|
923
852
|
conn_unique_constraints.discard(uqs_dupe_indexes[overlap])
|
@@ -1243,8 +1172,8 @@ def _compare_foreign_keys(
|
|
1243
1172
|
modify_table_ops: ModifyTableOps,
|
1244
1173
|
schema: Optional[str],
|
1245
1174
|
tname: Union[quoted_name, str],
|
1246
|
-
conn_table:
|
1247
|
-
metadata_table:
|
1175
|
+
conn_table: Table,
|
1176
|
+
metadata_table: Table,
|
1248
1177
|
) -> None:
|
1249
1178
|
# if we're doing CREATE TABLE, all FKs are created
|
1250
1179
|
# inline within the table def
|
@@ -1268,15 +1197,13 @@ def _compare_foreign_keys(
|
|
1268
1197
|
)
|
1269
1198
|
]
|
1270
1199
|
|
1271
|
-
backend_reflects_fk_options = bool(
|
1272
|
-
conn_fks_list and "options" in conn_fks_list[0]
|
1273
|
-
)
|
1274
|
-
|
1275
1200
|
conn_fks = {
|
1276
1201
|
_make_foreign_key(const, conn_table) # type: ignore[arg-type]
|
1277
1202
|
for const in conn_fks_list
|
1278
1203
|
}
|
1279
1204
|
|
1205
|
+
impl = autogen_context.migration_context.impl
|
1206
|
+
|
1280
1207
|
# give the dialect a chance to correct the FKs to match more
|
1281
1208
|
# closely
|
1282
1209
|
autogen_context.migration_context.impl.correct_for_autogen_foreignkeys(
|
@@ -1284,17 +1211,24 @@ def _compare_foreign_keys(
|
|
1284
1211
|
)
|
1285
1212
|
|
1286
1213
|
metadata_fks_sig = {
|
1287
|
-
|
1288
|
-
for fk in metadata_fks
|
1214
|
+
impl._create_metadata_constraint_sig(fk) for fk in metadata_fks
|
1289
1215
|
}
|
1290
1216
|
|
1291
1217
|
conn_fks_sig = {
|
1292
|
-
|
1293
|
-
for fk in conn_fks
|
1218
|
+
impl._create_reflected_constraint_sig(fk) for fk in conn_fks
|
1294
1219
|
}
|
1295
1220
|
|
1296
|
-
|
1297
|
-
|
1221
|
+
# check if reflected FKs include options, indicating the backend
|
1222
|
+
# can reflect FK options
|
1223
|
+
if conn_fks_list and "options" in conn_fks_list[0]:
|
1224
|
+
conn_fks_by_sig = {c.unnamed: c for c in conn_fks_sig}
|
1225
|
+
metadata_fks_by_sig = {c.unnamed: c for c in metadata_fks_sig}
|
1226
|
+
else:
|
1227
|
+
# otherwise compare by sig without options added
|
1228
|
+
conn_fks_by_sig = {c.unnamed_no_options: c for c in conn_fks_sig}
|
1229
|
+
metadata_fks_by_sig = {
|
1230
|
+
c.unnamed_no_options: c for c in metadata_fks_sig
|
1231
|
+
}
|
1298
1232
|
|
1299
1233
|
metadata_fks_by_name = {
|
1300
1234
|
c.name: c for c in metadata_fks_sig if c.name is not None
|