diff options
Diffstat (limited to 'lib/sqlalchemy/engine')
| -rw-r--r-- | lib/sqlalchemy/engine/base.py | 20 | ||||
| -rw-r--r-- | lib/sqlalchemy/engine/default.py | 25 | ||||
| -rw-r--r-- | lib/sqlalchemy/engine/result.py | 42 |
3 files changed, 77 insertions, 10 deletions
diff --git a/lib/sqlalchemy/engine/base.py b/lib/sqlalchemy/engine/base.py index 4ed3b9af7..34a4f04a9 100644 --- a/lib/sqlalchemy/engine/base.py +++ b/lib/sqlalchemy/engine/base.py @@ -1000,7 +1000,8 @@ class Connection(Connectable): tuple or scalar positional parameters. """ - if isinstance(object_, util.string_types[0]): + + if isinstance(object_, util.string_types): util.warn_deprecated_20( "Passing a string to Connection.execute() is " "deprecated and will be removed in version 2.0. Use the " @@ -1098,26 +1099,33 @@ class Connection(Connectable): keys = [] dialect = self.dialect + if "compiled_cache" in self._execution_options: + elem_cache_key, extracted_params = elem._generate_cache_key() key = ( dialect, - elem, + elem_cache_key, tuple(sorted(keys)), bool(self._schema_translate_map), len(distilled_params) > 1, ) - compiled_sql = self._execution_options["compiled_cache"].get(key) + cache = self._execution_options["compiled_cache"] + compiled_sql = cache.get(key) + if compiled_sql is None: compiled_sql = elem.compile( dialect=dialect, + cache_key=(elem_cache_key, extracted_params), column_keys=keys, inline=len(distilled_params) > 1, schema_translate_map=self._schema_translate_map, linting=self.dialect.compiler_linting | compiler.WARN_LINTING, ) - self._execution_options["compiled_cache"][key] = compiled_sql + cache[key] = compiled_sql + else: + extracted_params = None compiled_sql = elem.compile( dialect=dialect, column_keys=keys, @@ -1133,6 +1141,8 @@ class Connection(Connectable): distilled_params, compiled_sql, distilled_params, + elem, + extracted_params, ) if self._has_events or self.engine._has_events: self.dispatch.after_execute(self, elem, multiparams, params, ret) @@ -1156,6 +1166,8 @@ class Connection(Connectable): parameters, compiled, parameters, + None, + None, ) if self._has_events or self.engine._has_events: self.dispatch.after_execute( diff --git a/lib/sqlalchemy/engine/default.py b/lib/sqlalchemy/engine/default.py index c44f07538..af61be034 100644 --- a/lib/sqlalchemy/engine/default.py +++ b/lib/sqlalchemy/engine/default.py @@ -750,7 +750,14 @@ class DefaultExecutionContext(interfaces.ExecutionContext): @classmethod def _init_compiled( - cls, dialect, connection, dbapi_connection, compiled, parameters + cls, + dialect, + connection, + dbapi_connection, + compiled, + parameters, + invoked_statement, + extracted_parameters, ): """Initialize execution context for a Compiled construct.""" @@ -758,7 +765,8 @@ class DefaultExecutionContext(interfaces.ExecutionContext): self.root_connection = connection self._dbapi_connection = dbapi_connection self.dialect = connection.dialect - + self.extracted_parameters = extracted_parameters + self.invoked_statement = invoked_statement self.compiled = compiled # this should be caught in the engine before @@ -778,7 +786,6 @@ class DefaultExecutionContext(interfaces.ExecutionContext): compiled._textual_ordered_columns, compiled._loose_column_name_matching, ) - self.isinsert = compiled.isinsert self.isupdate = compiled.isupdate self.isdelete = compiled.isdelete @@ -792,10 +799,18 @@ class DefaultExecutionContext(interfaces.ExecutionContext): ) if not parameters: - self.compiled_parameters = [compiled.construct_params()] + self.compiled_parameters = [ + compiled.construct_params( + extracted_parameters=extracted_parameters + ) + ] else: self.compiled_parameters = [ - compiled.construct_params(m, _group_number=grp) + compiled.construct_params( + m, + _group_number=grp, + extracted_parameters=extracted_parameters, + ) for grp, m in enumerate(parameters) ] diff --git a/lib/sqlalchemy/engine/result.py b/lib/sqlalchemy/engine/result.py index ac033a5ae..986edd617 100644 --- a/lib/sqlalchemy/engine/result.py +++ b/lib/sqlalchemy/engine/result.py @@ -114,6 +114,44 @@ class CursorResultMetaData(ResultMetaData): "keys", ) + def _adapt_to_context(self, context): + """When using a cached result metadata against a new context, + we need to rewrite the _keymap so that it has the specific + Column objects in the new context inside of it. this accommodates + for select() constructs that contain anonymized columns and + are cached. + + """ + if not context.compiled._result_columns: + return self + + compiled_statement = context.compiled.statement + invoked_statement = context.invoked_statement + + # same statement was invoked as the one we cached against, + # return self + if compiled_statement is invoked_statement: + return self + + # make a copy and add the columns from the invoked statement + # to the result map. + md = self.__class__.__new__(self.__class__) + + md._keymap = self._keymap.copy() + + # match up new columns positionally to the result columns + for existing, new in zip( + context.compiled._result_columns, + invoked_statement._exported_columns_iterator(), + ): + md._keymap[new] = md._keymap[existing[RM_NAME]] + + md.case_sensitive = self.case_sensitive + md.matched_on_name = self.matched_on_name + md._processors = self._processors + md.keys = self.keys + return md + def __init__(self, parent, cursor_description): context = parent.context dialect = context.dialect @@ -1107,7 +1145,9 @@ class BaseResult(object): if strat.cursor_description is not None: if self.context.compiled: if self.context.compiled._cached_metadata: - self._metadata = self.context.compiled._cached_metadata + cached_md = self.context.compiled._cached_metadata + self._metadata = cached_md._adapt_to_context(self.context) + else: self._metadata = ( self.context.compiled._cached_metadata |
