]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Check for supports_execution at ClauseElement base
authorMike Bayer <mike_mp@zzzcomputing.com>
Wed, 31 Aug 2016 18:34:54 +0000 (14:34 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Wed, 31 Aug 2016 21:04:35 +0000 (17:04 -0400)
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
doc/build/changelog/changelog_11.rst
lib/sqlalchemy/engine/base.py
lib/sqlalchemy/engine/default.py
lib/sqlalchemy/exc.py
lib/sqlalchemy/sql/compiler.py
lib/sqlalchemy/sql/elements.py
lib/sqlalchemy/sql/schema.py
test/engine/test_execute.py

index ba967f9762a255855615184e9778a2461df859ff..88f1ebb244f5568a70076edcf767f3ccd7a5554c 100644 (file)
 .. changelog::
     :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
index ff855a5390f0a86188f64e41231b1d642402f264..83f0f0c83a9ba0ceb8719d489dff2668f547857a 100644 (file)
@@ -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)
 
index 1bb575984ab2a58ee395f620352a5404f312adc2..bd6e37d6ca80a77279477cca6c3f1ffbd77042b9 100644 (file)
@@ -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)
index 27298422938b26a2c2f79a106b39cd748f84b192..2a7f84de3313694c7d1bacad9f2851d677bbd625 100644 (file)
@@ -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."""
index 095c84f03b9b27e618ecda6e4ab133443c9caafd..85d5ff6da8bcfb604c85991cb5bf616ffb8743ab 100644 (file)
@@ -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):
index e277b28a4cd0ac3299b1e558785088ddcac0825e..75d5368d5801a6eea20469f4fdf9f07ffa5ae1f4 100644 (file)
@@ -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.
index e364b2e7f28fadbaf4d6791a98532cec3fd2534f..98a96fd56fb61f5595647755fdfbf39de104f371 100644 (file)
@@ -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."""
index 8e553307fdf1dfc6565a2eec8740cca2078fd00a..49b29f7f210f8f8eaafa7c4b4a4d797b2ac9800f 100644 (file)
@@ -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: