summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/sql/elements.py
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2020-04-27 12:58:12 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2020-05-25 13:56:37 -0400
commit6930dfc032c3f9f474e71ab4e021c0ef8384930e (patch)
tree34b919a3c34edaffda1750f161a629fc5b9a8020 /lib/sqlalchemy/sql/elements.py
parentdce8c7a125cb99fad62c76cd145752d5afefae36 (diff)
downloadsqlalchemy-6930dfc032c3f9f474e71ab4e021c0ef8384930e.tar.gz
Convert execution to move through Session
This patch replaces the ORM execution flow with a single pathway through Session.execute() for all queries, including Core and ORM. Currently included is full support for ORM Query, Query.from_statement(), select(), as well as the baked query and horizontal shard systems. Initial changes have also been made to the dogpile caching example, which like baked query makes use of a new ORM-specific execution hook that replaces the use of both QueryEvents.before_compile() as well as Query._execute_and_instances() as the central ORM interception hooks. select() and Query() constructs alike can be passed to Session.execute() where they will return ORM results in a Results object. This API is currently used internally by Query. Full support for Session.execute()->results to behave in a fully 2.0 fashion will be in later changesets. bulk update/delete with ORM support will also be delivered via the update() and delete() constructs, however these have not yet been adapted to the new system and may follow in a subsequent update. Performance is also beginning to lag as of this commit and some previous ones. It is hoped that a few central functions such as the coercions functions can be rewritten in C to re-gain performance. Additionally, query caching is now available and some subsequent patches will attempt to cache more of the per-execution work from the ORM layer, e.g. column getters and adapters. This patch also contains initial "turn on" of the caching system enginewide via the query_cache_size parameter to create_engine(). Still defaulting at zero for "no caching". The caching system still needs adjustments in order to gain adequate performance. Change-Id: I047a7ebb26aa85dc01f6789fac2bff561dcd555d
Diffstat (limited to 'lib/sqlalchemy/sql/elements.py')
-rw-r--r--lib/sqlalchemy/sql/elements.py70
1 files changed, 47 insertions, 23 deletions
diff --git a/lib/sqlalchemy/sql/elements.py b/lib/sqlalchemy/sql/elements.py
index c1bc9edbc..287e53724 100644
--- a/lib/sqlalchemy/sql/elements.py
+++ b/lib/sqlalchemy/sql/elements.py
@@ -191,7 +191,12 @@ class ClauseElement(
__visit_name__ = "clause"
- _annotations = {}
+ _propagate_attrs = util.immutabledict()
+ """like annotations, however these propagate outwards liberally
+ as SQL constructs are built, and are set up at construction time.
+
+ """
+
supports_execution = False
_from_objects = []
bind = None
@@ -215,6 +220,16 @@ class ClauseElement(
_cache_key_traversal = None
+ def _set_propagate_attrs(self, values):
+ # usually, self._propagate_attrs is empty here. one case where it's
+ # not is a subquery against ORM select, that is then pulled as a
+ # property of an aliased class. should all be good
+
+ # assert not self._propagate_attrs
+
+ self._propagate_attrs = util.immutabledict(values)
+ return self
+
def _clone(self):
"""Create a shallow copy of this ClauseElement.
@@ -870,6 +885,7 @@ class ColumnElement(
type_=getattr(self, "type", None),
_selectable=selectable,
)
+ co._propagate_attrs = selectable._propagate_attrs
co._proxies = [self]
if selectable._is_clone_of is not None:
co._is_clone_of = selectable._is_clone_of.columns.get(key)
@@ -1495,6 +1511,8 @@ class TextClause(
_render_label_in_columns_clause = False
+ _hide_froms = ()
+
def __and__(self, other):
# support use in select.where(), query.filter()
return and_(self, other)
@@ -1509,10 +1527,6 @@ class TextClause(
_allow_label_resolve = False
- @property
- def _hide_froms(self):
- return []
-
def __init__(self, text, bind=None):
self._bind = bind
self._bindparams = {}
@@ -2093,14 +2107,16 @@ class ClauseList(
)
if self.group_contents:
self.clauses = [
- coercions.expect(text_converter_role, clause).self_group(
- against=self.operator
- )
+ coercions.expect(
+ text_converter_role, clause, apply_propagate_attrs=self
+ ).self_group(against=self.operator)
for clause in clauses
]
else:
self.clauses = [
- coercions.expect(text_converter_role, clause)
+ coercions.expect(
+ text_converter_role, clause, apply_propagate_attrs=self
+ )
for clause in clauses
]
self._is_implicitly_boolean = operators.is_boolean(self.operator)
@@ -2641,7 +2657,9 @@ class Case(ColumnElement):
whenlist = [
(
coercions.expect(
- roles.ExpressionElementRole, c
+ roles.ExpressionElementRole,
+ c,
+ apply_propagate_attrs=self,
).self_group(),
coercions.expect(roles.ExpressionElementRole, r),
)
@@ -2650,7 +2668,9 @@ class Case(ColumnElement):
else:
whenlist = [
(
- coercions.expect(roles.ColumnArgumentRole, c).self_group(),
+ coercions.expect(
+ roles.ColumnArgumentRole, c, apply_propagate_attrs=self
+ ).self_group(),
coercions.expect(roles.ExpressionElementRole, r),
)
for (c, r) in whens
@@ -2805,7 +2825,10 @@ class Cast(WrapsColumnExpression, ColumnElement):
"""
self.type = type_api.to_instance(type_)
self.clause = coercions.expect(
- roles.ExpressionElementRole, expression, type_=self.type
+ roles.ExpressionElementRole,
+ expression,
+ type_=self.type,
+ apply_propagate_attrs=self,
)
self.typeclause = TypeClause(self.type)
@@ -2906,7 +2929,10 @@ class TypeCoerce(WrapsColumnExpression, ColumnElement):
"""
self.type = type_api.to_instance(type_)
self.clause = coercions.expect(
- roles.ExpressionElementRole, expression, type_=self.type
+ roles.ExpressionElementRole,
+ expression,
+ type_=self.type,
+ apply_propagate_attrs=self,
)
@property
@@ -3031,6 +3057,7 @@ class UnaryExpression(ColumnElement):
):
self.operator = operator
self.modifier = modifier
+ self._propagate_attrs = element._propagate_attrs
self.element = element.self_group(
against=self.operator or self.modifier
)
@@ -3474,6 +3501,7 @@ class BinaryExpression(ColumnElement):
if isinstance(operator, util.string_types):
operator = operators.custom_op(operator)
self._orig = (left.__hash__(), right.__hash__())
+ self._propagate_attrs = left._propagate_attrs or right._propagate_attrs
self.left = left.self_group(against=operator)
self.right = right.self_group(against=operator)
self.operator = operator
@@ -4159,6 +4187,7 @@ class Label(roles.LabeledColumnExprRole, ColumnElement):
name=name if name else self.name,
disallow_is_literal=True,
)
+ e._propagate_attrs = selectable._propagate_attrs
e._proxies.append(self)
if self._type is not None:
e.type = self._type
@@ -4340,16 +4369,10 @@ class ColumnClause(
return other.proxy_set.intersection(self.proxy_set)
def get_children(self, column_tables=False, **kw):
- if column_tables and self.table is not None:
- # TODO: this is only used by ORM query deep_entity_zero.
- # this is being removed in a later release so remove
- # column_tables also at that time.
- return [self.table]
- else:
- # override base get_children() to not return the Table
- # or selectable that is parent to this column. Traversals
- # expect the columns of tables and subqueries to be leaf nodes.
- return []
+ # override base get_children() to not return the Table
+ # or selectable that is parent to this column. Traversals
+ # expect the columns of tables and subqueries to be leaf nodes.
+ return []
@HasMemoized.memoized_attribute
def _from_objects(self):
@@ -4474,6 +4497,7 @@ class ColumnClause(
_selectable=selectable,
is_literal=is_literal,
)
+ c._propagate_attrs = selectable._propagate_attrs
if name is None:
c.key = self.key
c._proxies = [self]