summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2011-02-10 22:27:45 -0500
committerMike Bayer <mike_mp@zzzcomputing.com>2011-02-10 22:27:45 -0500
commit848257f7d2f325f67e2dab553d0ee9e5d89f03d6 (patch)
tree4c206f3fa5d3aa1542bf6270a1ed1865df9e74b6
parent3cbab1ee1ecc4dc2ae74eaccbfd6de2fcdeed22c (diff)
downloadsqlalchemy-848257f7d2f325f67e2dab553d0ee9e5d89f03d6.tar.gz
- The concept of associating a ".bind" directly with a
ClauseElement has been explicitly moved to Executable, i.e. the mixin that describes ClauseElements which represent engine-executable constructs. This change is an improvement to internal organization and is unlikely to affect any real-world usage. [ticket:2048]
-rw-r--r--CHANGES7
-rw-r--r--lib/sqlalchemy/sql/expression.py73
2 files changed, 41 insertions, 39 deletions
diff --git a/CHANGES b/CHANGES
index 0371cc87d..f1897c85c 100644
--- a/CHANGES
+++ b/CHANGES
@@ -215,6 +215,13 @@ CHANGES
executions which fail before the DBAPI becomes
involved. [ticket:2015]
+ - The concept of associating a ".bind" directly with a
+ ClauseElement has been explicitly moved to Executable,
+ i.e. the mixin that describes ClauseElements which represent
+ engine-executable constructs. This change is an improvement
+ to internal organization and is unlikely to affect any
+ real-world usage. [ticket:2048]
+
-sqlite
- SQLite dialect now uses `NullPool` for file-based databases
[ticket:1921]
diff --git a/lib/sqlalchemy/sql/expression.py b/lib/sqlalchemy/sql/expression.py
index 2a23d146b..ec2a0e516 100644
--- a/lib/sqlalchemy/sql/expression.py
+++ b/lib/sqlalchemy/sql/expression.py
@@ -1277,7 +1277,7 @@ class ClauseElement(Visitable):
_annotations = {}
supports_execution = False
_from_objects = []
- _bind = None
+ bind = None
def _clone(self):
"""Create a shallow copy of this ClauseElement.
@@ -1460,30 +1460,8 @@ class ClauseElement(Visitable):
"""
return self
- # TODO: remove .bind as a method from the root ClauseElement.
- # we should only be deriving binds from FromClause elements
- # and certain SchemaItem subclasses.
- # the "search_for_bind" functionality can still be used by
- # execute(), however.
- @property
- def bind(self):
- """Returns the Engine or Connection to which this ClauseElement is
- bound, or None if none found.
-
- """
- if self._bind is not None:
- return self._bind
-
- for f in _from_objects(self):
- if f is self:
- continue
- engine = f.bind
- if engine is not None:
- return engine
- else:
- return None
- @util.pending_deprecation('0.7',
+ @util.deprecated('0.7',
'Only SQL expressions which subclass '
':class:`.Executable` may provide the '
':func:`.execute` method.')
@@ -1494,15 +1472,11 @@ class ClauseElement(Visitable):
e = self.bind
if e is None:
label = getattr(self, 'description', self.__class__.__name__)
- msg = ('This %s is not bound and does not support direct '
- 'execution. Supply this statement to a Connection or '
- 'Engine for execution. Or, assign a bind to the statement '
- 'or the Metadata of its underlying tables to enable '
- 'implicit execution via this method.' % label)
+ msg = ('This %s does not support direct execution.' % label)
raise exc.UnboundExecutionError(msg)
return e._execute_clauseelement(self, multiparams, params)
- @util.pending_deprecation('0.7',
+ @util.deprecated('0.7',
'Only SQL expressions which subclass '
':class:`.Executable` may provide the '
':func:`.scalar` method.')
@@ -1555,9 +1529,7 @@ class ClauseElement(Visitable):
bind = self.bind
else:
dialect = default.DefaultDialect()
- c= self._compiler(dialect, bind=bind, **kw)
- #c.string = c.process(c.statement)
- return c
+ return self._compiler(dialect, bind=bind, **kw)
def _compiler(self, dialect, **kw):
"""Return a compiler appropriate for this ClauseElement, given a
@@ -2656,6 +2628,7 @@ class Executable(_Generative):
supports_execution = True
_execution_options = util.immutabledict()
+ _bind = None
@_generative
def execution_options(self, **kw):
@@ -2707,7 +2680,8 @@ class Executable(_Generative):
includes a connection-only option to specify transaction isolation
level.
- :meth:`.Query.execution_options()` - applies options to the statement
+ :meth:`.Query.execution_options()` - applies options to
+ the statement
generated by a :class:`.orm.Query` object.
"""
@@ -2727,11 +2701,9 @@ class Executable(_Generative):
e = self.bind
if e is None:
label = getattr(self, 'description', self.__class__.__name__)
- msg = ('This %s is not bound and does not support direct '
- 'execution. Supply this statement to a Connection or '
- 'Engine for execution. Or, assign a bind to the statement '
- 'or the Metadata of its underlying tables to enable '
- 'implicit execution via this method.' % label)
+ msg = ('This %s is not directly bound to a Connection or Engine.'
+ 'Use the .execute() method of a Connection or Engine '
+ 'to execute this construct.' % label)
raise exc.UnboundExecutionError(msg)
return e._execute_clauseelement(self, multiparams, params)
@@ -2742,6 +2714,29 @@ class Executable(_Generative):
"""
return self.execute(*multiparams, **params).scalar()
+ @property
+ def bind(self):
+ """Returns the :class:`.Engine` or :class:`.Connection` to
+ which this :class:`.Executable` is bound, or None if none found.
+
+ This is a traversal which checks locally, then
+ checks among the "from" clauses of associated objects
+ until a bound engine or connection is found.
+
+ """
+ if self._bind is not None:
+ return self._bind
+
+ for f in _from_objects(self):
+ if f is self:
+ continue
+ engine = f.bind
+ if engine is not None:
+ return engine
+ else:
+ return None
+
+
# legacy, some outside users may be calling this
_Executable = Executable