From: Allen Chen Date: Fri, 3 Oct 2025 02:53:34 +0000 (-0400) Subject: Fix mssql index column order X-Git-Tag: rel_2_0_44~4 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=a2e862ebd09195248c39dc30de02180a9c31e367;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git Fix mssql index column order Fixed issue where the index reflection for SQL Server would not correctly return the order of the column inside an index when the order of the columns in the index did not match the order of the columns in the table. Pull request courtesy of Allen Chen. Fixes: #12894 Closes: #12895 Pull-request: https://github.com/sqlalchemy/sqlalchemy/pull/12895 Pull-request-sha: bd9bd43219f35a29eaeee81fedea452afc64eb5d Change-Id: I45ed30bbd0fcfd4f67cb2b682ecb3a18029be2b7 (cherry picked from commit 3dc9720c365a8d03e7c173874db74a080752d24e) --- diff --git a/doc/build/changelog/unreleased_20/12894.rst b/doc/build/changelog/unreleased_20/12894.rst new file mode 100644 index 0000000000..43cbd74b79 --- /dev/null +++ b/doc/build/changelog/unreleased_20/12894.rst @@ -0,0 +1,9 @@ +.. change:: + :tags: bug, mssql + :tickets: 12894 + + Fixed issue where the index reflection for SQL Server would + not correctly return the order of the column inside an index + when the order of the columns in the index did not match the + order of the columns in the table. + Pull request courtesy of Allen Chen. diff --git a/lib/sqlalchemy/dialects/mssql/base.py b/lib/sqlalchemy/dialects/mssql/base.py index fed1740703..d422ad8c3d 100644 --- a/lib/sqlalchemy/dialects/mssql/base.py +++ b/lib/sqlalchemy/dialects/mssql/base.py @@ -3503,6 +3503,9 @@ join sys.schemas as sch on where tab.name = :tabname and sch.name = :schname +order by + ind_col.index_id, + ind_col.key_ordinal """ ) .bindparams( diff --git a/lib/sqlalchemy/testing/requirements.py b/lib/sqlalchemy/testing/requirements.py index b90e8363af..9719106b33 100644 --- a/lib/sqlalchemy/testing/requirements.py +++ b/lib/sqlalchemy/testing/requirements.py @@ -796,6 +796,11 @@ class SuiteRequirements(Requirements): ASC/DESC but reflects them as expressions (like oracle).""" return exclusions.closed() + @property + def indexes_check_column_order(self): + """target database supports CREATE INDEX with column order check.""" + return exclusions.closed() + @property def indexes_with_expressions(self): """target database supports CREATE INDEX against SQL expressions.""" diff --git a/lib/sqlalchemy/testing/suite/test_reflection.py b/lib/sqlalchemy/testing/suite/test_reflection.py index 12ac5df1c5..b701de126b 100644 --- a/lib/sqlalchemy/testing/suite/test_reflection.py +++ b/lib/sqlalchemy/testing/suite/test_reflection.py @@ -2698,6 +2698,26 @@ class ComponentReflectionTestExtra(ComparesIndexes, fixtures.TestBase): ], ) + @testing.requires.indexes_check_column_order + def test_index_column_order(self, metadata, inspect_for_table): + """test for #12894""" + with inspect_for_table("sa_multi_index") as (schema, inspector): + test_table = Table( + "sa_multi_index", + metadata, + Column("Column1", Integer, primary_key=True), + Column("Column2", Integer), + Column("Column3", Integer), + ) + Index( + "Index_Example", + test_table.c.Column3, + test_table.c.Column1, + test_table.c.Column2, + ) + indexes = inspector.get_indexes("sa_multi_index") + eq_(indexes[0]["column_names"], ["Column3", "Column1", "Column2"]) + @testing.requires.indexes_with_expressions def test_reflect_expression_based_indexes(self, metadata, connection): t = Table( diff --git a/test/dialect/mssql/test_reflection.py b/test/dialect/mssql/test_reflection.py index 733f29fef0..4569a5249f 100644 --- a/test/dialect/mssql/test_reflection.py +++ b/test/dialect/mssql/test_reflection.py @@ -750,6 +750,27 @@ class ReflectionTest(fixtures.TestBase, ComparesTables, AssertsCompiledSQL): CreateIndex(idx), "CREATE NONCLUSTERED INDEX idx_x ON t (x)" ) + def test_index_column_order_clustered(self, metadata, connection): + """test for #12894""" + test_table = Table( + "t", + metadata, + Column("id", Integer, primary_key=True), + Column("x", Integer), + Column("y", Integer), + PrimaryKeyConstraint("id", mssql_clustered=False), + ) + Index( + "idx_x", + test_table.c.y, + test_table.c.id, + test_table.c.x, + mssql_clustered=True, + ) + metadata.create_all(connection) + indexes = testing.db.dialect.get_indexes(connection, "t", None) + eq_(indexes[0]["column_names"], ["y", "id", "x"]) + @testing.only_if("mssql>=12") def test_index_reflection_colstore_clustered(self, metadata, connection): t1 = Table( diff --git a/test/requirements.py b/test/requirements.py index 16d4db465e..56087b6c84 100644 --- a/test/requirements.py +++ b/test/requirements.py @@ -673,6 +673,10 @@ class DefaultRequirements(SuiteRequirements): ] ) + @property + def indexes_check_column_order(self): + return exclusions.open() + @property def indexes_with_expressions(self): return only_on(["postgresql", "sqlite>=3.9.0", "oracle"])