From: Gord Thompson Date: Mon, 20 Dec 2021 21:37:13 +0000 (-0700) Subject: Reflect included columns as dialect_options X-Git-Tag: rel_2_0_0b1~573^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5ada58954afa4fa427f4f7a91c683c1210d3d6df;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git Reflect included columns as dialect_options Fixed reflection of covering indexes to report ``include_columns`` as part of the ``dialect_options`` entry in the reflected index dictionary, thereby enabling round trips from reflection->create to be complete. Included columns continue to also be present under the ``include_columns`` key for backwards compatibility. Fixes: #7382 Change-Id: I4f16b65caed3a36d405481690a3a92432b5efd62 --- diff --git a/doc/build/changelog/unreleased_14/7382.rst b/doc/build/changelog/unreleased_14/7382.rst new file mode 100644 index 0000000000..db6ae45311 --- /dev/null +++ b/doc/build/changelog/unreleased_14/7382.rst @@ -0,0 +1,9 @@ +.. change:: + :tags: bug, reflection, postgresql, mssql + :tickets: 7382 + + Fixed reflection of covering indexes to report ``include_columns`` as part + of the ``dialect_options`` entry in the reflected index dictionary, thereby + enabling round trips from reflection->create to be complete. Included + columns continue to also be present under the ``include_columns`` key for + backwards compatibility. diff --git a/lib/sqlalchemy/dialects/mssql/base.py b/lib/sqlalchemy/dialects/mssql/base.py index 5b38c4bb51..03ab9e85e4 100644 --- a/lib/sqlalchemy/dialects/mssql/base.py +++ b/lib/sqlalchemy/dialects/mssql/base.py @@ -3050,6 +3050,12 @@ class MSDialect(default.DefaultDialect): indexes[row["index_id"]]["column_names"].append( row["name"] ) + for index_info in indexes.values(): + # NOTE: "root level" include_columns is legacy, now part of + # dialect_options (issue #7382) + index_info.setdefault("dialect_options", {})[ + "mssql_include" + ] = index_info["include_columns"] return list(indexes.values()) diff --git a/lib/sqlalchemy/dialects/postgresql/base.py b/lib/sqlalchemy/dialects/postgresql/base.py index 614c84b56e..869e48bd41 100644 --- a/lib/sqlalchemy/dialects/postgresql/base.py +++ b/lib/sqlalchemy/dialects/postgresql/base.py @@ -4242,6 +4242,8 @@ class PGDialect(default.DefaultDialect): "column_names": [idx["cols"][i] for i in idx["key"]], } if self.server_version_info >= (11, 0): + # NOTE: this is legacy, this is part of dialect_options now + # as of #7382 entry["include_columns"] = [idx["cols"][i] for i in idx["inc"]] if "duplicates_constraint" in idx: entry["duplicates_constraint"] = idx["duplicates_constraint"] @@ -4250,6 +4252,10 @@ class PGDialect(default.DefaultDialect): (idx["cols"][idx["key"][i]], value) for i, value in idx["sorting"].items() ) + if "include_columns" in entry: + entry.setdefault("dialect_options", {})[ + "postgresql_include" + ] = entry["include_columns"] if "options" in idx: entry.setdefault("dialect_options", {})[ "postgresql_with" diff --git a/lib/sqlalchemy/testing/suite/test_reflection.py b/lib/sqlalchemy/testing/suite/test_reflection.py index ba176bcd91..9287f76dec 100644 --- a/lib/sqlalchemy/testing/suite/test_reflection.py +++ b/lib/sqlalchemy/testing/suite/test_reflection.py @@ -1232,6 +1232,9 @@ class ComponentReflectionTestExtra(fixtures.TestBase): ] if testing.requires.index_reflects_included_columns.enabled: expected[0]["include_columns"] = [] + expected[0]["dialect_options"] = { + "%s_include" % connection.engine.name: [] + } with expect_warnings( "Skipped unsupported reflection of expression-based index t_idx" @@ -1264,10 +1267,21 @@ class ComponentReflectionTestExtra(fixtures.TestBase): "column_names": ["x"], "include_columns": ["y"], "unique": False, + "dialect_options": { + "%s_include" % connection.engine.name: ["y"] + }, } ], ) + t2 = Table("t", MetaData(), autoload_with=connection) + eq_( + list(t2.indexes)[0].dialect_options[connection.engine.name][ + "include" + ], + ["y"], + ) + def _type_round_trip(self, connection, metadata, *types): t = Table( "t", diff --git a/test/dialect/postgresql/test_reflection.py b/test/dialect/postgresql/test_reflection.py index 2bfce557fe..0e8a18c0fc 100644 --- a/test/dialect/postgresql/test_reflection.py +++ b/test/dialect/postgresql/test_reflection.py @@ -183,6 +183,7 @@ class PartitionedReflectionTest(fixtures.TablesTest, AssertsExecutionResults): "unique": False, "column_names": ["q"], "include_columns": [], + "dialect_options": {"postgresql_include": []}, } ], ) @@ -198,6 +199,7 @@ class PartitionedReflectionTest(fixtures.TablesTest, AssertsExecutionResults): { "column_names": ["q"], "include_columns": [], + "dialect_options": {"postgresql_include": []}, "name": mock.ANY, "unique": False, } @@ -1131,6 +1133,7 @@ class ReflectionTest( expected = [{"name": "idx1", "unique": False, "column_names": ["y"]}] if testing.requires.index_reflects_included_columns.enabled: expected[0]["include_columns"] = [] + expected[0]["dialect_options"] = {"postgresql_include": []} eq_(ind, expected) @@ -1163,6 +1166,7 @@ class ReflectionTest( ] if testing.requires.index_reflects_included_columns.enabled: expected[0]["include_columns"] = [] + expected[0]["dialect_options"]["postgresql_include"] = [] eq_(ind, expected) m = MetaData() @@ -1195,6 +1199,7 @@ class ReflectionTest( ] if testing.requires.index_reflects_included_columns.enabled: expected[0]["include_columns"] = [] + expected[0]["dialect_options"]["postgresql_include"] = [] eq_(ind, expected) m = MetaData() t1 = Table("t", m, autoload_with=connection) @@ -1229,6 +1234,7 @@ class ReflectionTest( "unique": False, "column_names": ["x"], "include_columns": ["name"], + "dialect_options": {"postgresql_include": ["name"]}, "name": "idx1", } ], @@ -1604,6 +1610,7 @@ class ReflectionTest( ] if testing.requires.index_reflects_included_columns.enabled: expected[0]["include_columns"] = [] + expected[0]["dialect_options"]["postgresql_include"] = [] eq_(insp.get_indexes("t"), expected)