.. 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
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)
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)
"""
+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."""
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):
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.
__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."""
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."""
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: