summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/sqlalchemy/orm/properties.py3
-rw-r--r--lib/sqlalchemy/orm/query.py25
-rw-r--r--lib/sqlalchemy/orm/session.py17
-rw-r--r--lib/sqlalchemy/orm/strategies.py4
-rw-r--r--lib/sqlalchemy/sql/compiler.py2
5 files changed, 38 insertions, 13 deletions
diff --git a/lib/sqlalchemy/orm/properties.py b/lib/sqlalchemy/orm/properties.py
index 1ce71fdb6..b0eb54301 100644
--- a/lib/sqlalchemy/orm/properties.py
+++ b/lib/sqlalchemy/orm/properties.py
@@ -480,7 +480,8 @@ class PropertyLoader(StrategizedProperty):
def cascade_iterator(self, type_, state, visited_instances, halt_on=None):
if not type_ in self.cascade:
return
- passive = type_ != 'delete' or self.passive_deletes
+ # only actively lazy load on the 'delete' cascade
+ passive = type_ != 'delete' or self.passive_deletes
mapper = self.mapper.primary_mapper()
instances = state.value_as_iterable(self.key, passive=passive)
if instances:
diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py
index 07caae07a..2fba04ee0 100644
--- a/lib/sqlalchemy/orm/query.py
+++ b/lib/sqlalchemy/orm/query.py
@@ -1044,7 +1044,7 @@ class Query(object):
def _execute_and_instances(self, querycontext):
result = self.session.execute(querycontext.statement, params=self._params, mapper=self._mapper_zero_or_none(), _state=self._refresh_state)
return self.instances(result, querycontext)
-
+
def instances(self, cursor, __context=None):
"""Given a ResultProxy cursor as returned by connection.execute(), return an ORM result as an iterator.
@@ -1081,7 +1081,7 @@ class Query(object):
labels = dict([(label, property(util.itemgetter(i))) for i, label in enumerate(labels) if label])
rowtuple = type.__new__(type, "RowTuple", (tuple,), labels)
rowtuple.keys = labels.keys
-
+
while True:
context.progress = util.Set()
context.partials = {}
@@ -1165,7 +1165,11 @@ class Query(object):
if lockmode is not None:
q._lockmode = lockmode
- q.__get_options(populate_existing=bool(refresh_state), version_check=(lockmode is not None), only_load_props=only_load_props, refresh_state=refresh_state)
+ q.__get_options(
+ populate_existing=bool(refresh_state),
+ version_check=(lockmode is not None),
+ only_load_props=only_load_props,
+ refresh_state=refresh_state)
q._order_by = None
try:
# call using all() to avoid LIMIT compilation complexity
@@ -1174,7 +1178,13 @@ class Query(object):
return None
def _select_args(self):
- return {'limit':self._limit, 'offset':self._offset, 'distinct':self._distinct, 'group_by':self._group_by or None, 'having':self._having or None}
+ return {
+ 'limit':self._limit,
+ 'offset':self._offset,
+ 'distinct':self._distinct,
+ 'group_by':self._group_by or None,
+ 'having':self._having or None
+ }
_select_args = property(_select_args)
def _should_nest_selectable(self):
@@ -1343,6 +1353,13 @@ class Query(object):
self._adjust_for_single_inheritance(context)
+ if not context.primary_columns:
+ if self._only_load_props:
+ raise sa_exc.InvalidRequestError("No column-based properties specified for refresh operation."
+ " Use session.expire() to reload collections and related items.")
+ else:
+ raise sa_exc.InvalidRequestError("Query contains no columns with which to SELECT from.")
+
if eager_joins and self._should_nest_selectable:
# for eager joins present and LIMIT/OFFSET/DISTINCT, wrap the query inside a select,
# then append eager joins onto that
diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py
index 2110f8826..b9f170733 100644
--- a/lib/sqlalchemy/orm/session.py
+++ b/lib/sqlalchemy/orm/session.py
@@ -960,7 +960,7 @@ class Session(object):
# remove associations
cascaded = list(_cascade_state_iterator('refresh-expire', state))
_expire_state(state, None)
- for (state, m) in cascaded:
+ for (state, m, o) in cascaded:
_expire_state(state, None)
def prune(self):
@@ -991,7 +991,7 @@ class Session(object):
raise sa_exc.InvalidRequestError(
"Instance %s is not present in this Session" %
mapperutil.state_str(state))
- for s, m in [(state, None)] + list(_cascade_state_iterator('expunge', state)):
+ for s, m, o in [(state, None, None)] + list(_cascade_state_iterator('expunge', state)):
self._expunge_state(s)
def _expunge_state(self, state):
@@ -1120,8 +1120,12 @@ class Session(object):
state = attributes.instance_state(instance)
except exc.NO_STATE:
raise exc.UnmappedInstanceError(instance)
- self._delete_impl(state)
- for state, m in _cascade_state_iterator('delete', state):
+
+ # grab the full cascade list first, since lazyloads/autoflush
+ # may be triggered by this operation (delete cascade lazyloads by default)
+ cascade_states = list(_cascade_state_iterator('delete', state))
+ self._delete_impl(state)
+ for state, m, o in cascade_states:
self._delete_impl(state, ignore_transient=True)
def merge(self, instance, entity_name=None, dont_load=False,
@@ -1508,8 +1512,11 @@ _sessions = weakref.WeakValueDictionary()
def _cascade_state_iterator(cascade, state, **kwargs):
mapper = _state_mapper(state)
+ # yield the state, object, mapper. yielding the object
+ # allows the iterator's results to be held in a list without
+ # states being garbage collected
for (o, m) in mapper.cascade_iterator(cascade, state, **kwargs):
- yield attributes.instance_state(o), m
+ yield attributes.instance_state(o), o, m
def _cascade_unknown_state_iterator(cascade, state, **kwargs):
mapper = _state_mapper(state)
diff --git a/lib/sqlalchemy/orm/strategies.py b/lib/sqlalchemy/orm/strategies.py
index fcb56865b..3073e17db 100644
--- a/lib/sqlalchemy/orm/strategies.py
+++ b/lib/sqlalchemy/orm/strategies.py
@@ -524,8 +524,8 @@ class LoadLazyAttribute(object):
"lazy load operation of attribute '%s' cannot proceed" %
(mapperutil.state_str(state), self.key)
)
-
- q = session.query(prop.mapper).autoflush(False)._adapt_all_clauses()
+
+ q = session.query(prop.mapper)._adapt_all_clauses()
if self.path:
q = q._with_current_path(self.path)
diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py
index 51ad1dfb0..3b82fbdd0 100644
--- a/lib/sqlalchemy/sql/compiler.py
+++ b/lib/sqlalchemy/sql/compiler.py
@@ -788,7 +788,7 @@ class SchemaGenerator(DDLBase):
if column.default is not None:
self.traverse_single(column.default)
- self.append("\nCREATE " + " ".join(table._prefixes) + " TABLE " + self.preparer.format_table(table) + " (")
+ self.append("\nCREATE" + " ".join(table._prefixes) + " TABLE " + self.preparer.format_table(table) + " (")
separator = "\n"