From: Mike Bayer Date: Tue, 23 Jan 2024 19:06:01 +0000 (-0500) Subject: suffix index names with "_history" just like tables X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=dab1da6049d210843c16d96b20ae0efc063eead3;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git suffix index names with "_history" just like tables Fixed regression in history_meta example where the use of :meth:`_schema.MetaData.to_metadata` to make a copy of the history table would also copy indexes (which is a good thing), but causing naming conflicts indexes regardless of naming scheme used for those indexes. A "_history" suffix is now added to these indexes in the same way as is achieved for the table name. Fixes: #10920 Change-Id: I78823650956ff979d500bedbdbce261048894ce9 --- diff --git a/doc/build/changelog/unreleased_20/10920.rst b/doc/build/changelog/unreleased_20/10920.rst new file mode 100644 index 0000000000..e7bc7b8acd --- /dev/null +++ b/doc/build/changelog/unreleased_20/10920.rst @@ -0,0 +1,11 @@ +.. change:: + :tags: bug, examples + :tickets: 10920 + + Fixed regression in history_meta example where the use of + :meth:`_schema.MetaData.to_metadata` to make a copy of the history table + would also copy indexes (which is a good thing), but causing naming + conflicts indexes regardless of naming scheme used for those indexes. A + "_history" suffix is now added to these indexes in the same way as is + achieved for the table name. + diff --git a/examples/versioned_history/history_meta.py b/examples/versioned_history/history_meta.py index 806267cb41..3f26832b9e 100644 --- a/examples/versioned_history/history_meta.py +++ b/examples/versioned_history/history_meta.py @@ -56,6 +56,9 @@ def _history_mapper(local_mapper): local_mapper.local_table.metadata, name=local_mapper.local_table.name + "_history", ) + for idx in history_table.indexes: + if idx.name is not None: + idx.name += "_history" for orig_c, history_c in zip( local_mapper.local_table.c, history_table.c diff --git a/examples/versioned_history/test_versioning.py b/examples/versioned_history/test_versioning.py index 7b9c82c60f..ac122581a4 100644 --- a/examples/versioned_history/test_versioning.py +++ b/examples/versioned_history/test_versioning.py @@ -8,11 +8,15 @@ from sqlalchemy import Boolean from sqlalchemy import Column from sqlalchemy import create_engine from sqlalchemy import ForeignKey +from sqlalchemy import ForeignKeyConstraint +from sqlalchemy import Index from sqlalchemy import inspect from sqlalchemy import Integer from sqlalchemy import join from sqlalchemy import select from sqlalchemy import String +from sqlalchemy import testing +from sqlalchemy import UniqueConstraint from sqlalchemy.orm import clear_mappers from sqlalchemy.orm import column_property from sqlalchemy.orm import declarative_base @@ -31,7 +35,6 @@ from sqlalchemy.testing.entities import ComparableEntity from .history_meta import Versioned from .history_meta import versioned_session - warnings.simplefilter("error") @@ -127,6 +130,98 @@ class TestVersioning(AssertsCompiledSQL): ], ) + @testing.variation( + "constraint_type", + [ + "index_single_col", + "composite_index", + "explicit_name_index", + "unique_constraint", + "unique_constraint_naming_conv", + "unique_constraint_explicit_name", + "fk_constraint", + "fk_constraint_naming_conv", + "fk_constraint_explicit_name", + ], + ) + def test_index_naming(self, constraint_type): + """test #10920""" + + if ( + constraint_type.unique_constraint_naming_conv + or constraint_type.fk_constraint_naming_conv + ): + self.Base.metadata.naming_convention = { + "ix": "ix_%(column_0_label)s", + "uq": "uq_%(table_name)s_%(column_0_name)s", + "fk": ( + "fk_%(table_name)s_%(column_0_name)s" + "_%(referred_table_name)s" + ), + } + + if ( + constraint_type.fk_constraint + or constraint_type.fk_constraint_naming_conv + or constraint_type.fk_constraint_explicit_name + ): + + class Related(self.Base): + __tablename__ = "related" + + id = Column(Integer, primary_key=True) + + class SomeClass(Versioned, self.Base): + __tablename__ = "sometable" + + id = Column(Integer, primary_key=True) + x = Column(Integer) + y = Column(Integer) + + # Index objects are copied and these have to have a new name + if constraint_type.index_single_col: + __table_args__ = ( + Index( + None, + x, + ), + ) + elif constraint_type.composite_index: + __table_args__ = (Index(None, x, y),) + elif constraint_type.explicit_name_index: + __table_args__ = (Index("my_index", x, y),) + # unique constraint objects are discarded. + elif ( + constraint_type.unique_constraint + or constraint_type.unique_constraint_naming_conv + ): + __table_args__ = (UniqueConstraint(x, y),) + elif constraint_type.unique_constraint_explicit_name: + __table_args__ = (UniqueConstraint(x, y, name="my_uq"),) + # foreign key constraint objects are copied and have the same + # name, but no database in Core has any problem with this as the + # names are local to the parent table. + elif ( + constraint_type.fk_constraint + or constraint_type.fk_constraint_naming_conv + ): + __table_args__ = (ForeignKeyConstraint([x], [Related.id]),) + elif constraint_type.fk_constraint_explicit_name: + __table_args__ = ( + ForeignKeyConstraint([x], [Related.id], name="my_fk"), + ) + else: + constraint_type.fail() + + eq_( + set(idx.name + "_history" for idx in SomeClass.__table__.indexes), + set( + idx.name + for idx in SomeClass.__history_mapper__.local_table.indexes + ), + ) + self.create_tables() + def test_discussion_9546(self): class ThingExternal(Versioned, self.Base): __tablename__ = "things_external"