@@ -1674,85 +1674,81 @@ def get_sqlalchemy_column_metadata( # noqa: C901 # FIXME CoP
16741674 table_selectable : sqlalchemy .Select ,
16751675 schema_name : Optional [str ] = None ,
16761676) -> Sequence [Mapping [str , Any ]] | None :
1677- try :
1678- columns : Sequence [Dict [str , Any ]]
1677+ columns : Sequence [Dict [str , Any ]]
16791678
1680- engine = execution_engine .engine
1681- inspector = execution_engine .get_inspector ()
1682- try :
1683- # if a custom query was passed
1684- if sqlalchemy .TextClause and isinstance (table_selectable , sqlalchemy .TextClause ): # type: ignore[truthy-function]
1685- if hasattr (table_selectable , "selected_columns" ):
1686- # New in version 1.4.
1687- columns = table_selectable .selected_columns .columns
1688- else :
1689- # Implicit subquery for columns().column was deprecated in SQLAlchemy 1.4
1690- # We must explicitly create a subquery
1691- columns = table_selectable .columns ().subquery ().columns
1692- elif sqlalchemy .quoted_name and isinstance (table_selectable , sqlalchemy .quoted_name ): # type: ignore[truthy-function]
1693- columns = inspector .get_columns (
1694- table_name = table_selectable ,
1695- schema = schema_name ,
1696- )
1679+ engine = execution_engine .engine
1680+ inspector = execution_engine .get_inspector ()
1681+ try :
1682+ # if a custom query was passed
1683+ if sqlalchemy .TextClause and isinstance (table_selectable , sqlalchemy .TextClause ): # type: ignore[truthy-function]
1684+ if hasattr (table_selectable , "selected_columns" ):
1685+ # New in version 1.4.
1686+ columns = table_selectable .selected_columns .columns
16971687 else :
1698- # For Select, Subquery, or other SQLAlchemy constructs (e.g., when row conditions are applied),
1699- # we cannot use inspector.get_columns() as they are not simple table names.
1700- # Raise an exception to trigger the fallback mechanism that uses column reflection.
1701- logger .debug (
1702- f"table_selectable is of type { type (table_selectable ).__name__ } , "
1703- "using column reflection fallback"
1704- )
1705- raise AttributeError (
1706- "Cannot introspect columns from complex query; using reflection fallback"
1707- )
1708- except (
1709- KeyError ,
1710- AttributeError ,
1711- sa .exc .NoSuchTableError ,
1712- sa .exc .ProgrammingError ,
1713- ) as exc :
1714- logger .debug (f"{ type (exc ).__name__ } while introspecting columns" , exc_info = exc )
1715- logger .info (f"While introspecting columns { exc !r} ; attempting reflection fallback" )
1716- # we will get a KeyError for temporary tables, since
1717- # reflection will not find the temporary schema
1718- columns = column_reflection_fallback (
1719- selectable = table_selectable ,
1720- dialect = engine .dialect ,
1721- sqlalchemy_engine = engine ,
1688+ # Implicit subquery for columns().column was deprecated in SQLAlchemy 1.4
1689+ # We must explicitly create a subquery
1690+ columns = table_selectable .columns ().subquery ().columns
1691+ elif sqlalchemy .quoted_name and isinstance (table_selectable , sqlalchemy .quoted_name ): # type: ignore[truthy-function]
1692+ columns = inspector .get_columns (
1693+ table_name = table_selectable ,
1694+ schema = schema_name ,
17221695 )
1723-
1724- # Use fallback because for mssql and trino reflection mechanisms do not throw an error but return an empty list # noqa: E501 # FIXME CoP
1725- if len (columns ) == 0 :
1726- columns = column_reflection_fallback (
1727- selectable = table_selectable ,
1728- dialect = engine .dialect ,
1729- sqlalchemy_engine = engine ,
1696+ else :
1697+ # For Select, Subquery, or other SQLAlchemy constructs (e.g., when row conditions are applied),
1698+ # we cannot use inspector.get_columns() as they are not simple table names.
1699+ # Raise an exception to trigger the fallback mechanism that uses column reflection.
1700+ logger .debug (
1701+ f"table_selectable is of type { type (table_selectable ).__name__ } , "
1702+ "using column reflection fallback"
1703+ )
1704+ raise AttributeError (
1705+ "Cannot introspect columns from complex query; using reflection fallback"
17301706 )
1707+ except (
1708+ KeyError ,
1709+ AttributeError ,
1710+ sa .exc .NoSuchTableError ,
1711+ sa .exc .ProgrammingError ,
1712+ ) as exc :
1713+ logger .debug (f"{ type (exc ).__name__ } while introspecting columns" , exc_info = exc )
1714+ logger .info (f"While introspecting columns { exc !r} ; attempting reflection fallback" )
1715+ # we will get a KeyError for temporary tables, since
1716+ # reflection will not find the temporary schema
1717+ columns = column_reflection_fallback (
1718+ selectable = table_selectable ,
1719+ dialect = engine .dialect ,
1720+ sqlalchemy_engine = engine ,
1721+ )
17311722
1732- dialect_name = execution_engine .dialect .name
1733- if dialect_name in [
1734- GXSqlDialect .DATABRICKS ,
1735- GXSqlDialect .POSTGRESQL ,
1736- GXSqlDialect .SNOWFLAKE ,
1737- GXSqlDialect .TRINO ,
1738- ]:
1739- # WARNING: Do not alter columns in place, as they are cached on the inspector
1740- columns_copy = [column .copy () for column in columns ]
1741- for column in columns_copy :
1742- if column .get ("type" ):
1743- # When using column_reflection_fallback, we might not be able to
1744- # extract the column type, and only have the column name
1745- compiled_type = column ["type" ].compile (dialect = execution_engine .dialect )
1746- # Make the type case-insensitive
1747- column ["type" ] = CaseInsensitiveString (str (compiled_type ))
1748-
1749- # Wrap all columns in CaseInsensitiveNameDict for all three dialects
1750- return [CaseInsensitiveNameDict (column ) for column in columns_copy ]
1751-
1752- return columns
1753- except AttributeError as e :
1754- logger .debug (f"Error while introspecting columns: { e !r} " , exc_info = e )
1755- return None
1723+ # Use fallback because for mssql and trino reflection mechanisms do not throw an error but return an empty list # noqa: E501 # FIXME CoP
1724+ if len (columns ) == 0 :
1725+ columns = column_reflection_fallback (
1726+ selectable = table_selectable ,
1727+ dialect = engine .dialect ,
1728+ sqlalchemy_engine = engine ,
1729+ )
1730+
1731+ dialect_name = execution_engine .dialect .name
1732+ if dialect_name in [
1733+ GXSqlDialect .DATABRICKS ,
1734+ GXSqlDialect .POSTGRESQL ,
1735+ GXSqlDialect .SNOWFLAKE ,
1736+ GXSqlDialect .TRINO ,
1737+ ]:
1738+ # WARNING: Do not alter columns in place, as they are cached on the inspector
1739+ columns_copy = [column .copy () for column in columns ]
1740+ for column in columns_copy :
1741+ if column .get ("type" ):
1742+ # When using column_reflection_fallback, we might not be able to
1743+ # extract the column type, and only have the column name
1744+ compiled_type = column ["type" ].compile (dialect = execution_engine .dialect )
1745+ # Make the type case-insensitive
1746+ column ["type" ] = CaseInsensitiveString (str (compiled_type ))
1747+
1748+ # Wrap all columns in CaseInsensitiveNameDict for all three dialects
1749+ return [CaseInsensitiveNameDict (column ) for column in columns_copy ]
1750+
1751+ return columns
17561752
17571753
17581754def column_reflection_fallback ( # noqa: C901, PLR0912, PLR0915 # FIXME CoP
0 commit comments