summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/orm
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sqlalchemy/orm')
-rw-r--r--lib/sqlalchemy/orm/context.py28
-rw-r--r--lib/sqlalchemy/orm/loading.py1
-rw-r--r--lib/sqlalchemy/orm/persistence.py15
-rw-r--r--lib/sqlalchemy/orm/query.py9
-rw-r--r--lib/sqlalchemy/orm/relationships.py16
-rw-r--r--lib/sqlalchemy/orm/session.py43
6 files changed, 86 insertions, 26 deletions
diff --git a/lib/sqlalchemy/orm/context.py b/lib/sqlalchemy/orm/context.py
index 09163d4e9..d5f001db1 100644
--- a/lib/sqlalchemy/orm/context.py
+++ b/lib/sqlalchemy/orm/context.py
@@ -25,6 +25,7 @@ from ..sql import expression
from ..sql import roles
from ..sql import util as sql_util
from ..sql import visitors
+from ..sql.base import _entity_namespace_key
from ..sql.base import _select_iterables
from ..sql.base import CacheableOptions
from ..sql.base import CompileState
@@ -241,8 +242,6 @@ class ORMCompileState(CompileState):
# were passed to session.execute:
# session.execute(legacy_select([User.id, User.name]))
# see test_query->test_legacy_tuple_old_select
- if not statement._is_future:
- return result
load_options = execution_options.get(
"_sa_orm_load_options", QueryContext.default_load_options
@@ -399,6 +398,7 @@ class ORMSelectCompileState(ORMCompileState, SelectState):
compound_eager_adapter = None
correlate = None
+ correlate_except = None
_where_criteria = ()
_having_criteria = ()
@@ -406,9 +406,6 @@ class ORMSelectCompileState(ORMCompileState, SelectState):
def create_for_statement(cls, statement, compiler, **kw):
"""compiler hook, we arrive here from compiler.visit_select() only."""
- if not statement._is_future:
- return SelectState(statement, compiler, **kw)
-
if compiler is not None:
toplevel = not compiler.stack
compiler._rewrites_selected_columns = True
@@ -592,6 +589,13 @@ class ORMSelectCompileState(ORMCompileState, SelectState):
for s in query._correlate
)
)
+ elif query._correlate_except:
+ self.correlate_except = tuple(
+ util.flatten_iterator(
+ sql_util.surface_selectables(s) if s is not None else None
+ for s in query._correlate_except
+ )
+ )
elif not query._auto_correlate:
self.correlate = (None,)
@@ -827,6 +831,7 @@ class ORMSelectCompileState(ORMCompileState, SelectState):
hints=self.select_statement._hints,
statement_hints=self.select_statement._statement_hints,
correlate=self.correlate,
+ correlate_except=self.correlate_except,
**self._select_args
)
@@ -902,6 +907,7 @@ class ORMSelectCompileState(ORMCompileState, SelectState):
hints=self.select_statement._hints,
statement_hints=self.select_statement._statement_hints,
correlate=self.correlate,
+ correlate_except=self.correlate_except,
**self._select_args
)
@@ -921,6 +927,7 @@ class ORMSelectCompileState(ORMCompileState, SelectState):
hints,
statement_hints,
correlate,
+ correlate_except,
limit_clause,
offset_clause,
distinct,
@@ -972,6 +979,11 @@ class ORMSelectCompileState(ORMCompileState, SelectState):
if correlate:
statement.correlate.non_generative(statement, *correlate)
+ if correlate_except:
+ statement.correlate_except.non_generative(
+ statement, *correlate_except
+ )
+
return statement
def _adapt_polymorphic_element(self, element):
@@ -1222,7 +1234,7 @@ class ORMSelectCompileState(ORMCompileState, SelectState):
# string given, e.g. query(Foo).join("bar").
# we look to the left entity or what we last joined
# towards
- onclause = sql.util._entity_namespace_key(
+ onclause = _entity_namespace_key(
inspect(self._joinpoint_zero()), onclause
)
@@ -1243,9 +1255,7 @@ class ORMSelectCompileState(ORMCompileState, SelectState):
info = inspect(jp0)
if getattr(info, "mapper", None) is onclause._parententity:
- onclause = sql.util._entity_namespace_key(
- info, onclause.key
- )
+ onclause = _entity_namespace_key(info, onclause.key)
# legacy ^^^^^^^^^^^^^^^^^^^^^^^^^^^
if isinstance(onclause, interfaces.PropComparator):
diff --git a/lib/sqlalchemy/orm/loading.py b/lib/sqlalchemy/orm/loading.py
index abb8ce32d..55c2b79f5 100644
--- a/lib/sqlalchemy/orm/loading.py
+++ b/lib/sqlalchemy/orm/loading.py
@@ -431,6 +431,7 @@ def load_on_pk_identity(
params=load_options._params,
execution_options={"_sa_orm_load_options": load_options},
bind_arguments=bind_arguments,
+ future=True,
)
.unique()
.scalars()
diff --git a/lib/sqlalchemy/orm/persistence.py b/lib/sqlalchemy/orm/persistence.py
index cbe7bde33..0cc16b96e 100644
--- a/lib/sqlalchemy/orm/persistence.py
+++ b/lib/sqlalchemy/orm/persistence.py
@@ -29,17 +29,17 @@ from .. import future
from .. import sql
from .. import util
from ..engine import result as _result
-from ..future import select as future_select
from ..sql import coercions
from ..sql import expression
from ..sql import operators
from ..sql import roles
+from ..sql import select
+from ..sql.base import _entity_namespace_key
from ..sql.base import CompileState
from ..sql.base import Options
from ..sql.dml import DeleteDMLState
from ..sql.dml import UpdateDMLState
from ..sql.elements import BooleanClauseList
-from ..sql.util import _entity_namespace_key
def _bulk_insert(
@@ -887,7 +887,7 @@ def _emit_update_statements(
)
)
- stmt = table.update(clauses)
+ stmt = table.update().where(clauses)
return stmt
cached_stmt = base_mapper._memo(("update", table), update_stmt)
@@ -1280,7 +1280,7 @@ def _emit_post_update_statements(
)
)
- stmt = table.update(clauses)
+ stmt = table.update().where(clauses)
if mapper.version_id_col is not None:
stmt = stmt.return_defaults(mapper.version_id_col)
@@ -1394,7 +1394,7 @@ def _emit_delete_statements(
)
)
- return table.delete(clauses)
+ return table.delete().where(clauses)
statement = base_mapper._memo(("delete", table), delete_stmt)
for connection, recs in groupby(delete, lambda rec: rec[1]): # connection
@@ -1940,7 +1940,7 @@ class BulkUDCompileState(CompileState):
for k, v in iterator:
if mapper:
if isinstance(k, util.string_types):
- desc = sql.util._entity_namespace_key(mapper, k)
+ desc = _entity_namespace_key(mapper, k)
values.extend(desc._bulk_update_tuples(v))
elif "entity_namespace" in k._annotations:
k_anno = k._annotations
@@ -1989,7 +1989,7 @@ class BulkUDCompileState(CompileState):
):
mapper = update_options._subject_mapper
- select_stmt = future_select(
+ select_stmt = select(
*(mapper.primary_key + (mapper.select_identity_token,))
)
select_stmt._where_criteria = statement._where_criteria
@@ -2007,6 +2007,7 @@ class BulkUDCompileState(CompileState):
execution_options,
bind_arguments,
_add_event=skip_for_full_returning,
+ future=True,
)
matched_rows = result.fetchall()
diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py
index 1ca65c733..acc76094b 100644
--- a/lib/sqlalchemy/orm/query.py
+++ b/lib/sqlalchemy/orm/query.py
@@ -45,12 +45,13 @@ from .. import inspection
from .. import log
from .. import sql
from .. import util
-from ..future.selectable import Select as FutureSelect
from ..sql import coercions
from ..sql import expression
from ..sql import roles
+from ..sql import Select
from ..sql import util as sql_util
from ..sql.annotation import SupportsCloneAnnotations
+from ..sql.base import _entity_namespace_key
from ..sql.base import _generative
from ..sql.base import Executable
from ..sql.selectable import _SelectFromElements
@@ -61,7 +62,6 @@ from ..sql.selectable import HasSuffixes
from ..sql.selectable import LABEL_STYLE_NONE
from ..sql.selectable import LABEL_STYLE_TABLENAME_PLUS_COL
from ..sql.selectable import SelectStatementGrouping
-from ..sql.util import _entity_namespace_key
from ..sql.visitors import InternalTraversal
from ..util import collections_abc
@@ -419,7 +419,7 @@ class Query(
stmt._propagate_attrs = self._propagate_attrs
else:
# Query / select() internal attributes are 99% cross-compatible
- stmt = FutureSelect.__new__(FutureSelect)
+ stmt = Select.__new__(Select)
stmt.__dict__.update(self.__dict__)
stmt.__dict__.update(
_label_style=self._label_style,
@@ -2836,6 +2836,7 @@ class Query(
statement,
params,
execution_options={"_sa_orm_load_options": self.load_options},
+ future=True,
)
# legacy: automatically set scalars, unique
@@ -3209,6 +3210,7 @@ class Query(
delete_,
self.load_options._params,
execution_options={"synchronize_session": synchronize_session},
+ future=True,
)
bulk_del.result = result
self.session.dispatch.after_bulk_delete(bulk_del)
@@ -3363,6 +3365,7 @@ class Query(
upd,
self.load_options._params,
execution_options={"synchronize_session": synchronize_session},
+ future=True,
)
bulk_ud.result = result
self.session.dispatch.after_bulk_update(bulk_ud)
diff --git a/lib/sqlalchemy/orm/relationships.py b/lib/sqlalchemy/orm/relationships.py
index bedc54153..0be15260e 100644
--- a/lib/sqlalchemy/orm/relationships.py
+++ b/lib/sqlalchemy/orm/relationships.py
@@ -1352,12 +1352,18 @@ class RelationshipProperty(StrategizedProperty):
crit = j & sql.True_._ifnone(criterion)
if secondary is not None:
- ex = sql.exists(
- [1], crit, from_obj=[dest, secondary]
- ).correlate_except(dest, secondary)
+ ex = (
+ sql.exists(1)
+ .where(crit)
+ .select_from(dest, secondary)
+ .correlate_except(dest, secondary)
+ )
else:
- ex = sql.exists([1], crit, from_obj=dest).correlate_except(
- dest
+ ex = (
+ sql.exists(1)
+ .where(crit)
+ .select_from(dest)
+ .correlate_except(dest)
)
return ex
diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py
index abc990f7b..f4f7374e4 100644
--- a/lib/sqlalchemy/orm/session.py
+++ b/lib/sqlalchemy/orm/session.py
@@ -118,6 +118,7 @@ class ORMExecuteState(util.MemoizedSlots):
"_compile_state_cls",
"_starting_event_idx",
"_events_todo",
+ "_future",
)
def __init__(
@@ -129,6 +130,7 @@ class ORMExecuteState(util.MemoizedSlots):
bind_arguments,
compile_state_cls,
events_todo,
+ future,
):
self.session = session
self.statement = statement
@@ -137,6 +139,7 @@ class ORMExecuteState(util.MemoizedSlots):
self.bind_arguments = bind_arguments
self._compile_state_cls = compile_state_cls
self._events_todo = list(events_todo)
+ self._future = future
def _remaining_events(self):
return self._events_todo[self._starting_event_idx + 1 :]
@@ -212,6 +215,7 @@ class ORMExecuteState(util.MemoizedSlots):
_execution_options,
_bind_arguments,
_parent_execute_state=self,
+ future=self._future,
)
@property
@@ -924,6 +928,7 @@ class Session(_SessionClassMethods):
self,
bind=None,
autoflush=True,
+ future=False,
expire_on_commit=True,
autocommit=False,
twophase=False,
@@ -1039,6 +1044,26 @@ class Session(_SessionClassMethods):
so that all attribute/object access subsequent to a completed
transaction will load from the most recent database state.
+ :param future: if True, use 2.0 style behavior for the
+ :meth:`_orm.Session.execute` method. This includes that the
+ :class:`_engine.Result` object returned will return new-style
+ tuple rows, as well as that Core constructs such as
+ :class:`_sql.Select`,
+ :class:`_sql.Update` and :class:`_sql.Delete` will be interpreted
+ in an ORM context if they are made against ORM entities rather than
+ plain :class:`.Table` metadata objects.
+
+ The "future" flag is also available on a per-execution basis
+ using the :paramref:`_orm.Session.execute.future` flag.
+
+ .. versionadded:: 1.4
+
+ .. seealso::
+
+ :ref:`migration_20_toplevel`
+
+ :ref:`migration_20_result_rows`
+
:param info: optional dictionary of arbitrary data to be associated
with this :class:`.Session`. Is available via the
:attr:`.Session.info` attribute. Note the dictionary is copied at
@@ -1071,6 +1096,7 @@ class Session(_SessionClassMethods):
self._flushing = False
self._warn_on_events = False
self._transaction = None
+ self.future = future
self.hash_key = _new_sessionid()
self.autoflush = autoflush
self.autocommit = autocommit
@@ -1387,6 +1413,7 @@ class Session(_SessionClassMethods):
params=None,
execution_options=util.immutabledict(),
bind_arguments=None,
+ future=False,
_parent_execute_state=None,
_add_event=None,
**kw
@@ -1493,6 +1520,14 @@ class Session(_SessionClassMethods):
Contents of this dictionary are passed to the
:meth:`.Session.get_bind` method.
+ :param future:
+ Use future style execution for this statement. This is
+ the same effect as the :paramref:`_orm.Session.future` flag,
+ except at the level of this single statement execution. See
+ that flag for details.
+
+ .. versionadded:: 1.4
+
:param mapper:
deprecated; use the bind_arguments dictionary
@@ -1518,15 +1553,18 @@ class Session(_SessionClassMethods):
"""
statement = coercions.expect(roles.CoerceTextStatementRole, statement)
+ future = future or self.future
+
if not bind_arguments:
bind_arguments = kw
elif kw:
bind_arguments.update(kw)
- if (
+ if future and (
statement._propagate_attrs.get("compile_state_plugin", None)
== "orm"
):
+ # note that even without "future" mode, we need
compile_state_cls = CompileState._get_plugin_class_for_plugin(
statement, "orm"
)
@@ -1547,7 +1585,7 @@ class Session(_SessionClassMethods):
)
else:
bind_arguments.setdefault("clause", statement)
- if statement._is_future:
+ if future:
execution_options = util.immutabledict().merge_with(
execution_options, {"future_result": True}
)
@@ -1568,6 +1606,7 @@ class Session(_SessionClassMethods):
bind_arguments,
compile_state_cls,
events_todo,
+ future,
)
for idx, fn in enumerate(events_todo):
orm_exec_state._starting_event_idx = idx