summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES16
-rw-r--r--lib/sqlalchemy/engine/default.py24
-rw-r--r--lib/sqlalchemy/schema.py3
-rw-r--r--lib/sqlalchemy/sql/expression.py63
-rw-r--r--test/engine/test_transaction.py73
5 files changed, 140 insertions, 39 deletions
diff --git a/CHANGES b/CHANGES
index f8ebfbb41..aef444c38 100644
--- a/CHANGES
+++ b/CHANGES
@@ -240,6 +240,12 @@ CHANGES
Use the 'bind' keyword argument.
- sql
+
+ - the "autocommit" flag on select() and text() as well
+ as select().autocommit() are deprecated - now call
+ .execution_options(autocommit=True) on either of those
+ constructs, also available directly on Connection and orm.Query.
+
- the autoincrement flag on column now indicates the column
which should be linked to cursor.lastrowid, if that method
is used. See the API docs for details.
@@ -349,14 +355,16 @@ CHANGES
- transaction isolation level may be specified with
create_engine(... isolation_level="..."); available on
postgresql and sqlite. [ticket:443]
-
+
- Connection has execution_options(), generative method
which accepts keywords that affect how the statement
is executed w.r.t. the DBAPI. Currently supports
"stream_results", causes psycopg2 to use a server
- side cursor for that statement. Can also be set
- upon select() and text() constructs directly as well
- as ORM Query().
+ side cursor for that statement, as well as
+ "autocommit", which is the new location for the "autocommit"
+ option from select() and text(). select() and
+ text() also have .execution_options() as well as
+ ORM Query().
- fixed the import for entrypoint-driven dialects to
not rely upon silly tb_info trick to determine import
diff --git a/lib/sqlalchemy/engine/default.py b/lib/sqlalchemy/engine/default.py
index e168a5465..e2a03d227 100644
--- a/lib/sqlalchemy/engine/default.py
+++ b/lib/sqlalchemy/engine/default.py
@@ -229,7 +229,6 @@ class DefaultExecutionContext(base.ExecutionContext):
self.dialect = dialect
self._connection = self.root_connection = connection
self.engine = connection.engine
- self.execution_options = connection._execution_options
if compiled_ddl is not None:
self.compiled = compiled = compiled_ddl
@@ -268,9 +267,6 @@ class DefaultExecutionContext(base.ExecutionContext):
self.isinsert = compiled.isinsert
self.isupdate = compiled.isupdate
self.isdelete = compiled.isdelete
- if compiled.statement._execution_options:
- self.execution_options =\
- compiled.statement._execution_options.union(self.execution_options)
if not parameters:
self.compiled_parameters = [compiled.construct_params()]
@@ -301,18 +297,24 @@ class DefaultExecutionContext(base.ExecutionContext):
self.cursor = self.create_cursor()
@util.memoized_property
- def should_autocommit(self):
+ def execution_options(self):
if self.compiled:
- autocommit = self.compiled.statement._autocommit
- if autocommit is expression.PARSE_AUTOCOMMIT:
+ return self.compiled.statement._execution_options.union(
+ self._connection._execution_options)
+ else:
+ return self._connection._execution_options
+
+ @util.memoized_property
+ def should_autocommit(self):
+ autocommit = self.execution_options.get('autocommit', expression.PARSE_AUTOCOMMIT)
+ if autocommit is expression.PARSE_AUTOCOMMIT:
+ if self.statement:
return self.should_autocommit_text(self.statement)
else:
- return autocommit
- elif self.statement:
- return self.should_autocommit_text(self.statement)
+ return False
else:
- return False
+ return autocommit
@util.memoized_property
def _is_explicit_returning(self):
diff --git a/lib/sqlalchemy/schema.py b/lib/sqlalchemy/schema.py
index f2737ecde..cd9bd4892 100644
--- a/lib/sqlalchemy/schema.py
+++ b/lib/sqlalchemy/schema.py
@@ -2035,7 +2035,8 @@ class SchemaVisitor(visitors.ClauseVisitor):
class DDLElement(expression._Executable, expression.ClauseElement):
"""Base class for DDL expression constructs."""
- _autocommit = True
+ _execution_options = expression._Executable.\
+ _execution_options.union({'autocommit':True})
target = None
on = None
diff --git a/lib/sqlalchemy/sql/expression.py b/lib/sqlalchemy/sql/expression.py
index a4504e833..5edb6e47f 100644
--- a/lib/sqlalchemy/sql/expression.py
+++ b/lib/sqlalchemy/sql/expression.py
@@ -164,9 +164,8 @@ def select(columns=None, whereclause=None, from_obj=[], **kwargs):
Additional parameters include:
autocommit
- indicates this SELECT statement modifies the database, and
- should be subject to autocommit behavior if no transaction
- has been started.
+ Deprecated. Use .execution_options(autocommit=<True|False>)
+ to set the autocommit option.
prefixes
a list of strings or :class:`ClauseElement` objects to include
@@ -805,9 +804,8 @@ def text(text, bind=None, *args, **kwargs):
an optional connection or engine to be used for this text query.
autocommit=True
- indicates this SELECT statement modifies the database, and
- should be subject to autocommit behavior if no transaction
- has been started.
+ Deprecated. Use .execution_options(autocommit=<True|False>)
+ to set the autocommit option.
bindparams
a list of :func:`bindparam()` instances which can be used to define
@@ -2215,9 +2213,26 @@ class _Executable(object):
@_generative
def execution_options(self, **kw):
- """ Set non-SQL options for the statement, such as dialect-specific options.
-
- The options available are covered in the respective dialect's section.
+ """ Set non-SQL options for the statement which take effect during execution.
+
+ Current options include:
+
+ * autocommit - when True, a COMMIT will be invoked after execution
+ when executed in 'autocommit' mode, i.e. when an explicit transaction
+ is not begun on the connection. Note that DBAPI connections by
+ default are always in a transaction - SQLAlchemy uses rules applied
+ to different kinds of statements to determine if COMMIT will be invoked
+ in order to provide its "autocommit" feature. Typically, all
+ INSERT/UPDATE/DELETE statements as well as CREATE/DROP statements
+ have autocommit behavior enabled; SELECT constructs do not. Use this
+ option when invokving a SELECT or other specific SQL construct
+ where COMMIT is desired (typically when calling stored procedures
+ and such).
+
+ * stream_results - indicate to the dialect that results should be
+ "streamed" and not pre-buffered, if possible. This is a limitation
+ of many DBAPIs. The flag is currently understood only by the
+ psycopg2 dialect.
"""
self._execution_options = self._execution_options.union(kw)
@@ -2233,7 +2248,8 @@ class _TextClause(_Executable, ClauseElement):
__visit_name__ = 'textclause'
_bind_params_regex = re.compile(r'(?<![:\w\x5c]):(\w+)(?!:)', re.UNICODE)
-
+ _execution_options = _Executable._execution_options.union({'autocommit':PARSE_AUTOCOMMIT})
+
@property
def _select_iterable(self):
return (self,)
@@ -2242,11 +2258,16 @@ class _TextClause(_Executable, ClauseElement):
def __init__(self, text = "", bind=None,
bindparams=None, typemap=None,
- autocommit=PARSE_AUTOCOMMIT):
+ autocommit=None):
self._bind = bind
self.bindparams = {}
self.typemap = typemap
- self._autocommit = autocommit
+
+ if autocommit is not None:
+ util.warn_deprecated("autocommit on text() is deprecated. "
+ "Use .execution_options(autocommit=True)")
+ self._execution_options = self._execution_options.union({'autocommit':autocommit})
+
if typemap is not None:
for key in typemap.keys():
typemap[key] = sqltypes.to_instance(typemap[key])
@@ -2856,7 +2877,6 @@ class Alias(FromClause):
self.original = baseselectable
self.supports_execution = baseselectable.supports_execution
if self.supports_execution:
- self._autocommit = baseselectable._autocommit
self._execution_options = baseselectable._execution_options
self.element = selectable
if alias is None:
@@ -3229,10 +3249,13 @@ class _SelectBaseMixin(_Executable):
order_by=None,
group_by=None,
bind=None,
- autocommit=False):
+ autocommit=None):
self.use_labels = use_labels
self.for_update = for_update
- self._autocommit = autocommit
+ if autocommit is not None:
+ util.warn_deprecated("autocommit on select() is deprecated. "
+ "Use .execution_options(autocommit=True)")
+ self._execution_options = self._execution_options.union({'autocommit':autocommit})
self._limit = limit
self._offset = offset
self._bind = bind
@@ -3276,10 +3299,12 @@ class _SelectBaseMixin(_Executable):
return self.as_scalar().label(name)
@_generative
+ @util.deprecated(message="autocommit() is deprecated. "
+ "Use .execution_options(autocommit=True)")
def autocommit(self):
"""return a new selectable with the 'autocommit' flag set to True."""
-
- self._autocommit = True
+
+ self._execution_options = self._execution_options.union({'autocommit':True})
def _generate(self):
s = self.__class__.__new__(self.__class__)
@@ -3864,7 +3889,7 @@ class _UpdateBase(_Executable, ClauseElement):
__visit_name__ = 'update_base'
- _autocommit = True
+ _execution_options = _Executable._execution_options.union({'autocommit':True})
def _generate(self):
s = self.__class__.__new__(self.__class__)
@@ -4118,7 +4143,7 @@ class Delete(_UpdateBase):
class _IdentifiedClause(_Executable, ClauseElement):
__visit_name__ = 'identified'
- _autocommit = False
+ _execution_options = _Executable._execution_options.union({'autocommit':False})
quote = None
def __init__(self, ident):
diff --git a/test/engine/test_transaction.py b/test/engine/test_transaction.py
index a58d65e68..fa2cc3e59 100644
--- a/test/engine/test_transaction.py
+++ b/test/engine/test_transaction.py
@@ -369,7 +369,6 @@ class AutoRollbackTest(TestBase):
users.drop(conn2)
conn2.close()
-foo = None
class ExplicitAutoCommitTest(TestBase):
"""test the 'autocommit' flag on select() and text() objects.
@@ -382,9 +381,13 @@ class ExplicitAutoCommitTest(TestBase):
def setup_class(cls):
global metadata, foo
metadata = MetaData(testing.db)
- foo = Table('foo', metadata, Column('id', Integer, primary_key=True), Column('data', String(100)))
+ foo = Table('foo', metadata,
+ Column('id', Integer, primary_key=True),
+ Column('data', String(100))
+ )
metadata.create_all()
- testing.db.execute("create function insert_foo(varchar) returns integer as 'insert into foo(data) values ($1);select 1;' language sql")
+ testing.db.execute("create function insert_foo(varchar) returns integer "
+ "as 'insert into foo(data) values ($1);select 1;' language sql")
def teardown(self):
foo.delete().execute().close()
@@ -417,6 +420,67 @@ class ExplicitAutoCommitTest(TestBase):
conn1 = testing.db.connect()
conn2 = testing.db.connect()
+ conn1.execute(select([func.insert_foo('data1')]).execution_options(autocommit=True))
+ assert conn2.execute(select([foo.c.data])).fetchall() == [('data1',)]
+
+ conn1.close()
+ conn2.close()
+
+ def test_explicit_connection(self):
+ conn1 = testing.db.connect()
+ conn2 = testing.db.connect()
+
+ conn1.execution_options(autocommit=True).\
+ execute(select([func.insert_foo('data1')]))
+ eq_(
+ conn2.execute(select([foo.c.data])).fetchall(),
+ [('data1',), ]
+ )
+
+ # connection supercedes statement
+ conn1.execution_options(autocommit=False).\
+ execute(
+ select([func.insert_foo('data2')]).
+ execution_options(autocommit=True)
+ )
+ eq_(
+ conn2.execute(select([foo.c.data])).fetchall(),
+ [('data1',), ]
+ )
+
+ # ditto
+ conn1.execution_options(autocommit=True).\
+ execute(
+ select([func.insert_foo('data3')]).
+ execution_options(autocommit=False)
+ )
+ eq_(
+ conn2.execute(select([foo.c.data])).fetchall(),
+ [('data1',), ('data2', ), ('data3',)]
+ )
+
+ conn1.close()
+ conn2.close()
+
+ def test_explicit_text(self):
+ conn1 = testing.db.connect()
+ conn2 = testing.db.connect()
+
+ conn1.execute(
+ text("select insert_foo('moredata')").
+ execution_options(autocommit=True)
+ )
+ assert conn2.execute(select([foo.c.data])).fetchall() == [('moredata',)]
+
+ conn1.close()
+ conn2.close()
+
+ @testing.uses_deprecated(r'autocommit on select\(\) is deprecated',
+ r'autocommit\(\) is deprecated')
+ def test_explicit_compiled_deprecated(self):
+ conn1 = testing.db.connect()
+ conn2 = testing.db.connect()
+
conn1.execute(select([func.insert_foo('data1')], autocommit=True))
assert conn2.execute(select([foo.c.data])).fetchall() == [('data1',)]
@@ -426,7 +490,8 @@ class ExplicitAutoCommitTest(TestBase):
conn1.close()
conn2.close()
- def test_explicit_text(self):
+ @testing.uses_deprecated(r'autocommit on text\(\) is deprecated')
+ def test_explicit_text_deprecated(self):
conn1 = testing.db.connect()
conn2 = testing.db.connect()