From 34c0aaeedaf9865ea7aae2a04251c8f06df3230e Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Tue, 2 Sep 2014 14:21:10 -0400 Subject: [PATCH] - An adjustment to table/index reflection such that if an index reports a column that isn't found to be present in the table, a warning is emitted and the column is skipped. This can occur for some special system column situations as has been observed with Oracle. fixes #3180 --- doc/build/changelog/changelog_09.rst | 11 +++++++++++ lib/sqlalchemy/engine/reflection.py | 25 +++++++++++++++++-------- lib/sqlalchemy/testing/__init__.py | 2 +- lib/sqlalchemy/testing/assertions.py | 1 - test/engine/test_reflection.py | 20 ++++++++++++++++++++ 5 files changed, 49 insertions(+), 10 deletions(-) diff --git a/doc/build/changelog/changelog_09.rst b/doc/build/changelog/changelog_09.rst index 2931916e3c..44a2add716 100644 --- a/doc/build/changelog/changelog_09.rst +++ b/doc/build/changelog/changelog_09.rst @@ -13,6 +13,17 @@ .. changelog:: :version: 0.9.8 + .. change:: + :tags: bug, sql + :versions: 1.0.0 + :tickets: 3180 + + An adjustment to table/index reflection such that if an index + reports a column that isn't found to be present in the table, + a warning is emitted and the column is skipped. This can occur + for some special system column situations as has been observed + with Oracle. + .. change:: :tags: bug, ext :versions: 1.0.0 diff --git a/lib/sqlalchemy/engine/reflection.py b/lib/sqlalchemy/engine/reflection.py index 012d1d35dd..cf1f2d3dd1 100644 --- a/lib/sqlalchemy/engine/reflection.py +++ b/lib/sqlalchemy/engine/reflection.py @@ -578,18 +578,27 @@ class Inspector(object): name = index_d['name'] columns = index_d['column_names'] unique = index_d['unique'] - flavor = index_d.get('type', 'unknown type') + flavor = index_d.get('type', 'index') if include_columns and \ not set(columns).issubset(include_columns): util.warn( - "Omitting %s KEY for (%s), key covers omitted columns." % + "Omitting %s key for (%s), key covers omitted columns." % (flavor, ', '.join(columns))) continue # look for columns by orig name in cols_by_orig_name, # but support columns that are in-Python only as fallback - sa_schema.Index(name, *[ - cols_by_orig_name[c] if c in cols_by_orig_name - else table.c[c] - for c in columns - ], - **dict(unique=unique)) + idx_cols = [] + for c in columns: + try: + idx_col = cols_by_orig_name[c] \ + if c in cols_by_orig_name else table.c[c] + except KeyError: + util.warn( + "%s key '%s' was not located in " + "columns for table '%s'" % ( + flavor, c, table_name + )) + else: + idx_cols.append(idx_col) + + sa_schema.Index(name, *idx_cols, **dict(unique=unique)) diff --git a/lib/sqlalchemy/testing/__init__.py b/lib/sqlalchemy/testing/__init__.py index 8f8f56412e..fd4ed3886e 100644 --- a/lib/sqlalchemy/testing/__init__.py +++ b/lib/sqlalchemy/testing/__init__.py @@ -21,7 +21,7 @@ def against(*queries): from .assertions import emits_warning, emits_warning_on, uses_deprecated, \ eq_, ne_, is_, is_not_, startswith_, assert_raises, \ assert_raises_message, AssertsCompiledSQL, ComparesTables, \ - AssertsExecutionResults, expect_deprecated + AssertsExecutionResults, expect_deprecated, expect_warnings from .util import run_as_contextmanager, rowset, fail, provide_metadata, adict diff --git a/lib/sqlalchemy/testing/assertions.py b/lib/sqlalchemy/testing/assertions.py index c15b95d059..d255991368 100644 --- a/lib/sqlalchemy/testing/assertions.py +++ b/lib/sqlalchemy/testing/assertions.py @@ -19,7 +19,6 @@ from .warnings import resetwarnings from .exclusions import db_spec, _is_excluded from . import assertsql from . import config -import itertools from .util import fail import contextlib diff --git a/test/engine/test_reflection.py b/test/engine/test_reflection.py index d019a156e0..952dfa7686 100644 --- a/test/engine/test_reflection.py +++ b/test/engine/test_reflection.py @@ -967,6 +967,26 @@ class ReflectionTest(fixtures.TestBase, ComparesTables): assert set([t2.c.name, t2.c.id]) == set(r2.columns) assert set([t2.c.name]) == set(r3.columns) + @testing.provide_metadata + def test_index_reflection_cols_busted(self): + t = Table('x', self.metadata, + Column('a', Integer), Column('b', Integer)) + sa.Index('x_ix', t.c.a, t.c.b) + self.metadata.create_all() + + def mock_get_columns(self, connection, table_name, **kw): + return [ + {"name": "b", "type": Integer, "primary_key": False} + ] + + with testing.mock.patch.object( + testing.db.dialect, "get_columns", mock_get_columns): + m = MetaData() + with testing.expect_warnings( + "index key 'a' was not located in columns"): + t = Table('x', m, autoload=True, autoload_with=testing.db) + + eq_(list(t.indexes)[0].columns, [t.c.b]) @testing.requires.views @testing.provide_metadata -- 2.47.3