summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES15
-rw-r--r--lib/sqlalchemy/engine/base.py37
-rw-r--r--lib/sqlalchemy/engine/default.py7
-rw-r--r--test/engine/test_execute.py10
4 files changed, 64 insertions, 5 deletions
diff --git a/CHANGES b/CHANGES
index 29e8c2ba0..a25c5f45a 100644
--- a/CHANGES
+++ b/CHANGES
@@ -71,6 +71,21 @@ CHANGES
with no columns at all. [ticket:2380]
- engine
+ - [feature] Added "no_parameters=True" execution
+ option for connections. If no parameters
+ are present, will pass the statement
+ as cursor.execute(statement), thereby invoking
+ the DBAPIs behavior when no parameter collection
+ is present; for psycopg2 and mysql-python, this
+ means not interpreting % signs in the string.
+ This only occurs with this option, and not
+ just if the param list is blank, as otherwise
+ this would produce inconsistent behavior
+ of SQL expressions that normally escape percent
+ signs (and while compiling, can't know ahead of
+ time if parameters will be present in
+ some cases). [ticket:2407]
+
- [feature] Added pool_reset_on_return argument
to create_engine, allows control over
"connection return" behavior. Also added
diff --git a/lib/sqlalchemy/engine/base.py b/lib/sqlalchemy/engine/base.py
index 0224d0843..7cc5522c4 100644
--- a/lib/sqlalchemy/engine/base.py
+++ b/lib/sqlalchemy/engine/base.py
@@ -491,14 +491,23 @@ class Dialect(object):
raise NotImplementedError()
def do_executemany(self, cursor, statement, parameters, context=None):
- """Provide an implementation of *cursor.executemany(statement,
- parameters)*."""
+ """Provide an implementation of ``cursor.executemany(statement,
+ parameters)``."""
raise NotImplementedError()
def do_execute(self, cursor, statement, parameters, context=None):
- """Provide an implementation of *cursor.execute(statement,
- parameters)*."""
+ """Provide an implementation of ``cursor.execute(statement,
+ parameters)``."""
+
+ raise NotImplementedError()
+
+ def do_execute_no_params(self, cursor, statement, parameters, context=None):
+ """Provide an implementation of ``cursor.execute(statement)``.
+
+ The parameter collection should not be sent.
+
+ """
raise NotImplementedError()
@@ -976,6 +985,18 @@ class Connection(Connectable):
is returned to the connection pool, i.e.
the :meth:`.Connection.close` method is called.
+ :param no_parameters: When ``True``, if the final parameter
+ list or dictionary is totally empty, will invoke the
+ statement on the cursor as ``cursor.execute(statement)``,
+ not passing the parameter collection at all.
+ Some DBAPIs such as psycopg2 and mysql-python consider
+ percent signs as significant only when parameters are
+ present; this option allows code to generate SQL
+ containing percent signs (and possibly other characters)
+ that is neutral regarding whether it's executed by the DBAPI
+ or piped into a script that's later invoked by
+ command line tools. New in 0.7.6.
+
:param stream_results: Available on: Connection, statement.
Indicate to the dialect that results should be
"streamed" and not pre-buffered, if possible. This is a limitation
@@ -1647,7 +1668,8 @@ class Connection(Connectable):
if self._echo:
self.engine.logger.info(statement)
- self.engine.logger.info("%r", sql_util._repr_params(parameters, batches=10))
+ self.engine.logger.info("%r",
+ sql_util._repr_params(parameters, batches=10))
try:
if context.executemany:
self.dialect.do_executemany(
@@ -1655,6 +1677,11 @@ class Connection(Connectable):
statement,
parameters,
context)
+ elif not parameters and context.no_parameters:
+ self.dialect.do_execute_no_params(
+ cursor,
+ statement,
+ context)
else:
self.dialect.do_execute(
cursor,
diff --git a/lib/sqlalchemy/engine/default.py b/lib/sqlalchemy/engine/default.py
index a1e5a5799..5c2d98146 100644
--- a/lib/sqlalchemy/engine/default.py
+++ b/lib/sqlalchemy/engine/default.py
@@ -334,6 +334,9 @@ class DefaultDialect(base.Dialect):
def do_execute(self, cursor, statement, parameters, context=None):
cursor.execute(statement, parameters)
+ def do_execute_no_params(self, cursor, statement, context=None):
+ cursor.execute(statement)
+
def is_disconnect(self, e, connection, cursor):
return False
@@ -538,6 +541,10 @@ class DefaultExecutionContext(base.ExecutionContext):
return self
@util.memoized_property
+ def no_parameters(self):
+ return self.execution_options.get("no_parameters", False)
+
+ @util.memoized_property
def is_crud(self):
return self.isinsert or self.isupdate or self.isdelete
diff --git a/test/engine/test_execute.py b/test/engine/test_execute.py
index f3206e9cc..dc3e46aab 100644
--- a/test/engine/test_execute.py
+++ b/test/engine/test_execute.py
@@ -41,6 +41,16 @@ class ExecuteTest(fixtures.TestBase):
def teardown_class(cls):
metadata.drop_all()
+ def test_no_params_option(self):
+ stmt = "SELECT '%'"
+ if testing.against('oracle'):
+ stmt += " FROM DUAL"
+ conn = testing.db.connect()
+ result = conn.\
+ execution_options(no_parameters=True).\
+ scalar(stmt)
+ eq_(result, '%')
+
@testing.fails_on_everything_except('firebird', 'maxdb',
'sqlite', '+pyodbc',
'+mxodbc', '+zxjdbc', 'mysql+oursql',