From: Mike Bayer Date: Fri, 5 Jun 2026 20:02:02 +0000 (-0400) Subject: factor single-table reflection wrappers into common mixin X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=eeedfc4d522b88df4e3855fed11d8b7da10b0f5b;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git factor single-table reflection wrappers into common mixin Introduced _BackendsMultiReflection mixin in engine/default.py that provides the get_columns(), get_pk_constraint(), get_foreign_keys(), get_indexes(), get_unique_constraints(), get_check_constraints(), get_table_comment(), and get_table_options() single-table methods, each delegating to the corresponding get_multi_* method with filter_names=[table_name]. PostgreSQL, Oracle, and MSSQL dialects now inherit from this mixin instead of duplicating the wrapper pattern. Oracle retains its _value_or_raise() override which applies normalize_name() for case-folding. MSSQL overrides get_unique_constraints(), get_check_constraints(), and get_table_options() with NotImplementedError since it has no native get_multi_* for those yet. Change-Id: If68bbf94b3956348fc7ae8179ff093d63dcdfbe2 --- diff --git a/lib/sqlalchemy/dialects/mssql/base.py b/lib/sqlalchemy/dialects/mssql/base.py index b313acdbd9..9d348eb92e 100644 --- a/lib/sqlalchemy/dialects/mssql/base.py +++ b/lib/sqlalchemy/dialects/mssql/base.py @@ -3072,7 +3072,7 @@ def _schema_elements(schema): return dbname, owner -class MSDialect(default.DefaultDialect): +class MSDialect(default._BackendsMultiReflection, default.DefaultDialect): # will assume it's at least mssql2005 name = "mssql" supports_statement_cache = True @@ -3959,22 +3959,6 @@ index_info AS ( lookup = {n.lower(): n for n in filter_names} return lambda n: lookup.get(n.lower(), n) - @staticmethod - def _value_or_raise(data, table, schema): - """Unwrap a single ``(schema, table)`` entry from a multi-method - result, raising :exc:`.NoSuchTableError` when missing. - - Mirrors PostgreSQL's helper of the same name. Used by the - single-table reflection wrappers that delegate to the multi - implementation. - """ - try: - return dict(data)[(schema, table)] - except KeyError: - raise exc.NoSuchTableError( - f"{schema}.{table}" if schema else table - ) from None - @_db_plus_owner_multi def get_multi_columns( self, @@ -4791,64 +4775,17 @@ index_info AS ( exec_opts={"schema_translate_map": {"sys": "tempdb.sys"}}, ) - # --- Single-table reflection wrappers (delegate to multi) --- + # override mixin methods for which this dialect has no native + # get_multi_* implementation, preventing infinite recursion + # through DefaultDialect._default_multi_reflect - @reflection.cache - def get_columns(self, connection, table_name, schema=None, **kw): - data = self.get_multi_columns( - connection, - schema=schema, - filter_names=[table_name], - scope=ObjectScope.ANY, - kind=ObjectKind.ANY, - **kw, - ) - return self._value_or_raise(data, table_name, schema) - - @reflection.cache - def get_pk_constraint(self, connection, table_name, schema=None, **kw): - data = self.get_multi_pk_constraint( - connection, - schema=schema, - filter_names=[table_name], - scope=ObjectScope.ANY, - kind=ObjectKind.ANY, - **kw, - ) - return self._value_or_raise(data, table_name, schema) - - @reflection.cache - def get_foreign_keys(self, connection, table_name, schema=None, **kw): - data = self.get_multi_foreign_keys( - connection, - schema=schema, - filter_names=[table_name], - scope=ObjectScope.ANY, - kind=ObjectKind.ANY, - **kw, - ) - return self._value_or_raise(data, table_name, schema) + def get_unique_constraints( + self, connection, table_name, schema=None, **kw + ): + raise NotImplementedError() - @reflection.cache - def get_indexes(self, connection, table_name, schema=None, **kw): - data = self.get_multi_indexes( - connection, - schema=schema, - filter_names=[table_name], - scope=ObjectScope.ANY, - kind=ObjectKind.ANY, - **kw, - ) - return self._value_or_raise(data, table_name, schema) + def get_check_constraints(self, connection, table_name, schema=None, **kw): + raise NotImplementedError() - @reflection.cache - def get_table_comment(self, connection, table_name, schema=None, **kw): - data = self.get_multi_table_comment( - connection, - schema=schema, - filter_names=[table_name], - scope=ObjectScope.ANY, - kind=ObjectKind.ANY, - **kw, - ) - return self._value_or_raise(data, table_name, schema) + def get_table_options(self, connection, table_name, schema=None, **kw): + raise NotImplementedError() diff --git a/lib/sqlalchemy/dialects/oracle/base.py b/lib/sqlalchemy/dialects/oracle/base.py index a27306e067..fbe0c6c90a 100644 --- a/lib/sqlalchemy/dialects/oracle/base.py +++ b/lib/sqlalchemy/dialects/oracle/base.py @@ -2083,7 +2083,7 @@ class OracleExecutionContext(default.DefaultExecutionContext): ) -class OracleDialect(default.DefaultDialect): +class OracleDialect(default._BackendsMultiReflection, default.DefaultDialect): name = "oracle" supports_statement_cache = True supports_alter = True @@ -2738,21 +2738,6 @@ class OracleDialect(default.DefaultDialect): else: return False, {} - @reflection.cache - def get_table_options(self, connection, table_name, schema=None, **kw): - """Supported kw arguments are: ``dblink`` to reflect via a db link; - ``oracle_resolve_synonyms`` to resolve names to synonyms - """ - data = self.get_multi_table_options( - connection, - schema=schema, - filter_names=[table_name], - scope=ObjectScope.ANY, - kind=ObjectKind.ANY, - **kw, - ) - return self._value_or_raise(data, table_name, schema) - @lru_cache() def _table_options_query( self, owner, scope, kind, has_filter_names, has_mat_views @@ -2876,22 +2861,6 @@ class OracleDialect(default.DefaultDialect): return options.items() - @reflection.cache - def get_columns(self, connection, table_name, schema=None, **kw): - """Supported kw arguments are: ``dblink`` to reflect via a db link; - ``oracle_resolve_synonyms`` to resolve names to synonyms - """ - - data = self.get_multi_columns( - connection, - schema=schema, - filter_names=[table_name], - scope=ObjectScope.ANY, - kind=ObjectKind.ANY, - **kw, - ) - return self._value_or_raise(data, table_name, schema) - def _run_batches( self, connection, query, dblink, returns_long, mappings, all_objects ): @@ -3158,21 +3127,6 @@ class OracleDialect(default.DefaultDialect): identity["oracle_order"] = value == "Y" return identity - @reflection.cache - def get_table_comment(self, connection, table_name, schema=None, **kw): - """Supported kw arguments are: ``dblink`` to reflect via a db link; - ``oracle_resolve_synonyms`` to resolve names to synonyms - """ - data = self.get_multi_table_comment( - connection, - schema=schema, - filter_names=[table_name], - scope=ObjectScope.ANY, - kind=ObjectKind.ANY, - **kw, - ) - return self._value_or_raise(data, table_name, schema) - @lru_cache() def _comment_query(self, owner, scope, kind, has_filter_names): # NOTE: all_tab_comments / all_mview_comments have a row for all @@ -3270,21 +3224,6 @@ class OracleDialect(default.DefaultDialect): for table, comment in result ) - @reflection.cache - def get_indexes(self, connection, table_name, schema=None, **kw): - """Supported kw arguments are: ``dblink`` to reflect via a db link; - ``oracle_resolve_synonyms`` to resolve names to synonyms - """ - data = self.get_multi_indexes( - connection, - schema=schema, - filter_names=[table_name], - scope=ObjectScope.ANY, - kind=ObjectKind.ANY, - **kw, - ) - return self._value_or_raise(data, table_name, schema) - @lru_cache() def _index_query(self, owner): return ( @@ -3452,21 +3391,6 @@ class OracleDialect(default.DefaultDialect): ) ) - @reflection.cache - def get_pk_constraint(self, connection, table_name, schema=None, **kw): - """Supported kw arguments are: ``dblink`` to reflect via a db link; - ``oracle_resolve_synonyms`` to resolve names to synonyms - """ - data = self.get_multi_pk_constraint( - connection, - schema=schema, - filter_names=[table_name], - scope=ObjectScope.ANY, - kind=ObjectKind.ANY, - **kw, - ) - return self._value_or_raise(data, table_name, schema) - @lru_cache() def _constraint_query(self, owner): local = dictionary.all_cons_columns.alias("local") @@ -3590,27 +3514,6 @@ class OracleDialect(default.DefaultDialect): ) ) - @reflection.cache - def get_foreign_keys( - self, - connection, - table_name, - schema=None, - **kw, - ): - """Supported kw arguments are: ``dblink`` to reflect via a db link; - ``oracle_resolve_synonyms`` to resolve names to synonyms - """ - data = self.get_multi_foreign_keys( - connection, - schema=schema, - filter_names=[table_name], - scope=ObjectScope.ANY, - kind=ObjectKind.ANY, - **kw, - ) - return self._value_or_raise(data, table_name, schema) - @_handle_synonyms_decorator def get_multi_foreign_keys( self, @@ -3745,23 +3648,6 @@ class OracleDialect(default.DefaultDialect): ) ) - @reflection.cache - def get_unique_constraints( - self, connection, table_name, schema=None, **kw - ): - """Supported kw arguments are: ``dblink`` to reflect via a db link; - ``oracle_resolve_synonyms`` to resolve names to synonyms - """ - data = self.get_multi_unique_constraints( - connection, - schema=schema, - filter_names=[table_name], - scope=ObjectScope.ANY, - kind=ObjectKind.ANY, - **kw, - ) - return self._value_or_raise(data, table_name, schema) - @_handle_synonyms_decorator def get_multi_unique_constraints( self, @@ -3886,24 +3772,6 @@ class OracleDialect(default.DefaultDialect): else: return rp - @reflection.cache - def get_check_constraints( - self, connection, table_name, schema=None, include_all=False, **kw - ): - """Supported kw arguments are: ``dblink`` to reflect via a db link; - ``oracle_resolve_synonyms`` to resolve names to synonyms - """ - data = self.get_multi_check_constraints( - connection, - schema=schema, - filter_names=[table_name], - scope=ObjectScope.ANY, - include_all=include_all, - kind=ObjectKind.ANY, - **kw, - ) - return self._value_or_raise(data, table_name, schema) - @_handle_synonyms_decorator def get_multi_check_constraints( self, diff --git a/lib/sqlalchemy/dialects/postgresql/base.py b/lib/sqlalchemy/dialects/postgresql/base.py index 97a553bec1..1312fe033b 100644 --- a/lib/sqlalchemy/dialects/postgresql/base.py +++ b/lib/sqlalchemy/dialects/postgresql/base.py @@ -3424,7 +3424,7 @@ class PGDeferrableConnectionCharacteristic( return dialect.get_deferrable(dbapi_conn) -class PGDialect(default.DefaultDialect): +class PGDialect(default._BackendsMultiReflection, default.DefaultDialect): name = "postgresql" supports_statement_cache = True supports_alter = True @@ -3980,14 +3980,6 @@ class PGDialect(default.DefaultDialect): else: return res - def _value_or_raise(self, data, table, schema): - try: - return dict(data)[(schema, table)] - except KeyError: - raise exc.NoSuchTableError( - f"{schema}.{table}" if schema else table - ) from None - def _prepare_filter_names(self, filter_names): if filter_names: return True, {"filter_names": filter_names} @@ -4006,18 +3998,6 @@ class PGDialect(default.DefaultDialect): relkinds += pg_catalog.RELKINDS_MAT_VIEW return relkinds - @reflection.cache - def get_table_options(self, connection, table_name, schema=None, **kw): - data = self.get_multi_table_options( - connection, - schema=schema, - filter_names=[table_name], - scope=ObjectScope.ANY, - kind=ObjectKind.ANY, - **kw, - ) - return self._value_or_raise(data, table_name, schema) - @lru_cache() def _table_options_query(self, schema, has_filter_names, scope, kind): inherits_sq = ( @@ -4130,18 +4110,6 @@ class PGDialect(default.DefaultDialect): return table_options.items() - @reflection.cache - def get_columns(self, connection, table_name, schema=None, **kw): - data = self.get_multi_columns( - connection, - schema=schema, - filter_names=[table_name], - scope=ObjectScope.ANY, - kind=ObjectKind.ANY, - **kw, - ) - return self._value_or_raise(data, table_name, schema) - @lru_cache() def _columns_query(self, schema, has_filter_names, scope, kind): # NOTE: the query with the default and identity options scalar @@ -4739,18 +4707,6 @@ class PGDialect(default.DefaultDialect): else: yield tablename, None, None, None, None - @reflection.cache - def get_pk_constraint(self, connection, table_name, schema=None, **kw): - data = self.get_multi_pk_constraint( - connection, - schema=schema, - filter_names=[table_name], - scope=ObjectScope.ANY, - kind=ObjectKind.ANY, - **kw, - ) - return self._value_or_raise(data, table_name, schema) - def get_multi_pk_constraint( self, connection, schema, filter_names, scope, kind, **kw ): @@ -4784,26 +4740,6 @@ class PGDialect(default.DefaultDialect): for table_name, cols, pk_name, comment, opts in result ) - @reflection.cache - def get_foreign_keys( - self, - connection, - table_name, - schema=None, - postgresql_ignore_search_path=False, - **kw, - ): - data = self.get_multi_foreign_keys( - connection, - schema=schema, - filter_names=[table_name], - postgresql_ignore_search_path=postgresql_ignore_search_path, - scope=ObjectScope.ANY, - kind=ObjectKind.ANY, - **kw, - ) - return self._value_or_raise(data, table_name, schema) - @lru_cache() def _foreing_key_query(self, schema, has_filter_names, scope, kind): pg_class_ref = pg_catalog.pg_class.alias("cls_ref") @@ -5015,18 +4951,6 @@ class PGDialect(default.DefaultDialect): table_fks.append(fkey_d) return fkeys.items() - @reflection.cache - def get_indexes(self, connection, table_name, schema=None, **kw): - data = self.get_multi_indexes( - connection, - schema=schema, - filter_names=[table_name], - scope=ObjectScope.ANY, - kind=ObjectKind.ANY, - **kw, - ) - return self._value_or_raise(data, table_name, schema) - @util.memoized_property def _index_query(self): # NOTE: pg_index is used as from two times to improve performance, @@ -5341,20 +5265,6 @@ class PGDialect(default.DefaultDialect): table_indexes.append(index) return indexes.items() - @reflection.cache - def get_unique_constraints( - self, connection, table_name, schema=None, **kw - ): - data = self.get_multi_unique_constraints( - connection, - schema=schema, - filter_names=[table_name], - scope=ObjectScope.ANY, - kind=ObjectKind.ANY, - **kw, - ) - return self._value_or_raise(data, table_name, schema) - def get_multi_unique_constraints( self, connection, @@ -5389,18 +5299,6 @@ class PGDialect(default.DefaultDialect): uniques[(schema, table_name)].append(uc_dict) return uniques.items() - @reflection.cache - def get_table_comment(self, connection, table_name, schema=None, **kw): - data = self.get_multi_table_comment( - connection, - schema, - [table_name], - scope=ObjectScope.ANY, - kind=ObjectKind.ANY, - **kw, - ) - return self._value_or_raise(data, table_name, schema) - @lru_cache() def _comment_query(self, schema, has_filter_names, scope, kind): relkinds = self._kind_to_relkinds(kind) @@ -5445,18 +5343,6 @@ class PGDialect(default.DefaultDialect): for table, comment in result ) - @reflection.cache - def get_check_constraints(self, connection, table_name, schema=None, **kw): - data = self.get_multi_check_constraints( - connection, - schema, - [table_name], - scope=ObjectScope.ANY, - kind=ObjectKind.ANY, - **kw, - ) - return self._value_or_raise(data, table_name, schema) - @lru_cache() def _check_constraint_query(self, schema, has_filter_names, scope, kind): relkinds = self._kind_to_relkinds(kind) diff --git a/lib/sqlalchemy/engine/default.py b/lib/sqlalchemy/engine/default.py index 833ce04964..05dd336d47 100644 --- a/lib/sqlalchemy/engine/default.py +++ b/lib/sqlalchemy/engine/default.py @@ -44,6 +44,7 @@ import weakref from . import characteristics from . import cursor as _cursor from . import interfaces +from . import reflection from .base import Connection from .interfaces import CacheStats from .interfaces import DBAPICursor @@ -119,6 +120,121 @@ SERVER_SIDE_CURSOR_RE = re.compile(r"\s*SELECT", re.I | re.UNICODE) ) = list(CacheStats) +class _BackendsMultiReflection(Dialect): + """Mixin providing single-table reflection wrappers that delegate to + the corresponding ``get_multi_*`` methods. + + Used by dialects that implement native multi-table reflection + (PostgreSQL, Oracle, MSSQL). + """ + + def _value_or_raise(self, data, table, schema): + try: + return dict(data)[(schema, table)] + except KeyError: + raise exc.NoSuchTableError( + f"{schema}.{table}" if schema else table + ) from None + + @reflection.cache + def get_columns(self, connection, table_name, schema=None, **kw): + data = self.get_multi_columns( + connection, + schema=schema, + filter_names=[table_name], + scope=ObjectScope.ANY, + kind=ObjectKind.ANY, + **kw, + ) + return self._value_or_raise(data, table_name, schema) + + @reflection.cache + def get_table_options(self, connection, table_name, schema=None, **kw): + data = self.get_multi_table_options( + connection, + schema=schema, + filter_names=[table_name], + scope=ObjectScope.ANY, + kind=ObjectKind.ANY, + **kw, + ) + return self._value_or_raise(data, table_name, schema) + + @reflection.cache + def get_pk_constraint(self, connection, table_name, schema=None, **kw): + data = self.get_multi_pk_constraint( + connection, + schema=schema, + filter_names=[table_name], + scope=ObjectScope.ANY, + kind=ObjectKind.ANY, + **kw, + ) + return self._value_or_raise(data, table_name, schema) + + @reflection.cache + def get_foreign_keys(self, connection, table_name, schema=None, **kw): + data = self.get_multi_foreign_keys( + connection, + schema=schema, + filter_names=[table_name], + scope=ObjectScope.ANY, + kind=ObjectKind.ANY, + **kw, + ) + return self._value_or_raise(data, table_name, schema) + + @reflection.cache + def get_indexes(self, connection, table_name, schema=None, **kw): + data = self.get_multi_indexes( + connection, + schema=schema, + filter_names=[table_name], + scope=ObjectScope.ANY, + kind=ObjectKind.ANY, + **kw, + ) + return self._value_or_raise(data, table_name, schema) + + @reflection.cache + def get_unique_constraints( + self, connection, table_name, schema=None, **kw + ): + data = self.get_multi_unique_constraints( + connection, + schema=schema, + filter_names=[table_name], + scope=ObjectScope.ANY, + kind=ObjectKind.ANY, + **kw, + ) + return self._value_or_raise(data, table_name, schema) + + @reflection.cache + def get_check_constraints(self, connection, table_name, schema=None, **kw): + data = self.get_multi_check_constraints( + connection, + schema=schema, + filter_names=[table_name], + scope=ObjectScope.ANY, + kind=ObjectKind.ANY, + **kw, + ) + return self._value_or_raise(data, table_name, schema) + + @reflection.cache + def get_table_comment(self, connection, table_name, schema=None, **kw): + data = self.get_multi_table_comment( + connection, + schema=schema, + filter_names=[table_name], + scope=ObjectScope.ANY, + kind=ObjectKind.ANY, + **kw, + ) + return self._value_or_raise(data, table_name, schema) + + class DefaultDialect(Dialect): """Default implementation of Dialect"""