caching system that removes the need for the :class:`.BakedQuery` system.
Caching is now transparently active for all Core and ORM queries with no
action taken by the user, using the system described at :ref:`sql_caching`.
- For background on using lambda-style construction for cacheable Core and ORM
- SQL constructs, which is now an optional technique to provide additional
- performance gains, see the section :ref:`engine_lambda_caching`.
-
-.. versionadded:: 1.0.0
-
.. note::
The :mod:`sqlalchemy.ext.baked` extension is **not for beginners**. Using
Like all session flags, it is also accepted by factory objects like
:class:`.sessionmaker` and methods like :meth:`.sessionmaker.configure`.
-The immediate rationale for this flag is to reduce memory use in the case
-that the query baking used by relationship loaders and other loaders
-is not desirable. It also can be used in the case that an application
+The immediate rationale for this flag is so that an application
which is seeing issues potentially due to cache key conflicts from user-defined
baked queries or other baked query issues can turn the behavior off, in
order to identify or eliminate baked queries as the cause of an issue.
Lazy Loading Integration
------------------------
-The baked query system is integrated into SQLAlchemy's lazy loader feature
-as used by :func:`_orm.relationship`, and will cache queries for most lazy
-load conditions. A small subset of
-"lazy loads" may not be cached; these involve query options in conjunction with ad-hoc
-:obj:`.aliased` structures that cannot produce a repeatable cache
-key.
-
-.. versionchanged:: 1.2 "baked" queries are now the foundation of the
- lazy-loader feature of :func:`_orm.relationship`.
-
-Opting out with the bake_queries flag
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-The :func:`_orm.relationship` construct includes a flag
-:paramref:`_orm.relationship.bake_queries` which when set to False will cause
-that relationship to opt out of caching queries. Additionally, the
-:paramref:`.Session.enable_baked_queries` setting can be used to disable
-all "baked query" use. These flags can be useful to conserve memory,
-when memory conservation is more important than performance for a particular
-relationship or for the application overall.
+.. versionchanged:: 1.4 As of SQLAlchemy 1.4, the "baked query" system is no
+ longer part of the relationship loading system.
+ The :ref:`native caching <sql_caching>` system is used instead.
+
API Documentation
-----------------
if ck is None:
self.spoil(full=True)
else:
+ assert not ck[1], (
+ "loader options with variable bound parameters "
+ "not supported with baked queries. Please "
+ "use new-style select() statements for cached "
+ "ORM queries."
+ )
key += ck[0]
self.add_criteria(
for fn in self._post_criteria:
q = fn(q)
- params = q.load_options._params
+ params = q._params
execution_options = dict(q._execution_options)
execution_options.update(
{
def execute_and_instances(orm_context):
- params = orm_context.parameters
if orm_context.is_select:
load_options = active_options = orm_context.load_options
update_options = None
- if params is None:
- params = active_options._params
else:
load_options = None
__slots__ = (
"compile_state",
"query",
+ "params",
"load_options",
"bind_arguments",
"execution_options",
self,
compile_state,
statement,
+ params,
session,
load_options,
execution_options=None,
self.session = session
self.loaders_require_buffering = False
self.loaders_require_uniquing = False
+ self.params = params
self.propagated_loader_options = {
o for o in statement._with_options if o.propagate_to_loaders
@classmethod
def orm_setup_cursor_result(
- cls, session, statement, execution_options, bind_arguments, result
+ cls,
+ session,
+ statement,
+ params,
+ execution_options,
+ bind_arguments,
+ result,
):
execution_context = result.context
compile_state = execution_context.compiled.compile_state
querycontext = QueryContext(
compile_state,
statement,
+ params,
session,
load_options,
execution_options,
and "entity_namespace" in element._annotations
):
for elem in _select_iterables(
- element._annotations["entity_namespace"].columns
+ element._annotations[
+ "entity_namespace"
+ ]._all_column_expressions
):
yield elem
else:
from ..sql import operators
from ..sql import roles
from ..sql import visitors
+from ..sql.base import ExecutableOption
from ..sql.traversals import HasCacheKey
if util.TYPE_CHECKING:
)
-class ORMOption(HasCacheKey):
+class ORMOption(ExecutableOption):
"""Base class for option objects that are passed to ORM queries.
These options may be consumed by :meth:`.Query.options`,
@util.preload_module("sqlalchemy.orm.context")
def merge_result(query, iterator, load=True):
"""Merge a result into this :class:`.Query` object's Session."""
+
querycontext = util.preloaded.orm_context
session = query.session
with_for_update=None,
only_load_props=None,
no_autoflush=False,
- bind_arguments=util.immutabledict(),
+ bind_arguments=util.EMPTY_DICT,
+ execution_options=util.EMPTY_DICT,
):
"""Load the given identity key from the database."""
if key is not None:
identity_token=identity_token,
no_autoflush=no_autoflush,
bind_arguments=bind_arguments,
+ execution_options=execution_options,
)
only_load_props=None,
identity_token=None,
no_autoflush=False,
- bind_arguments=util.immutabledict(),
+ bind_arguments=util.EMPTY_DICT,
+ execution_options=util.EMPTY_DICT,
):
"""Load the given primary key identity from the database."""
query = statement
q = query._clone()
+ is_lambda = q._is_lambda_element
+
# TODO: fix these imports ....
from .context import QueryContext, ORMCompileState
if load_options is None:
load_options = QueryContext.default_load_options
- compile_options = ORMCompileState.default_compile_options.safe_merge(
- q._compile_options
- )
+ compile_options = ORMCompileState.default_compile_options
if primary_key_identity is not None:
- # mapper = query._only_full_mapper_zero("load_on_pk_identity")
-
- # TODO: error checking?
- mapper = query._raw_columns[0]._annotations["parententity"]
+ mapper = query._propagate_attrs["plugin_subject"]
(_get_clause, _get_params) = mapper._get_clause
"release."
)
- # TODO: can mapper._get_clause be pre-adapted?
- q._where_criteria = (
- sql_util._deep_annotate(_get_clause, {"_orm_adapt": True}),
- )
+ if is_lambda:
+ q = q.add_criteria(
+ lambda q: q.where(
+ sql_util._deep_annotate(_get_clause, {"_orm_adapt": True})
+ ),
+ )
+ else:
+ q._where_criteria = (
+ sql_util._deep_annotate(_get_clause, {"_orm_adapt": True}),
+ )
params = dict(
[
)
]
)
+ else:
+ params = None
- load_options += {"_params": params}
+ if is_lambda:
+ if with_for_update is not None or refresh_state or only_load_props:
+ raise NotImplementedError(
+ "refresh operation not supported with lambda statement"
+ )
- if with_for_update is not None:
- version_check = True
- q._for_update_arg = with_for_update
- elif query._for_update_arg is not None:
- version_check = True
- q._for_update_arg = query._for_update_arg
- else:
version_check = False
- if refresh_state and refresh_state.load_options:
- compile_options += {"_current_path": refresh_state.load_path.parent}
- q = q.options(*refresh_state.load_options)
-
- # TODO: most of the compile_options that are not legacy only involve this
- # function, so try to see if handling of them can mostly be local to here
+ _, load_options = _set_get_options(
+ compile_options,
+ load_options,
+ populate_existing=bool(refresh_state),
+ version_check=version_check,
+ only_load_props=only_load_props,
+ refresh_state=refresh_state,
+ identity_token=identity_token,
+ )
- q._compile_options, load_options = _set_get_options(
- compile_options,
- load_options,
- populate_existing=bool(refresh_state),
- version_check=version_check,
- only_load_props=only_load_props,
- refresh_state=refresh_state,
- identity_token=identity_token,
- )
- q._order_by = None
+ if no_autoflush:
+ load_options += {"_autoflush": False}
+ else:
+ if with_for_update is not None:
+ version_check = True
+ q._for_update_arg = with_for_update
+ elif query._for_update_arg is not None:
+ version_check = True
+ q._for_update_arg = query._for_update_arg
+ else:
+ version_check = False
+
+ if refresh_state and refresh_state.load_options:
+ compile_options += {
+ "_current_path": refresh_state.load_path.parent
+ }
+ q = q.options(*refresh_state.load_options)
+
+ # TODO: most of the compile_options that are not legacy only involve
+ # this function, so try to see if handling of them can mostly be local
+ # to here
+
+ q._compile_options, load_options = _set_get_options(
+ compile_options,
+ load_options,
+ populate_existing=bool(refresh_state),
+ version_check=version_check,
+ only_load_props=only_load_props,
+ refresh_state=refresh_state,
+ identity_token=identity_token,
+ )
+ q._order_by = None
- if no_autoflush:
- load_options += {"_autoflush": False}
+ if no_autoflush:
+ load_options += {"_autoflush": False}
+ execution_options = util.EMPTY_DICT.merge_with(
+ execution_options, {"_sa_orm_load_options": load_options}
+ )
result = (
session.execute(
q,
- params=load_options._params,
- execution_options={"_sa_orm_load_options": load_options},
+ params=params,
+ execution_options=execution_options,
bind_arguments=bind_arguments,
future=True,
)
"""
return self
- _cache_key_traversal = [
- ("mapper", visitors.ExtendedInternalTraversal.dp_plain_obj),
- ]
+ def _gen_cache_key(self, anon_map, bindparams):
+ return (self,)
@property
def entity(self):
)
)
+ @property
+ def _all_column_expressions(self):
+ poly_properties = self._polymorphic_properties
+ adapter = self._polymorphic_adapter
+
+ return [
+ adapter.columns[prop.columns[0]] if adapter else prop.columns[0]
+ for prop in poly_properties
+ if isinstance(prop, properties.ColumnProperty)
+ ]
+
def _columns_plus_keys(self, polymorphic_mappers=()):
if polymorphic_mappers:
poly_properties = self._iterate_polymorphic_properties(
@classmethod
def orm_setup_cursor_result(
- cls, session, statement, execution_options, bind_arguments, result
+ cls,
+ session,
+ statement,
+ params,
+ execution_options,
+ bind_arguments,
+ result,
):
# this stage of the execution is called after the
load_options = QueryContext.default_load_options
+ _params = util.EMPTY_DICT
+
# local Query builder state, not needed for
# compilation or execution
_aliased_generation = None
else:
stmt = self._compile_state(for_statement=True).statement
- if self.load_options._params:
- # this is the search and replace thing. this is kind of nuts
- # to be doing here.
- stmt = stmt.params(self.load_options._params)
+ if self._params:
+ stmt = stmt.params(self._params)
return stmt
return stmt
def subquery(
- self,
- name=None,
- with_labels=False,
- reduce_columns=False,
- _legacy_core_statement=False,
+ self, name=None, with_labels=False, reduce_columns=False,
):
"""Return the full SELECT statement represented by
this :class:`_query.Query`, embedded within an
if with_labels:
q = q.with_labels()
- if _legacy_core_statement:
- q = q._compile_state(for_statement=True).statement
- else:
- q = q.statement
+ q = q.statement
if reduce_columns:
q = q.reduce_columns()
after rollback or commit handles object state automatically.
This method is not intended for general use.
+ .. versionadded:: 1.4
+
+ The :meth:`.populate_existing` method is equivalent to passing the
+ ``populate_existing=True`` option to the
+ :meth:`_orm.Query.execution_options` method.
+
+
"""
self.load_options += {"_populate_existing": True}
self.with_labels()
.enable_eagerloads(False)
.correlate(None)
- .subquery(_legacy_core_statement=True)
+ .subquery()
._anonymous_fromclause()
)
- parententity = self._raw_columns[0]._annotations.get("parententity")
- if parententity:
- ac = aliased(parententity.mapper, alias=fromclause)
- q = self._from_selectable(ac)
- else:
- q = self._from_selectable(fromclause)
+ q = self._from_selectable(fromclause)
if entities:
q._set_entities(entities)
def execution_options(self, **kwargs):
""" Set non-SQL options which take effect during execution.
- The options are the same as those accepted by
- :meth:`_engine.Connection.execution_options`.
+ Options allowed here include all of those accepted by
+ :meth:`_engine.Connection.execution_options`, as well as a series
+ of ORM specific options:
+
+ ``populate_existing=True`` - equivalent to using
+ :meth:`_orm.Query.populate_existing`
+
+ ``autoflush=True|False`` - equivalent to using
+ :meth:`_orm.Query.autoflush`
+
+ ``yield_per=<value>`` - equivalent to using
+ :meth:`_orm.Query.yield_per`
Note that the ``stream_results`` execution option is enabled
automatically if the :meth:`~sqlalchemy.orm.query.Query.yield_per()`
- method is used.
+ method or execution option is used.
+
+ The execution options may also be specified on a per execution basis
+ when using :term:`2.0 style` queries via the
+ :paramref:`_orm.Session.execution_options` parameter.
+
+ .. versionadded:: 1.4 - added ORM options to
+ :meth:`_orm.Query.execution_options`
.. seealso::
"params() takes zero or one positional argument, "
"which is a dictionary."
)
- params = self.load_options._params.union(kwargs)
- self.load_options += {"_params": params}
+ self._params = self._params.union(kwargs)
def where(self, *criterion):
"""A synonym for :meth:`.Query.filter`.
def _iter(self):
# new style execution.
- params = self.load_options._params
+ params = self._params
+
statement = self._statement_20()
result = self.session.execute(
statement,
context = QueryContext(
compile_state,
compile_state.statement,
+ self._params,
self.session,
self.load_options,
)
delete_._where_criteria = self._where_criteria
result = self.session.execute(
delete_,
- self.load_options._params,
+ self._params,
execution_options={"synchronize_session": synchronize_session},
future=True,
)
upd._where_criteria = self._where_criteria
result = self.session.execute(
upd,
- self.load_options._params,
+ self._params,
execution_options={"synchronize_session": synchronize_session},
future=True,
)
context = QueryContext(
compile_state,
compile_state.statement,
+ self._params,
self.session,
self.load_options,
)
from ..sql import coercions
from ..sql import dml
from ..sql import roles
-from ..sql import selectable
from ..sql import visitors
from ..sql.base import CompileState
@property
def is_select(self):
"""return True if this is a SELECT operation."""
- return isinstance(self.statement, selectable.Select)
+ return self.statement.is_select
+
+ @property
+ def is_insert(self):
+ """return True if this is an INSERT operation."""
+ return self.statement.is_dml and self.statement.is_insert
@property
def is_update(self):
"""return True if this is an UPDATE operation."""
- return isinstance(self.statement, dml.Update)
+ return self.statement.is_dml and self.statement.is_update
@property
def is_delete(self):
"""return True if this is a DELETE operation."""
- return isinstance(self.statement, dml.Delete)
+ return self.statement.is_dml and self.statement.is_delete
@property
def _is_crud(self):
if compile_state_cls:
result = compile_state_cls.orm_setup_cursor_result(
- self, statement, execution_options, bind_arguments, result
+ self,
+ statement,
+ params,
+ execution_options,
+ bind_arguments,
+ result,
)
return result
from .base import _SET_DEFERRED_EXPIRED
from .context import _column_descriptions
from .context import ORMCompileState
+from .context import QueryContext
from .interfaces import LoaderStrategy
from .interfaces import StrategizedProperty
from .session import _state_session
from .util import aliased
from .. import event
from .. import exc as sa_exc
-from .. import future
from .. import inspect
from .. import log
from .. import sql
if (
loading.load_on_ident(
session,
- future.select(localparent).apply_labels(),
+ sql.select(localparent).apply_labels(),
state.key,
only_load_props=group,
refresh_state=state,
"_simple_lazy_clause",
"_raise_always",
"_raise_on_sql",
- "_bakery",
+ "_query_cache",
)
def __init__(self, parent, strategy_key):
for pk in self.mapper.primary_key
]
- @util.preload_module("sqlalchemy.ext.baked")
- def _memoized_attr__bakery(self):
- return util.preloaded.ext_baked.bakery(size=50)
+ def _memoized_attr__query_cache(self):
+ return util.LRUCache(30)
@util.preload_module("sqlalchemy.orm.strategy_options")
def _emit_lazyload(self, session, state, primary_key_identity, passive):
- # emit lazy load now using BakedQuery, to cut way down on the overhead
- # of generating queries.
- # there are two big things we are trying to guard against here:
- #
- # 1. two different lazy loads that need to have a different result,
- # being cached on the same key. The results between two lazy loads
- # can be different due to the options passed to the query, which
- # take effect for descendant objects. Therefore we have to make
- # sure paths and load options generate good cache keys, and if they
- # don't, we don't cache.
- # 2. a lazy load that gets cached on a key that includes some
- # "throwaway" object, like a per-query AliasedClass, meaning
- # the cache key will never be seen again and the cache itself
- # will fill up. (the cache is an LRU cache, so while we won't
- # run out of memory, it will perform terribly when it's full. A
- # warning is emitted if this occurs.) We must prevent the
- # generation of a cache key that is including a throwaway object
- # in the key.
-
strategy_options = util.preloaded.orm_strategy_options
- # note that "lazy='select'" and "lazy=True" make two separate
- # lazy loaders. Currently the LRU cache is local to the LazyLoader,
- # however add ourselves to the initial cache key just to future
- # proof in case it moves
- q = self._bakery(lambda session: session.query(self.entity), self)
-
- q.add_criteria(
- lambda q: q._with_invoke_all_eagers(False), self.parent_property,
+ stmt = sql.lambda_stmt(
+ lambda: sql.select(self.entity)
+ .apply_labels()
+ ._set_compile_options(ORMCompileState.default_compile_options),
+ global_track_bound_values=False,
+ lambda_cache=self._query_cache,
+ track_on=(self,),
)
if not self.parent_property.bake_queries:
- q.spoil(full=True)
+ stmt = stmt.spoil()
+
+ load_options = QueryContext.default_load_options
+
+ load_options += {
+ "_invoke_all_eagers": False,
+ "_lazy_loaded_from": state,
+ }
if self.parent_property.secondary is not None:
- q.add_criteria(
- lambda q: q.select_from(
- self.mapper, self.parent_property.secondary
- )
+ stmt += lambda stmt: stmt.select_from(
+ self.mapper, self.parent_property.secondary
)
pending = not state.key
# don't autoflush on pending
if pending or passive & attributes.NO_AUTOFLUSH:
- q.add_criteria(lambda q: q.autoflush(False))
+ stmt += lambda stmt: stmt.execution_options(autoflush=False)
if state.load_options:
- # here, if any of the options cannot return a cache key,
- # the BakedQuery "spoils" and caching will not occur. a path
- # that features Cls.attribute.of_type(some_alias) will cancel
- # caching, for example, since "some_alias" is user-defined and
- # is usually a throwaway object.
+
effective_path = state.load_path[self.parent_property]
- q._add_lazyload_options(state.load_options, effective_path)
+ opts = list(state.load_options)
+
+ stmt += lambda stmt: stmt.options(*opts)
+ stmt += lambda stmt: stmt._update_compile_options(
+ {"_current_path": effective_path}
+ )
if self.use_get:
if self._raise_on_sql:
self._invoke_raise_load(state, passive, "raise_on_sql")
- return (
- q(session)
- .with_post_criteria(lambda q: q._set_lazyload_from(state))
- ._load_on_pk_identity(
- session, session.query(self.mapper), primary_key_identity
- )
+ return loading.load_on_pk_identity(
+ session,
+ stmt,
+ primary_key_identity,
+ load_options=load_options,
+ execution_options={"compiled_cache": self._query_cache},
)
if self._order_by:
- q.add_criteria(lambda q: q.order_by(*self._order_by))
+ stmt += lambda stmt: stmt.order_by(*self._order_by)
def _lazyload_reverse(compile_context):
for rev in self.parent_property._reverse_property:
]
).lazyload(rev.key).process_compile_state(compile_context)
- q.add_criteria(
- lambda q: q._add_context_option(
- _lazyload_reverse, self.parent_property
- )
+ stmt += lambda stmt: stmt._add_context_option(
+ _lazyload_reverse, self.parent_property
)
lazy_clause, params = self._generate_lazy_clause(state, passive)
+
+ execution_options = {
+ "_sa_orm_load_options": load_options,
+ }
+ if not self.parent_property.bake_queries:
+ execution_options["compiled_cache"] = None
+
if self.key in state.dict:
return attributes.ATTR_WAS_SET
if self._raise_on_sql:
self._invoke_raise_load(state, passive, "raise_on_sql")
- q.add_criteria(lambda q: q.filter(lazy_clause))
-
- # set parameters in the query such that we don't overwrite
- # parameters that are already set within it
- def set_default_params(q):
- params.update(q.load_options._params)
- q.load_options += {"_params": params}
- return q
+ stmt = stmt.add_criteria(
+ lambda stmt: stmt.where(lazy_clause), enable_tracking=False
+ )
- result = (
- q(session)
- .with_post_criteria(lambda q: q._set_lazyload_from(state))
- .with_post_criteria(set_default_params)
- .all()
+ result = session.execute(
+ stmt, params, future=True, execution_options=execution_options
)
+
+ result = result.unique().scalars().all()
+
if self.uselist:
return result
else:
"session",
"execution_options",
"load_options",
+ "params",
"subq",
"_data",
)
self.session = context.session
self.execution_options = context.execution_options
self.load_options = context.load_options
+ self.params = context.params or {}
self.subq = subq
self._data = None
# to work with baked query, the parameters may have been
# updated since this query was created, so take these into account
- rows = list(q.params(self.load_options._params))
+ rows = list(q.params(self.params))
for k, v in itertools.groupby(rows, lambda x: x[1:]):
self._data[k].extend(vv[0] for vv in v)
orig_query, "orm"
)
- # this would create the full blown compile state, which we don't
- # need
- # orig_compile_state = compile_state_cls.create_for_statement(
- # orig_query, None)
-
if orig_query._is_lambda_element:
- util.warn(
- 'subqueryloader for "%s" must invoke lambda callable at %r in '
- "order to produce a new query, decreasing the efficiency "
- "of caching for this statement. Consider using "
- "selectinload() for more effective full-lambda caching"
- % (self, orig_query)
- )
+ if context.load_options._lazy_loaded_from is None:
+ util.warn(
+ 'subqueryloader for "%s" must invoke lambda callable '
+ "at %r in "
+ "order to produce a new query, decreasing the efficiency "
+ "of caching for this statement. Consider using "
+ "selectinload() for more effective full-lambda caching"
+ % (self, orig_query)
+ )
orig_query = orig_query._resolved
# this is the more "quick" version, however it's not clear how
else:
# all other cases are innerjoin=='nested' approach
eagerjoin = self._splice_nested_inner_join(
- path, towrap, clauses, onclause
+ path, towrap, clauses, onclause,
)
compile_state.eager_joins[query_entity_key] = eagerjoin
assert isinstance(join_obj, orm_util._ORMJoin)
elif isinstance(join_obj, sql.selectable.FromGrouping):
return self._splice_nested_inner_join(
- path, join_obj.element, clauses, onclause, splicing
+ path, join_obj.element, clauses, onclause, splicing,
)
elif not isinstance(join_obj, orm_util._ORMJoin):
if path[-2] is splicing:
return None
target_join = self._splice_nested_inner_join(
- path, join_obj.right, clauses, onclause, join_obj._right_memo
+ path, join_obj.right, clauses, onclause, join_obj._right_memo,
)
if target_join is None:
right_splice = False
target_join = self._splice_nested_inner_join(
- path, join_obj.left, clauses, onclause, join_obj._left_memo
+ path, join_obj.left, clauses, onclause, join_obj._left_memo,
)
if target_join is None:
# should only return None when recursively called,
"_parent_alias",
"_query_info",
"_fallback_query_info",
- "_bakery",
+ "_query_cache",
)
query_info = collections.namedtuple(
(("lazy", "select"),)
).init_class_attribute(mapper)
- @util.preload_module("sqlalchemy.ext.baked")
- def _memoized_attr__bakery(self):
- return util.preloaded.ext_baked.bakery(size=50)
+ def _memoized_attr__query_cache(self):
+ return util.LRUCache(30)
def create_row_processor(
self,
with_poly_entity = path_w_prop.get(
context.attributes, "path_with_polymorphic", None
)
-
if with_poly_entity is not None:
- effective_entity = with_poly_entity
+ effective_entity = inspect(with_poly_entity)
else:
effective_entity = self.entity
# we need to adapt our "pk_cols" and "in_expr" to that
# entity. in non-"omit join" mode, these are against the
# parent entity and do not need adaption.
- insp = inspect(effective_entity)
- if insp.is_aliased_class:
- pk_cols = [insp._adapt_element(col) for col in pk_cols]
- in_expr = insp._adapt_element(in_expr)
- pk_cols = [insp._adapt_element(col) for col in pk_cols]
-
- q = self._bakery(
- lambda session: session.query(
+ if effective_entity.is_aliased_class:
+ pk_cols = [
+ effective_entity._adapt_element(col) for col in pk_cols
+ ]
+ in_expr = effective_entity._adapt_element(in_expr)
+
+ q = sql.lambda_stmt(
+ lambda: sql.select(
orm_util.Bundle("pk", *pk_cols), effective_entity
- ),
- self,
+ ).apply_labels(),
+ lambda_cache=self._query_cache,
+ global_track_bound_values=False,
+ track_on=(self, effective_entity,) + tuple(pk_cols),
)
+ if not self.parent_property.bake_queries:
+ q = q.spoil()
+
if not query_info.load_with_join:
# the Bundle we have in the "omit_join" case is against raw, non
# annotated columns, so to ensure the Query knows its primary
# entity, we add it explicitly. If we made the Bundle against
# annotated columns, we hit a performance issue in this specific
# case, which is detailed in issue #4347.
- q.add_criteria(lambda q: q.select_from(effective_entity))
+ q = q.add_criteria(lambda q: q.select_from(effective_entity))
else:
# in the non-omit_join case, the Bundle is against the annotated/
# mapped column of the parent entity, but the #4347 issue does not
# occur in this case.
pa = self._parent_alias
- q.add_criteria(
+ q = q.add_criteria(
lambda q: q.select_from(pa).join(
getattr(pa, self.parent_property.key).of_type(
effective_entity
)
)
- if query_info.load_only_child:
- q.add_criteria(
- lambda q: q.filter(
- in_expr.in_(sql.bindparam("primary_keys", expanding=True))
- )
- )
- else:
- q.add_criteria(
- lambda q: q.filter(
- in_expr.in_(sql.bindparam("primary_keys", expanding=True))
- )
- )
+ q = q.add_criteria(
+ lambda q: q.filter(in_expr.in_(sql.bindparam("primary_keys")))
+ )
# a test which exercises what these comments talk about is
# test_selectin_relations.py -> test_twolevel_selectin_w_polymorphic
# that query will be in terms of the effective entity we were just
# handed.
#
- # But now the selectinload/ baked query we are running is *also*
+ # But now the selectinload query we are running is *also*
# cached. What if it's cached and running from some previous iteration
# of that AliasedInsp? Well in that case it will also use the previous
- # iteration of the loader options. If the baked query expires and
+ # iteration of the loader options. If the query expires and
# gets generated again, it will be handed the current effective_entity
# and the current _with_options, again in terms of whatever
# compile_state.select_statement happens to be right now, so the
# query will still be internally consistent and loader callables
# will be correctly invoked.
- q._add_lazyload_options(
- orig_query._with_options, path[self.parent_property]
+ effective_path = path[self.parent_property]
+
+ options = orig_query._with_options
+ q = q.add_criteria(
+ lambda q: q.options(*options)._update_compile_options(
+ {"_current_path": effective_path}
+ )
)
if context.populate_existing:
- q.add_criteria(lambda q: q.populate_existing())
+ q = q.add_criteria(
+ lambda q: q.execution_options(populate_existing=True)
+ )
if self.parent_property.order_by:
if not query_info.load_with_join:
eager_order_by = self.parent_property.order_by
- if insp.is_aliased_class:
+ if effective_entity.is_aliased_class:
eager_order_by = [
- insp._adapt_element(elem) for elem in eager_order_by
+ effective_entity._adapt_element(elem)
+ for elem in eager_order_by
]
- q.add_criteria(lambda q: q.order_by(*eager_order_by))
+ q = q.add_criteria(lambda q: q.order_by(*eager_order_by))
else:
def _setup_outermost_orderby(compile_context):
util.to_list(self.parent_property.order_by)
)
- q.add_criteria(
+ q = q.add_criteria(
lambda q: q._add_context_option(
_setup_outermost_orderby, self.parent_property
)
our_keys = our_keys[self._chunksize :]
data = {
k: v
- for k, v in q(context.session).params(
- primary_keys=[
- key[0] if query_info.zero_idx else key for key in chunk
- ]
- )
+ for k, v in context.session.execute(
+ q,
+ params={
+ "primary_keys": [
+ key[0] if query_info.zero_idx else key
+ for key in chunk
+ ]
+ },
+ future=True,
+ ).unique()
}
for key in chunk:
data = collections.defaultdict(list)
for k, v in itertools.groupby(
- q(context.session).params(primary_keys=primary_keys),
+ context.session.execute(
+ q, params={"primary_keys": primary_keys}, future=True
+ ).unique(),
lambda x: x[0],
):
data[k].extend(vv[1] for vv in v)
self, attr, strategy, propagate_to_loaders=True
):
strategy = self._coerce_strat(strategy)
-
self.propagate_to_loaders = propagate_to_loaders
cloned = self._clone_for_bind_strategy(attr, strategy, "relationship")
self.path = cloned.path
if attr:
path = path + (attr,)
self.path = path
+
return path
def __getstate__(self):
return {}
@util.memoized_property
- def columns(self):
+ def _all_column_expressions(self):
if self._is_with_polymorphic:
cols_plus_keys = self.mapper._columns_plus_keys(
[ent.mapper for ent in self._with_polymorphic_entities]
from . import roles
from .traversals import HasCacheKey # noqa
+from .traversals import HasCopyInternals # noqa
from .traversals import MemoizedHasCacheKey # noqa
from .visitors import ClauseVisitor
from .visitors import ExtendedInternalTraversal
return HasCacheKey._generate_cache_key_for_object(self)
+class ExecutableOption(HasCopyInternals, HasCacheKey):
+ _annotations = util.EMPTY_DICT
+
+ __visit_name__ = "executable_option"
+
+ def _clone(self):
+ """Create a shallow copy of this ExecutableOption.
+
+ """
+ c = self.__class__.__new__(self.__class__)
+ c.__dict__ = dict(self.__dict__)
+ return c
+
+
class Executable(Generative):
"""Mark a :class:`_expression.ClauseElement` as supporting execution.
_with_context_options = ()
_executable_traverse_internals = [
- ("_with_options", ExtendedInternalTraversal.dp_has_cache_key_list),
+ ("_with_options", InternalTraversal.dp_executable_options),
("_with_context_options", ExtendedInternalTraversal.dp_plain_obj),
("_propagate_attrs", ExtendedInternalTraversal.dp_propagate_attrs),
]
+ is_select = False
+ is_update = False
+ is_insert = False
+ is_text = False
+ is_delete = False
+ is_dml = False
+
@property
def _effective_plugin_target(self):
return self.__visit_name__
coercions.expect(roles.HasCacheKeyRole, opt) for opt in options
)
+ @_generative
+ def _set_compile_options(self, compile_options):
+ """Assign the compile options to a new value.
+
+ :param compile_options: appropriate CacheableOptions structure
+
+ """
+
+ self._compile_options = compile_options
+
+ @_generative
+ def _update_compile_options(self, options):
+ """update the _compile_options with new keys."""
+
+ self._compile_options += options
+
@_generative
def _add_context_option(self, callable_, cache_args):
"""Add a context option to this statement.
message = template.format(
froms=froms_str, start=self.froms[start_with]
)
+
util.warn(message)
self.cache_key = cache_key
if cache_key:
- self._cache_key_bind_match = {b: b for b in cache_key[1]}
+ self._cache_key_bind_match = ckbm = {
+ b.key: b for b in cache_key[1]
+ }
+ ckbm.update({b: b for b in cache_key[1]})
# compile INSERT/UPDATE defaults/sequences to expect executemany
# style execution, which may mean no pre-execute of defaults,
# key was been generated.
ckbm = self._cache_key_bind_match
if ckbm:
- ckbm.update({bp: bindparam for bp in bindparam._cloned_set})
+ for bp in bindparam._cloned_set:
+ if bp.key in ckbm:
+ cb = ckbm[bp.key]
+ ckbm[cb] = bindparam
if bindparam.isoutparam:
self.has_out_parameters = True
expanding=bindparam.expanding,
**kwargs
)
+
if bindparam.expanding:
ret = "(%s)" % ret
return ret
_hints = util.immutabledict()
named_with_column = False
+ is_dml = True
+
@classmethod
def _constructor_20_deprecations(cls, fn_name, clsname, names):
select = None
include_insert_from_select_defaults = False
+ is_insert = True
+
_traverse_internals = (
[
("table", InternalTraversal.dp_clauseelement),
__visit_name__ = "update"
+ is_update = True
+
_traverse_internals = (
[
("table", InternalTraversal.dp_clauseelement),
__visit_name__ = "delete"
+ is_delete = True
+
_traverse_internals = (
[
("table", InternalTraversal.dp_clauseelement),
# sql/lambdas.py
-# Copyright (C) 2005-2019 the SQLAlchemy authors and contributors
+# Copyright (C) 2005-2020 the SQLAlchemy authors and contributors
# <see AUTHORS file>
#
# This module is part of SQLAlchemy and is released under
class SelectInEagerLoadTest(NoCache, fixtures.MappedTest):
- """basic test for selectin() loading, which uses a baked query.
+ """basic test for selectin() loading, which uses a lambda query.
- if the baked query starts spoiling due to some bug in cache keys,
- this callcount blows up.
+ For the previous "baked query" version of this, statement caching
+ was still taking effect as the selectinloader used its own baked
+ query cache. in 1.4 we align the loader caches with the global
+ "cache_size" (tenatitively) so the callcount has gone up to accommodate
+ for 3x the compilations.
"""
r.context.compiled.compile_state = compile_state
obj = ORMCompileState.orm_setup_cursor_result(
- sess, compile_state.statement, exec_opts, {}, r,
+ sess, compile_state.statement, {}, exec_opts, {}, r,
)
list(obj)
sess.close()
from sqlalchemy import event
from sqlalchemy import exc as sa_exc
from sqlalchemy import func
-from sqlalchemy import literal_column
from sqlalchemy import testing
from sqlalchemy.ext import baked
-from sqlalchemy.orm import aliased
-from sqlalchemy.orm import backref
-from sqlalchemy.orm import defaultload
from sqlalchemy.orm import exc as orm_exc
-from sqlalchemy.orm import lazyload
-from sqlalchemy.orm import Load
from sqlalchemy.orm import mapper
from sqlalchemy.orm import relationship
from sqlalchemy.orm import Session
from sqlalchemy.testing import is_
from sqlalchemy.testing import is_not_
from sqlalchemy.testing import mock
-from sqlalchemy.testing.assertsql import CompiledSQL
from test.orm import _fixtures
self.assert_sql_count(testing.db, go, 2)
-class LazyLoaderTest(testing.AssertsCompiledSQL, BakedTest):
- run_setup_mappers = "each"
-
- @testing.fixture
- def modify_query_fixture(self):
- def set_event(bake_ok):
-
- event.listen(
- Query,
- "before_compile",
- _modify_query,
- retval=True,
- bake_ok=bake_ok,
- )
- return m1
-
- m1 = mock.Mock()
-
- def _modify_query(query):
- m1(query.column_descriptions[0]["entity"])
- query = query.enable_assertions(False).filter(
- literal_column("1") == 1
- )
- return query
-
- yield set_event
- event.remove(Query, "before_compile", _modify_query)
-
- def _o2m_fixture(self, lazy="select", **kw):
- User = self.classes.User
- Address = self.classes.Address
-
- mapper(
- User,
- self.tables.users,
- properties={
- "addresses": relationship(
- Address,
- order_by=self.tables.addresses.c.id,
- lazy=lazy,
- **kw
- )
- },
- )
- mapper(Address, self.tables.addresses)
- return User, Address
-
- def _o2m_twolevel_fixture(self, lazy="select", **kw):
- User = self.classes.User
- Address = self.classes.Address
- Dingaling = self.classes.Dingaling
-
- mapper(
- User,
- self.tables.users,
- properties={
- "addresses": relationship(
- Address,
- order_by=self.tables.addresses.c.id,
- lazy=lazy,
- **kw
- )
- },
- )
- mapper(
- Address,
- self.tables.addresses,
- properties={"dingalings": relationship(Dingaling, lazy=lazy)},
- )
- mapper(Dingaling, self.tables.dingalings)
- return User, Address, Dingaling
-
- def _o2m_threelevel_fixture(self, lazy="select", **kw):
- Order = self.classes.Order
- User = self.classes.User
- Address = self.classes.Address
- Dingaling = self.classes.Dingaling
-
- mapper(
- Order, self.tables.orders, properties={"user": relationship(User)}
- )
-
- mapper(
- User,
- self.tables.users,
- properties={
- "addresses": relationship(
- Address,
- order_by=self.tables.addresses.c.id,
- lazy=lazy,
- **kw
- )
- },
- )
- mapper(
- Address,
- self.tables.addresses,
- properties={"dingalings": relationship(Dingaling, lazy=lazy)},
- )
- mapper(Dingaling, self.tables.dingalings)
- return Order, User, Address, Dingaling
-
- def _m2o_fixture(self):
- User = self.classes.User
- Address = self.classes.Address
-
- mapper(User, self.tables.users)
- mapper(
- Address,
- self.tables.addresses,
- properties={"user": relationship(User)},
- )
- return User, Address
-
- def test_same_lazyload_multiple_paths(self):
- """this is an initial test that requires the presence of
- the ResultMetaData._adapt_to_context method.
-
- Both queries emit a lazyload against User.addresses that then
- includes a subqueryload against Address.dingalings.
-
- The subqueryload produces an adapted query that has anonymized
- columns. for these to be locatable in the result, the columns in
- the adapted query have to match up to the result map, which is not
- the case if the statement is pulled from the cache.
-
- The query is done in two different ways so that one lazyload will not
- have a state.load_path on User, and the other one will. this means
- there will be two different baked queries that both produce the same
- Core SELECT statement. Using logical cache keys in Core, rather than
- based on identity, means two different BakedContext objects will use
- the same Core context. For this reason, at result fetch time the
- ResultMetaData._adapt_to_context step is required so that the ORM can
- still locate the columns.
-
- This will all be done differently with direct ORM results, however this
- same issue that ResultMetaData has to be adapted to the live statement
- will exist indepndently of baked queries. More statement-level caching
- tests need to be added which test with complex statements that include
- anonymous columns.
-
- """
- Order, User, Address, Dingaling = self._o2m_threelevel_fixture()
-
- sess = Session()
- from sqlalchemy.orm import joinedload
-
- o1 = (
- sess.query(Order)
- .options(
- joinedload(Order.user)
- .lazyload(User.addresses)
- .subqueryload(Address.dingalings)
- )
- .filter(Order.id == 2)
- .first()
- )
- assert o1.user.addresses[0].dingalings
-
- sess.expire_all()
-
- u1 = (
- sess.query(User)
- .options(lazyload(User.addresses).subqueryload(Address.dingalings))
- .filter(User.id == 9)
- .first()
- )
- assert u1.addresses[0].dingalings
-
- def test_no_cache_for_event(self, modify_query_fixture):
-
- m1 = modify_query_fixture(False)
-
- User, Address = self._o2m_fixture()
-
- sess = Session()
- u1 = sess.query(User).filter(User.id == 7).first()
-
- u1.addresses
-
- eq_(m1.mock_calls, [mock.call(User), mock.call(Address)])
-
- sess.expire(u1, ["addresses"])
-
- u1.addresses
- eq_(
- m1.mock_calls,
- [mock.call(User), mock.call(Address), mock.call(Address)],
- )
-
- def test_cache_ok_for_event(self, modify_query_fixture):
-
- m1 = modify_query_fixture(True)
-
- User, Address = self._o2m_fixture()
-
- sess = Session()
- u1 = sess.query(User).filter(User.id == 7).first()
-
- u1.addresses
-
- eq_(m1.mock_calls, [mock.call(User), mock.call(Address)])
-
- sess.expire(u1, ["addresses"])
-
- u1.addresses
- eq_(m1.mock_calls, [mock.call(User), mock.call(Address)])
-
- def test_aliased_unbound_are_now_safe_to_cache(self):
- User, Address, Dingaling = self._o2m_twolevel_fixture(lazy="joined")
-
- class SubDingaling(Dingaling):
- pass
-
- mapper(SubDingaling, None, inherits=Dingaling)
-
- lru = Address.dingalings.property._lazy_strategy._bakery(
- lambda q: None
- )._bakery
- l1 = len(lru)
- for i in range(5):
- sess = Session()
- u1 = (
- sess.query(User)
- .options(
- defaultload(User.addresses).lazyload(
- Address.dingalings.of_type(aliased(SubDingaling))
- )
- )
- .first()
- )
- for ad in u1.addresses:
- ad.dingalings
- l2 = len(lru)
- eq_(l1, 0)
- eq_(l2, 2)
-
- def test_aliased_bound_are_now_safe_to_cache(self):
- User, Address, Dingaling = self._o2m_twolevel_fixture(lazy="joined")
-
- class SubDingaling(Dingaling):
- pass
-
- mapper(SubDingaling, None, inherits=Dingaling)
-
- lru = Address.dingalings.property._lazy_strategy._bakery(
- lambda q: None
- )._bakery
- l1 = len(lru)
- for i in range(5):
- sess = Session()
- u1 = (
- sess.query(User)
- .options(
- Load(User)
- .defaultload(User.addresses)
- .lazyload(
- Address.dingalings.of_type(aliased(SubDingaling))
- )
- )
- .first()
- )
- for ad in u1.addresses:
- ad.dingalings
- l2 = len(lru)
- eq_(l1, 0)
- eq_(l2, 2)
-
- def test_safe_unbound_option_allows_bake(self):
- User, Address, Dingaling = self._o2m_twolevel_fixture(lazy="joined")
-
- lru = Address.dingalings.property._lazy_strategy._bakery(
- lambda q: None
- )._bakery
- l1 = len(lru)
- for i in range(5):
- sess = Session()
- u1 = (
- sess.query(User)
- .options(
- defaultload(User.addresses).lazyload(Address.dingalings)
- )
- .first()
- )
- for ad in u1.addresses:
- ad.dingalings
- l2 = len(lru)
- eq_(l1, 0)
- eq_(l2, 2)
-
- def test_safe_bound_option_allows_bake(self):
- User, Address, Dingaling = self._o2m_twolevel_fixture(lazy="joined")
-
- lru = Address.dingalings.property._lazy_strategy._bakery(
- lambda q: None
- )._bakery
- l1 = len(lru)
- for i in range(5):
- sess = Session()
- u1 = (
- sess.query(User)
- .options(
- Load(User)
- .defaultload(User.addresses)
- .lazyload(Address.dingalings)
- )
- .first()
- )
- for ad in u1.addresses:
- ad.dingalings
- l2 = len(lru)
- eq_(l1, 0)
- eq_(l2, 2)
-
- def test_baked_lazy_loading_relationship_flag_true(self):
- self._test_baked_lazy_loading_relationship_flag(True)
-
- def test_baked_lazy_loading_relationship_flag_false(self):
- self._test_baked_lazy_loading_relationship_flag(False)
-
- def _test_baked_lazy_loading_relationship_flag(self, flag):
- User, Address = self._o2m_fixture(bake_queries=flag)
- from sqlalchemy import inspect
- from sqlalchemy.orm.interfaces import UserDefinedOption
-
- address_mapper = inspect(Address)
- sess = Session(testing.db)
-
- # there's no event in the compile process either at the ORM
- # or core level and it is not easy to patch. the option object
- # is the one thing that will get carried into the lazyload from the
- # outside and invoked on a per-compile basis
-
- class MockOpt(UserDefinedOption):
- _is_compile_state = True
- propagate_to_loaders = True
- _is_legacy_option = True
-
- def _gen_cache_key(self, *args):
- return ("hi",)
-
- def _generate_path_cache_key(self, *args):
- return ("hi",)
-
- def _generate_cache_key(self, *args):
- return (("hi",), [])
-
- _mock = mock.Mock()
-
- def process_query(self, *args):
- self._mock.process_query(*args)
-
- def process_query_conditionally(self, *args):
- self._mock.process_query_conditionally(*args)
-
- def process_compile_state(self, *args):
- self._mock.process_compile_state(*args)
-
- def orm_execute(self):
- self._mock.orm_execute()
-
- @property
- def mock_calls(self):
- return self._mock.mock_calls
-
- mock_opt = MockOpt()
-
- u1 = sess.query(User).options(mock_opt).first()
-
- @event.listens_for(sess, "do_orm_execute")
- def _my_compile_state(context):
- if (
- context.statement._raw_columns[0]._annotations["parententity"]
- is address_mapper
- ):
- mock_opt.orm_execute()
-
- u1.addresses
-
- sess.expire(u1)
- u1.addresses
-
- if flag:
- eq_(
- mock_opt.mock_calls,
- [
- mock.call.process_query(mock.ANY),
- mock.call.process_compile_state(mock.ANY), # query.first()
- mock.call.process_query_conditionally(mock.ANY),
- mock.call.orm_execute(), # lazyload addresses
- mock.call.process_compile_state(mock.ANY), # emit lazyload
- mock.call.process_compile_state(
- mock.ANY
- ), # load scalar attributes for user
- # lazyload addresses, no call to process_compile_state
- mock.call.orm_execute(),
- ],
- )
- else:
- eq_(
- mock_opt.mock_calls,
- [
- mock.call.process_query(mock.ANY),
- mock.call.process_compile_state(mock.ANY), # query.first()
- mock.call.process_query_conditionally(mock.ANY),
- mock.call.orm_execute(), # lazyload addresses
- mock.call.process_compile_state(mock.ANY), # emit_lazyload
- mock.call.process_compile_state(
- mock.ANY
- ), # load_scalar_attributes for user
- mock.call.process_query_conditionally(mock.ANY),
- mock.call.orm_execute(), # lazyload addresses
- mock.call.process_compile_state(
- mock.ANY
- ), # emit_lazyload, here the query was not cached
- ],
- )
-
- def test_baked_lazy_loading_option_o2m(self):
- User, Address = self._o2m_fixture()
- self._test_baked_lazy_loading(set_option=True)
-
- def test_baked_lazy_loading_mapped_o2m(self):
- User, Address = self._o2m_fixture(lazy="baked_select")
- self._test_baked_lazy_loading(set_option=False)
-
- def _test_baked_lazy_loading(self, set_option):
- User, Address = self.classes.User, self.classes.Address
-
- base_bq = self.bakery(lambda s: s.query(User))
-
- if set_option:
- base_bq += lambda q: q.options(lazyload(User.addresses))
-
- base_bq += lambda q: q.order_by(User.id)
-
- assert_result = self.static.user_address_result
-
- for i in range(4):
- for cond1, cond2 in itertools.product(
- *[(False, True) for j in range(2)]
- ):
-
- bq = base_bq._clone()
- sess = Session()
-
- if cond1:
- bq += lambda q: q.filter(User.name == "jack")
- else:
- bq += lambda q: q.filter(User.name.like("%ed%"))
-
- if cond2:
- ct = func.count(Address.id).label("count")
- subq = (
- sess.query(ct, Address.user_id)
- .group_by(Address.user_id)
- .having(ct > 2)
- .subquery()
- )
-
- bq += lambda q: q.join(subq)
-
- if cond2:
- if cond1:
-
- def go():
- result = bq(sess).all()
- eq_([], result)
-
- self.assert_sql_count(testing.db, go, 1)
- else:
-
- def go():
- result = bq(sess).all()
- eq_(assert_result[1:2], result)
-
- self.assert_sql_count(testing.db, go, 2)
- else:
- if cond1:
-
- def go():
- result = bq(sess).all()
- eq_(assert_result[0:1], result)
-
- self.assert_sql_count(testing.db, go, 2)
- else:
-
- def go():
- result = bq(sess).all()
- eq_(assert_result[1:3], result)
-
- self.assert_sql_count(testing.db, go, 3)
-
- sess.close()
-
- def test_baked_lazy_loading_m2o(self):
- User, Address = self._m2o_fixture()
-
- base_bq = self.bakery(lambda s: s.query(Address))
-
- base_bq += lambda q: q.options(lazyload(Address.user))
- base_bq += lambda q: q.order_by(Address.id)
-
- assert_result = self.static.address_user_result
-
- for i in range(4):
- for cond1 in (False, True):
- bq = base_bq._clone()
-
- sess = Session()
-
- if cond1:
- bq += lambda q: q.filter(
- Address.email_address == "jack@bean.com"
- )
- else:
- bq += lambda q: q.filter(
- Address.email_address.like("ed@%")
- )
-
- if cond1:
-
- def go():
- result = bq(sess).all()
- eq_(assert_result[0:1], result)
-
- self.assert_sql_count(testing.db, go, 2)
- else:
-
- def go():
- result = bq(sess).all()
- eq_(assert_result[1:4], result)
-
- self.assert_sql_count(testing.db, go, 2)
-
- sess.close()
-
- def test_useget_cancels_eager(self):
- """test that a one to many lazyload cancels the unnecessary
- eager many-to-one join on the other side."""
-
- User = self.classes.User
- Address = self.classes.Address
-
- mapper(User, self.tables.users)
- mapper(
- Address,
- self.tables.addresses,
- properties={
- "user": relationship(
- User,
- lazy="joined",
- backref=backref("addresses", lazy="baked_select"),
- )
- },
- )
-
- sess = Session()
- u1 = sess.query(User).filter(User.id == 8).one()
-
- def go():
- eq_(u1.addresses[0].user, u1)
-
- self.assert_sql_execution(
- testing.db,
- go,
- CompiledSQL(
- "SELECT addresses.id AS addresses_id, addresses.user_id AS "
- "addresses_user_id, addresses.email_address AS "
- "addresses_email_address FROM addresses WHERE :param_1 = "
- "addresses.user_id",
- {"param_1": 8},
- ),
- )
-
- def test_useget_cancels_eager_propagated_present(self):
- """test that a one to many lazyload cancels the unnecessary
- eager many-to-one join on the other side, even when a propagated
- option is present."""
-
- User = self.classes.User
- Address = self.classes.Address
-
- mapper(User, self.tables.users)
- mapper(
- Address,
- self.tables.addresses,
- properties={
- "user": relationship(
- User,
- lazy="joined",
- backref=backref("addresses", lazy="baked_select"),
- )
- },
- )
-
- from sqlalchemy.orm.interfaces import MapperOption
-
- class MyBogusOption(MapperOption):
- propagate_to_loaders = True
-
- sess = Session()
- u1 = (
- sess.query(User)
- .options(MyBogusOption())
- .filter(User.id == 8)
- .one()
- )
-
- def go():
- eq_(u1.addresses[0].user, u1)
-
- self.assert_sql_execution(
- testing.db,
- go,
- CompiledSQL(
- "SELECT addresses.id AS addresses_id, addresses.user_id AS "
- "addresses_user_id, addresses.email_address AS "
- "addresses_email_address FROM addresses WHERE :param_1 = "
- "addresses.user_id",
- {"param_1": 8},
- ),
- )
-
- def test_simple_lazy_clause_no_race_on_generate(self):
- User, Address = self._o2m_fixture()
-
- (
- expr1,
- paramdict1,
- ) = User.addresses.property._lazy_strategy._simple_lazy_clause
-
- # delete the attr, as though a concurrent thread is also generating it
- del User.addresses.property._lazy_strategy._simple_lazy_clause
- (
- expr2,
- paramdict2,
- ) = User.addresses.property._lazy_strategy._simple_lazy_clause
-
- eq_(paramdict1, paramdict2)
-
- # additional tests:
- # 1. m2m w lazyload
- # 2. o2m lazyload where m2o backrefs have an eager load, test
- # that eager load is canceled out
- # 3. uselist = False, uselist=False assertion
-
-
# assert that the integration style illustrated in the dogpile.cache
# example works w/ baked
class CustomIntegrationTest(testing.AssertsCompiledSQL, BakedTest):
)
def test_self_referential_two(self):
+
sess = create_session()
palias = aliased(Person)
expected = [(m1, e1), (m1, e2), (m1, b1)]
expected,
)
+ def test_self_referential_two_point_five(self):
+ """Using two aliases, the above case works.
+ """
+ sess = create_session()
+ palias = aliased(Person)
+ palias2 = aliased(Person)
+
+ expected = [(m1, e1), (m1, e2), (m1, b1)]
+
+ eq_(
+ sess.query(palias, palias2)
+ .filter(palias.company_id == palias2.company_id)
+ .filter(palias.name == "dogbert")
+ .filter(palias.person_id > palias2.person_id)
+ .from_self()
+ .order_by(palias.person_id, palias2.person_id)
+ .all(),
+ expected,
+ )
+
def test_self_referential_two_future(self):
+ # TODO: this is the SECOND test *EVER* of an aliased class of
+ # an aliased class.
+ sess = create_session(testing.db, future=True)
+ expected = [(m1, e1), (m1, e2), (m1, b1)]
+
+ # not aliasing the first class
+ p1 = Person
+ p2 = aliased(Person)
+ stmt = (
+ select(p1, p2)
+ .filter(p1.company_id == p2.company_id)
+ .filter(p1.name == "dogbert")
+ .filter(p1.person_id > p2.person_id)
+ )
+
+ subq = stmt.subquery()
+
+ pa1 = aliased(p1, subq)
+ pa2 = aliased(p2, subq)
+
+ stmt2 = select(pa1, pa2).order_by(pa1.person_id, pa2.person_id)
+
+ eq_(
+ sess.execute(stmt2).unique().all(), expected,
+ )
+
+ def test_self_referential_two_point_five_future(self):
+
# TODO: this is the first test *EVER* of an aliased class of
# an aliased class. we should add many more tests for this.
# new case added in Id810f485c5f7ed971529489b84694e02a3356d6d
sess = create_session(testing.db, future=True)
expected = [(m1, e1), (m1, e2), (m1, b1)]
+ # aliasing the first class
p1 = aliased(Person)
p2 = aliased(Person)
stmt = (
pa1 = aliased(p1, subq)
pa2 = aliased(p2, subq)
- stmt = select(pa1, pa2).order_by(pa1.person_id, pa2.person_id)
+ stmt2 = select(pa1, pa2).order_by(pa1.person_id, pa2.person_id)
eq_(
- sess.execute(stmt).unique().all(), expected,
+ sess.execute(stmt2).unique().all(), expected,
)
def test_nesting_queries(self):
Parent, Base1, Base2, Sub1, Sub2, EP1, EP2 = self._classes()
s = Session()
+
+ # as of from_self() changing in
+ # I3abfb45dd6e50f84f29d39434caa0b550ce27864,
+ # this query is coming out instead which is equivalent, but not
+ # totally sure where this happens
+
self.assert_compile(
s.query(Sub2).from_self().join(Sub2.ep1).join(Sub2.ep2),
"SELECT anon_1.sub2_id AS anon_1_sub2_id, "
- "anon_1.base2_id AS anon_1_base2_id, "
"anon_1.base2_base1_id AS anon_1_base2_base1_id, "
"anon_1.base2_data AS anon_1_base2_data, "
"anon_1.sub2_subdata AS anon_1_sub2_subdata "
"base2.base1_id AS base2_base1_id, base2.data AS base2_data, "
"sub2.subdata AS sub2_subdata "
"FROM base2 JOIN sub2 ON base2.id = sub2.id) AS anon_1 "
- "JOIN ep1 ON anon_1.base2_id = ep1.base2_id "
- "JOIN ep2 ON anon_1.base2_id = ep2.base2_id",
+ "JOIN ep1 ON anon_1.sub2_id = ep1.base2_id "
+ "JOIN ep2 ON anon_1.sub2_id = ep2.base2_id",
)
def test_seven(self):
Parent, Base1, Base2, Sub1, Sub2, EP1, EP2 = self._classes()
s = Session()
+
+ # as of from_self() changing in
+ # I3abfb45dd6e50f84f29d39434caa0b550ce27864,
+ # this query is coming out instead which is equivalent, but not
+ # totally sure where this happens
self.assert_compile(
# adding Sub2 to the entities list helps it,
# otherwise the joins for Sub2.ep1/ep2 don't have columns
"SELECT anon_1.parent_id AS anon_1_parent_id, "
"anon_1.parent_data AS anon_1_parent_data, "
"anon_1.sub2_id AS anon_1_sub2_id, "
- "anon_1.base2_id AS anon_1_base2_id, "
"anon_1.base2_base1_id AS anon_1_base2_base1_id, "
"anon_1.base2_data AS anon_1_base2_data, "
"anon_1.sub2_subdata AS anon_1_sub2_subdata "
"ON parent.id = sub1.parent_id JOIN "
"(base2 JOIN sub2 ON base2.id = sub2.id) "
"ON base1.id = base2.base1_id) AS anon_1 "
- "JOIN ep1 ON anon_1.base2_id = ep1.base2_id "
- "JOIN ep2 ON anon_1.base2_id = ep2.base2_id",
+ "JOIN ep1 ON anon_1.sub2_id = ep1.base2_id "
+ "JOIN ep2 ON anon_1.sub2_id = ep2.base2_id",
)
from sqlalchemy.orm import mapper
from sqlalchemy.orm import relationship
from sqlalchemy.orm import Session
-from sqlalchemy.orm.interfaces import MapperOption
from sqlalchemy.testing import assert_raises
from sqlalchemy.testing import eq_
from sqlalchemy.testing import fixtures
from sqlalchemy.testing import is_false
from sqlalchemy.testing import is_true
-from sqlalchemy.testing import mock
from sqlalchemy.testing.assertsql import CompiledSQL
from sqlalchemy.testing.schema import Column
from sqlalchemy.testing.schema import Table
fred = s.query(User).filter_by(name="fred").one()
eq_(fred.addresses, []) # fred is missing
- def test_custom_bind(self):
- Address, addresses, users, User = (
- self.classes.Address,
- self.tables.addresses,
- self.tables.users,
- self.classes.User,
- )
-
- mapper(
- User,
- users,
- properties=dict(
- addresses=relationship(
- mapper(Address, addresses),
- lazy="select",
- primaryjoin=and_(
- users.c.id == addresses.c.user_id,
- users.c.name == bindparam("name"),
- ),
- )
- ),
- )
-
- canary = mock.Mock()
-
- class MyOption(MapperOption):
- propagate_to_loaders = True
-
- def __init__(self, crit):
- self.crit = crit
-
- def process_query_conditionally(self, query):
- """process query during a lazyload"""
- canary()
- query.params.non_generative(query, dict(name=self.crit))
-
- s = Session()
- ed = s.query(User).options(MyOption("ed")).filter_by(name="ed").one()
- eq_(
- ed.addresses,
- [
- Address(id=2, user_id=8),
- Address(id=3, user_id=8),
- Address(id=4, user_id=8),
- ],
- )
- eq_(canary.mock_calls, [mock.call()])
-
- fred = (
- s.query(User).options(MyOption("ed")).filter_by(name="fred").one()
- )
- eq_(fred.addresses, []) # fred is missing
- eq_(canary.mock_calls, [mock.call(), mock.call()])
-
- # the lazy query was not cached; the option is re-applied to the
- # Fred object due to populate_existing()
- fred = (
- s.query(User)
- .populate_existing()
- .options(MyOption("fred"))
- .filter_by(name="fred")
- .one()
- )
- eq_(fred.addresses, [Address(id=5, user_id=9)]) # fred is there
-
- eq_(canary.mock_calls, [mock.call(), mock.call(), mock.call()])
-
def test_one_to_many_scalar(self):
Address, addresses, users, User = (
self.classes.Address,
go,
CompiledSQL(
"SELECT person.id AS person_id, person.city_id AS "
- "person_city_id FROM person "
- "WHERE person.city_id = :param_1 AND :param_2 = 0",
+ "person_city_id FROM person WHERE person.city_id = :param_1 "
+ "AND :param_2 = 0",
{"param_1": 2, "param_2": 1},
),
)
asserter.assert_(
CompiledSQL(
- "SELECT pets.id AS pets_id, pets.person_id "
- "AS pets_person_id FROM pets "
- "WHERE pets.person_id = CAST(:param_1 AS INTEGER)",
+ "SELECT pets.id AS pets_id, pets.person_id AS "
+ "pets_person_id FROM pets WHERE pets.person_id = "
+ "CAST(:param_1 AS INTEGER)",
[{"param_1": 5}],
)
)
# TEST: test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_w_annotation
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_w_annotation 2.7_sqlite_pysqlite_dbapiunicode_cextensions 45105
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_w_annotation 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 55905
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_w_annotation 3.8_sqlite_pysqlite_dbapiunicode_cextensions 49105
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_w_annotation 3.8_sqlite_pysqlite_dbapiunicode_nocextensions 60705
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_w_annotation 2.7_sqlite_pysqlite_dbapiunicode_cextensions 46105
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_w_annotation 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 57205
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_w_annotation 3.8_sqlite_pysqlite_dbapiunicode_cextensions 50105
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_w_annotation 3.8_sqlite_pysqlite_dbapiunicode_nocextensions 62005
# TEST: test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_wo_annotation
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_wo_annotation 2.7_sqlite_pysqlite_dbapiunicode_cextensions 44005
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_wo_annotation 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 54805
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_wo_annotation 3.8_sqlite_pysqlite_dbapiunicode_cextensions 48005
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_wo_annotation 3.8_sqlite_pysqlite_dbapiunicode_nocextensions 59605
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_wo_annotation 2.7_sqlite_pysqlite_dbapiunicode_cextensions 45005
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_wo_annotation 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 56105
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_wo_annotation 3.8_sqlite_pysqlite_dbapiunicode_cextensions 49005
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_bundle_wo_annotation 3.8_sqlite_pysqlite_dbapiunicode_nocextensions 60905
# TEST: test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_entity_w_annotations
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_entity_w_annotations 2.7_sqlite_pysqlite_dbapiunicode_cextensions 43105
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_entity_w_annotations 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 51405
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_entity_w_annotations 3.8_sqlite_pysqlite_dbapiunicode_cextensions 46405
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_entity_w_annotations 3.8_sqlite_pysqlite_dbapiunicode_nocextensions 55505
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_entity_w_annotations 2.7_sqlite_pysqlite_dbapiunicode_cextensions 44105
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_entity_w_annotations 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 52705
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_entity_w_annotations 3.8_sqlite_pysqlite_dbapiunicode_cextensions 47405
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_entity_w_annotations 3.8_sqlite_pysqlite_dbapiunicode_nocextensions 56805
# TEST: test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_entity_wo_annotations
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_entity_wo_annotations 2.7_sqlite_pysqlite_dbapiunicode_cextensions 42305
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_entity_wo_annotations 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 50605
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_entity_wo_annotations 3.8_sqlite_pysqlite_dbapiunicode_cextensions 45605
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_entity_wo_annotations 3.8_sqlite_pysqlite_dbapiunicode_nocextensions 54705
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_entity_wo_annotations 2.7_sqlite_pysqlite_dbapiunicode_cextensions 43305
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_entity_wo_annotations 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 51905
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_entity_wo_annotations 3.8_sqlite_pysqlite_dbapiunicode_cextensions 46605
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_entity_wo_annotations 3.8_sqlite_pysqlite_dbapiunicode_nocextensions 56005
# TEST: test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle 2.7_sqlite_pysqlite_dbapiunicode_cextensions 41705
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 45005
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle 3.8_sqlite_pysqlite_dbapiunicode_cextensions 44405
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle 3.8_sqlite_pysqlite_dbapiunicode_nocextensions 48505
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle 2.7_sqlite_pysqlite_dbapiunicode_cextensions 42705
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 46305
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle 3.8_sqlite_pysqlite_dbapiunicode_cextensions 45405
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle 3.8_sqlite_pysqlite_dbapiunicode_nocextensions 49805
# TEST: test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle_w_annotations
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle_w_annotations 2.7_sqlite_pysqlite_dbapiunicode_cextensions 43105
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle_w_annotations 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 51405
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle_w_annotations 3.8_sqlite_pysqlite_dbapiunicode_cextensions 46405
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle_w_annotations 3.8_sqlite_pysqlite_dbapiunicode_nocextensions 55505
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle_w_annotations 2.7_sqlite_pysqlite_dbapiunicode_cextensions 44105
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle_w_annotations 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 52705
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle_w_annotations 3.8_sqlite_pysqlite_dbapiunicode_cextensions 47405
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle_w_annotations 3.8_sqlite_pysqlite_dbapiunicode_nocextensions 56805
# TEST: test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle_wo_annotations
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle_wo_annotations 2.7_sqlite_pysqlite_dbapiunicode_cextensions 42305
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle_wo_annotations 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 50605
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle_wo_annotations 3.8_sqlite_pysqlite_dbapiunicode_cextensions 45605
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle_wo_annotations 3.8_sqlite_pysqlite_dbapiunicode_nocextensions 54705
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle_wo_annotations 2.7_sqlite_pysqlite_dbapiunicode_cextensions 43305
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle_wo_annotations 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 51905
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle_wo_annotations 3.8_sqlite_pysqlite_dbapiunicode_cextensions 46605
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_bundle_wo_annotations 3.8_sqlite_pysqlite_dbapiunicode_nocextensions 56005
# TEST: test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_entity_w_annotations
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_entity_w_annotations 2.7_sqlite_pysqlite_dbapiunicode_cextensions 27205
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_entity_w_annotations 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 29405
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_entity_w_annotations 3.8_sqlite_pysqlite_dbapiunicode_cextensions 30105
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_entity_w_annotations 3.8_sqlite_pysqlite_dbapiunicode_nocextensions 32505
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_entity_w_annotations 2.7_sqlite_pysqlite_dbapiunicode_cextensions 28205
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_entity_w_annotations 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 30705
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_entity_w_annotations 3.8_sqlite_pysqlite_dbapiunicode_cextensions 31105
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_entity_w_annotations 3.8_sqlite_pysqlite_dbapiunicode_nocextensions 33805
# TEST: test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_entity_wo_annotations
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_entity_wo_annotations 2.7_sqlite_pysqlite_dbapiunicode_cextensions 26405
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_entity_wo_annotations 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 28605
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_entity_wo_annotations 3.8_sqlite_pysqlite_dbapiunicode_cextensions 29305
-test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_entity_wo_annotations 3.8_sqlite_pysqlite_dbapiunicode_nocextensions 31705
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_entity_wo_annotations 2.7_sqlite_pysqlite_dbapiunicode_cextensions 27405
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_entity_wo_annotations 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 29905
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_entity_wo_annotations 3.8_sqlite_pysqlite_dbapiunicode_cextensions 30305
+test.aaa_profiling.test_orm.AnnotatedOverheadTest.test_no_entity_wo_annotations 3.8_sqlite_pysqlite_dbapiunicode_nocextensions 33005
# TEST: test.aaa_profiling.test_orm.AttributeOverheadTest.test_attribute_set
# TEST: test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline
-test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 2.7_sqlite_pysqlite_dbapiunicode_cextensions 15150
-test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 26159
-test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 3.8_sqlite_pysqlite_dbapiunicode_cextensions 15188
-test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 3.8_sqlite_pysqlite_dbapiunicode_nocextensions 27200
+test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 2.7_sqlite_pysqlite_dbapiunicode_cextensions 15158
+test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 26170
+test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 3.8_sqlite_pysqlite_dbapiunicode_cextensions 15196
+test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 3.8_sqlite_pysqlite_dbapiunicode_nocextensions 27211
# TEST: test.aaa_profiling.test_orm.DeferOptionsTest.test_defer_many_cols
-test.aaa_profiling.test_orm.DeferOptionsTest.test_defer_many_cols 2.7_sqlite_pysqlite_dbapiunicode_cextensions 21294
-test.aaa_profiling.test_orm.DeferOptionsTest.test_defer_many_cols 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 26303
-test.aaa_profiling.test_orm.DeferOptionsTest.test_defer_many_cols 3.8_sqlite_pysqlite_dbapiunicode_cextensions 21339
-test.aaa_profiling.test_orm.DeferOptionsTest.test_defer_many_cols 3.8_sqlite_pysqlite_dbapiunicode_nocextensions 27351
+test.aaa_profiling.test_orm.DeferOptionsTest.test_defer_many_cols 2.7_sqlite_pysqlite_dbapiunicode_cextensions 21312
+test.aaa_profiling.test_orm.DeferOptionsTest.test_defer_many_cols 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 26324
+test.aaa_profiling.test_orm.DeferOptionsTest.test_defer_many_cols 3.8_sqlite_pysqlite_dbapiunicode_cextensions 21357
+test.aaa_profiling.test_orm.DeferOptionsTest.test_defer_many_cols 3.8_sqlite_pysqlite_dbapiunicode_nocextensions 27372
# TEST: test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_b_aliased
-test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_b_aliased 2.7_sqlite_pysqlite_dbapiunicode_cextensions 9703
-test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_b_aliased 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 9853
-test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_b_aliased 3.8_sqlite_pysqlite_dbapiunicode_cextensions 10154
-test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_b_aliased 3.8_sqlite_pysqlite_dbapiunicode_nocextensions 10304
+test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_b_aliased 2.7_sqlite_pysqlite_dbapiunicode_cextensions 9853
+test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_b_aliased 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 10003
+test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_b_aliased 3.8_sqlite_pysqlite_dbapiunicode_cextensions 10304
+test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_b_aliased 3.8_sqlite_pysqlite_dbapiunicode_nocextensions 10454
# TEST: test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_b_plain
-test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_b_plain 2.7_sqlite_pysqlite_dbapiunicode_cextensions 3953
-test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_b_plain 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 4103
-test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_b_plain 3.8_sqlite_pysqlite_dbapiunicode_cextensions 3954
-test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_b_plain 3.8_sqlite_pysqlite_dbapiunicode_nocextensions 4104
+test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_b_plain 2.7_sqlite_pysqlite_dbapiunicode_cextensions 4053
+test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_b_plain 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 4203
+test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_b_plain 3.8_sqlite_pysqlite_dbapiunicode_cextensions 4054
+test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_b_plain 3.8_sqlite_pysqlite_dbapiunicode_nocextensions 4204
# TEST: test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_d
-test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_d 2.7_sqlite_pysqlite_dbapiunicode_cextensions 93738
-test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_d 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 94088
-test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_d 3.8_sqlite_pysqlite_dbapiunicode_cextensions 101554
-test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_d 3.8_sqlite_pysqlite_dbapiunicode_nocextensions 101704
+test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_d 2.7_sqlite_pysqlite_dbapiunicode_cextensions 94638
+test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_d 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 94788
+test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_d 3.8_sqlite_pysqlite_dbapiunicode_cextensions 102254
+test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_d 3.8_sqlite_pysqlite_dbapiunicode_nocextensions 102204
# TEST: test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_d_aliased
-test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_d_aliased 2.7_sqlite_pysqlite_dbapiunicode_cextensions 91788
-test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_d_aliased 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 92138
-test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_d_aliased 3.8_sqlite_pysqlite_dbapiunicode_cextensions 99919
-test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_d_aliased 3.8_sqlite_pysqlite_dbapiunicode_nocextensions 100069
+test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_d_aliased 2.7_sqlite_pysqlite_dbapiunicode_cextensions 92688
+test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_d_aliased 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 92838
+test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_d_aliased 3.8_sqlite_pysqlite_dbapiunicode_cextensions 100604
+test.aaa_profiling.test_orm.JoinConditionTest.test_a_to_d_aliased 3.8_sqlite_pysqlite_dbapiunicode_nocextensions 100569
# TEST: test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_build_query
-test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_build_query 2.7_sqlite_pysqlite_dbapiunicode_cextensions 434915
-test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_build_query 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 436762
-test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_build_query 3.8_sqlite_pysqlite_dbapiunicode_cextensions 465676
-test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_build_query 3.8_sqlite_pysqlite_dbapiunicode_nocextensions 467518
+test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_build_query 2.7_sqlite_pysqlite_dbapiunicode_cextensions 438105
+test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_build_query 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 439952
+test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_build_query 3.8_sqlite_pysqlite_dbapiunicode_cextensions 468876
+test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_build_query 3.8_sqlite_pysqlite_dbapiunicode_nocextensions 470718
# TEST: test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_fetch_results
test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_fetch_results 2.7_sqlite_pysqlite_dbapiunicode_cextensions 391100
-test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_fetch_results 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 405907
-test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_fetch_results 3.8_sqlite_pysqlite_dbapiunicode_cextensions 395113
-test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_fetch_results 3.8_sqlite_pysqlite_dbapiunicode_nocextensions 412627
+test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_fetch_results 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 406507
+test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_fetch_results 3.8_sqlite_pysqlite_dbapiunicode_cextensions 396913
+test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_fetch_results 3.8_sqlite_pysqlite_dbapiunicode_nocextensions 412027
# TEST: test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_identity
# TEST: test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity
-test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 2.7_sqlite_pysqlite_dbapiunicode_cextensions 77560
-test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 79780
-test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 3.8_sqlite_pysqlite_dbapiunicode_cextensions 80098
-test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 3.8_sqlite_pysqlite_dbapiunicode_nocextensions 83365
+test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 2.7_sqlite_pysqlite_dbapiunicode_cextensions 80113
+test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 82391
+test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 3.8_sqlite_pysqlite_dbapiunicode_cextensions 83854
+test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 3.8_sqlite_pysqlite_dbapiunicode_nocextensions 87383
# TEST: test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks
-test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 2.7_sqlite_pysqlite_dbapiunicode_cextensions 18701
-test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 19147
-test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 3.8_sqlite_pysqlite_dbapiunicode_cextensions 19670
-test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 3.8_sqlite_pysqlite_dbapiunicode_nocextensions 20154
+test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 2.7_sqlite_pysqlite_dbapiunicode_cextensions 20145
+test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 20583
+test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 3.8_sqlite_pysqlite_dbapiunicode_cextensions 21187
+test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 3.8_sqlite_pysqlite_dbapiunicode_nocextensions 21723
# TEST: test.aaa_profiling.test_orm.MergeTest.test_merge_load
-test.aaa_profiling.test_orm.MergeTest.test_merge_load 2.7_sqlite_pysqlite_dbapiunicode_cextensions 1035
-test.aaa_profiling.test_orm.MergeTest.test_merge_load 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 1062
-test.aaa_profiling.test_orm.MergeTest.test_merge_load 3.8_sqlite_pysqlite_dbapiunicode_cextensions 1079
-test.aaa_profiling.test_orm.MergeTest.test_merge_load 3.8_sqlite_pysqlite_dbapiunicode_nocextensions 1113
+test.aaa_profiling.test_orm.MergeTest.test_merge_load 2.7_sqlite_pysqlite_dbapiunicode_cextensions 1313
+test.aaa_profiling.test_orm.MergeTest.test_merge_load 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 1345
+test.aaa_profiling.test_orm.MergeTest.test_merge_load 3.8_sqlite_pysqlite_dbapiunicode_cextensions 1384
+test.aaa_profiling.test_orm.MergeTest.test_merge_load 3.8_sqlite_pysqlite_dbapiunicode_nocextensions 1425
# TEST: test.aaa_profiling.test_orm.MergeTest.test_merge_no_load
# TEST: test.aaa_profiling.test_orm.QueryTest.test_query_cols
-test.aaa_profiling.test_orm.QueryTest.test_query_cols 2.7_sqlite_pysqlite_dbapiunicode_cextensions 5306
-test.aaa_profiling.test_orm.QueryTest.test_query_cols 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 6026
-test.aaa_profiling.test_orm.QueryTest.test_query_cols 3.8_sqlite_pysqlite_dbapiunicode_cextensions 5674
-test.aaa_profiling.test_orm.QueryTest.test_query_cols 3.8_sqlite_pysqlite_dbapiunicode_nocextensions 6414
+test.aaa_profiling.test_orm.QueryTest.test_query_cols 2.7_sqlite_pysqlite_dbapiunicode_cextensions 5397
+test.aaa_profiling.test_orm.QueryTest.test_query_cols 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 6147
+test.aaa_profiling.test_orm.QueryTest.test_query_cols 3.8_sqlite_pysqlite_dbapiunicode_cextensions 5765
+test.aaa_profiling.test_orm.QueryTest.test_query_cols 3.8_sqlite_pysqlite_dbapiunicode_nocextensions 6535
# TEST: test.aaa_profiling.test_orm.SelectInEagerLoadTest.test_round_trip_results
-test.aaa_profiling.test_orm.SelectInEagerLoadTest.test_round_trip_results 2.7_sqlite_pysqlite_dbapiunicode_cextensions 152251
-test.aaa_profiling.test_orm.SelectInEagerLoadTest.test_round_trip_results 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 168676
-test.aaa_profiling.test_orm.SelectInEagerLoadTest.test_round_trip_results 3.8_sqlite_pysqlite_dbapiunicode_cextensions 159629
-test.aaa_profiling.test_orm.SelectInEagerLoadTest.test_round_trip_results 3.8_sqlite_pysqlite_dbapiunicode_nocextensions 177451
+test.aaa_profiling.test_orm.SelectInEagerLoadTest.test_round_trip_results 2.7_sqlite_pysqlite_dbapiunicode_cextensions 232364
+test.aaa_profiling.test_orm.SelectInEagerLoadTest.test_round_trip_results 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 248890
+test.aaa_profiling.test_orm.SelectInEagerLoadTest.test_round_trip_results 3.8_sqlite_pysqlite_dbapiunicode_cextensions 244874
+test.aaa_profiling.test_orm.SelectInEagerLoadTest.test_round_trip_results 3.8_sqlite_pysqlite_dbapiunicode_nocextensions 262595
# TEST: test.aaa_profiling.test_orm.SessionTest.test_expire_lots
-test.aaa_profiling.test_orm.SessionTest.test_expire_lots 2.7_sqlite_pysqlite_dbapiunicode_cextensions 1135
-test.aaa_profiling.test_orm.SessionTest.test_expire_lots 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 1150
-test.aaa_profiling.test_orm.SessionTest.test_expire_lots 3.8_sqlite_pysqlite_dbapiunicode_cextensions 1241
+test.aaa_profiling.test_orm.SessionTest.test_expire_lots 2.7_sqlite_pysqlite_dbapiunicode_cextensions 1141
+test.aaa_profiling.test_orm.SessionTest.test_expire_lots 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 1156
+test.aaa_profiling.test_orm.SessionTest.test_expire_lots 3.8_sqlite_pysqlite_dbapiunicode_cextensions 1259
test.aaa_profiling.test_orm.SessionTest.test_expire_lots 3.8_sqlite_pysqlite_dbapiunicode_nocextensions 1256
# TEST: test.aaa_profiling.test_pool.QueuePoolTest.test_first_connect