summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2016-08-31 14:34:54 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2016-08-31 17:04:35 -0400
commitc7b9c84312b6b252e68ea704670d0ea7fc0042f0 (patch)
treea71b8cbbb123e10a779c3a1f8efb4fc09ce47bf1
parent53c3119ebb6801cbfcaf2841311d117eba250444 (diff)
downloadsqlalchemy-c7b9c84312b6b252e68ea704670d0ea7fc0042f0.tar.gz
Check for supports_execution at ClauseElement base
Raise a more descriptive exception / message when ClauseElement or non-SQLAlchemy objects that are not "executable" are erroneously passed to ``.execute()``; a new exception ObjectNotExecutableError is raised consistently in all cases. Change-Id: I2dd393121e2c7e5b6b9e40286a2f25670876e8e4 Fixes: #3786
-rw-r--r--doc/build/changelog/changelog_11.rst9
-rw-r--r--lib/sqlalchemy/engine/base.py4
-rw-r--r--lib/sqlalchemy/engine/default.py5
-rw-r--r--lib/sqlalchemy/exc.py14
-rw-r--r--lib/sqlalchemy/sql/compiler.py5
-rw-r--r--lib/sqlalchemy/sql/elements.py5
-rw-r--r--lib/sqlalchemy/sql/schema.py6
-rw-r--r--test/engine/test_execute.py20
8 files changed, 58 insertions, 10 deletions
diff --git a/doc/build/changelog/changelog_11.rst b/doc/build/changelog/changelog_11.rst
index ba967f976..88f1ebb24 100644
--- a/doc/build/changelog/changelog_11.rst
+++ b/doc/build/changelog/changelog_11.rst
@@ -22,6 +22,15 @@
:version: 1.1.0
.. change::
+ :tags: bug, sql
+ :tickets: 3786
+
+ Raise a more descriptive exception / message when ClauseElement
+ or non-SQLAlchemy objects that are not "executable" are erroneously
+ passed to ``.execute()``; a new exception ObjectNotExecutableError
+ is raised consistently in all cases.
+
+ .. change::
:tags: bug, orm
:tickets: 3776
diff --git a/lib/sqlalchemy/engine/base.py b/lib/sqlalchemy/engine/base.py
index ff855a539..83f0f0c83 100644
--- a/lib/sqlalchemy/engine/base.py
+++ b/lib/sqlalchemy/engine/base.py
@@ -940,9 +940,7 @@ class Connection(Connectable):
try:
meth = object._execute_on_connection
except AttributeError:
- raise exc.InvalidRequestError(
- "Unexecutable object type: %s" %
- type(object))
+ raise exc.ObjectNotExecutableError(object)
else:
return meth(self, multiparams, params)
diff --git a/lib/sqlalchemy/engine/default.py b/lib/sqlalchemy/engine/default.py
index 1bb575984..bd6e37d6c 100644
--- a/lib/sqlalchemy/engine/default.py
+++ b/lib/sqlalchemy/engine/default.py
@@ -554,8 +554,9 @@ class DefaultExecutionContext(interfaces.ExecutionContext):
self.compiled = compiled
- if not compiled.can_execute:
- raise exc.ArgumentError("Not an executable clause")
+ # this should be caught in the engine before
+ # we get here
+ assert compiled.can_execute
self.execution_options = compiled.statement._execution_options.union(
connection._execution_options)
diff --git a/lib/sqlalchemy/exc.py b/lib/sqlalchemy/exc.py
index 272984229..2a7f84de3 100644
--- a/lib/sqlalchemy/exc.py
+++ b/lib/sqlalchemy/exc.py
@@ -26,6 +26,20 @@ class ArgumentError(SQLAlchemyError):
"""
+class ObjectNotExecutableError(ArgumentError):
+ """Raised when an object is passed to .execute() that can't be
+ executed as SQL.
+
+ .. versionadded:: 1.1
+
+ """
+
+ def __init__(self, target):
+ super(ObjectNotExecutableError, self).__init__(
+ "Not an executable object: %r" % target
+ )
+
+
class NoSuchModuleError(ArgumentError):
"""Raised when a dynamically-loaded module (usually a database dialect)
of a particular name cannot be located."""
diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py
index 095c84f03..85d5ff6da 100644
--- a/lib/sqlalchemy/sql/compiler.py
+++ b/lib/sqlalchemy/sql/compiler.py
@@ -215,7 +215,10 @@ class Compiled(object):
pass
def _execute_on_connection(self, connection, multiparams, params):
- return connection._execute_compiled(self, multiparams, params)
+ if self.can_execute:
+ return connection._execute_compiled(self, multiparams, params)
+ else:
+ raise exc.ObjectNotExecutableError(self.statement)
@property
def sql_compiler(self):
diff --git a/lib/sqlalchemy/sql/elements.py b/lib/sqlalchemy/sql/elements.py
index e277b28a4..75d5368d5 100644
--- a/lib/sqlalchemy/sql/elements.py
+++ b/lib/sqlalchemy/sql/elements.py
@@ -259,7 +259,10 @@ class ClauseElement(Visitable):
return self
def _execute_on_connection(self, connection, multiparams, params):
- return connection._execute_clauseelement(self, multiparams, params)
+ if self.supports_execution:
+ return connection._execute_clauseelement(self, multiparams, params)
+ else:
+ raise exc.ObjectNotExecutableError(self)
def unique_params(self, *optionaldict, **kwargs):
"""Return a copy with :func:`bindparam()` elements replaced.
diff --git a/lib/sqlalchemy/sql/schema.py b/lib/sqlalchemy/sql/schema.py
index e364b2e7f..98a96fd56 100644
--- a/lib/sqlalchemy/sql/schema.py
+++ b/lib/sqlalchemy/sql/schema.py
@@ -71,9 +71,6 @@ class SchemaItem(SchemaEventTarget, visitors.Visitable):
__visit_name__ = 'schema_item'
- def _execute_on_connection(self, connection, multiparams, params):
- return connection._execute_default(self, multiparams, params)
-
def _init_items(self, *args):
"""Initialize the list of child items for this SchemaItem."""
@@ -1941,6 +1938,9 @@ class DefaultGenerator(_NotAColumnExpr, SchemaItem):
bind = _bind_or_error(self)
return bind._execute_default(self, **kwargs)
+ def _execute_on_connection(self, connection, multiparams, params):
+ return connection._execute_default(self, multiparams, params)
+
@property
def bind(self):
"""Return the connectable associated with this default."""
diff --git a/test/engine/test_execute.py b/test/engine/test_execute.py
index 8e553307f..49b29f7f2 100644
--- a/test/engine/test_execute.py
+++ b/test/engine/test_execute.py
@@ -306,6 +306,26 @@ class ExecuteTest(fixtures.TestBase):
finally:
conn.close()
+ def test_not_an_executable(self):
+ for obj in (
+ Table("foo", MetaData(), Column("x", Integer)),
+ Column('x', Integer),
+ tsa.and_(),
+ column('foo'),
+ tsa.and_().compile(),
+ column('foo').compile(),
+ MetaData(),
+ Integer(),
+ tsa.Index(name='foo'),
+ tsa.UniqueConstraint('x')
+ ):
+ with testing.db.connect() as conn:
+ assert_raises_message(
+ tsa.exc.ObjectNotExecutableError,
+ "Not an executable object",
+ conn.execute, obj
+ )
+
def test_stmt_exception_non_ascii(self):
name = util.u('méil')
with testing.db.connect() as conn: