- added "autocommit=True" kwarg to select() and text(),

as well as generative autocommit() method on select();
for statements which modify the database through some
user-defined means other than the usual INSERT/UPDATE/
DELETE etc., this flag will enable "autocommit" behavior
during execution if no transaction is in progress
[ticket:915]
This commit is contained in:
Mike Bayer
2008-01-31 17:48:22 +00:00
parent e13fdb965f
commit e1aa7573f2
4 changed files with 108 additions and 5 deletions
+8
View File
@@ -16,6 +16,14 @@ CHANGES
- cast() accepts text('something') and other non-literal
operands properly [ticket:962]
- added "autocommit=True" kwarg to select() and text(),
as well as generative autocommit() method on select();
for statements which modify the database through some
user-defined means other than the usual INSERT/UPDATE/
DELETE etc., this flag will enable "autocommit" behavior
during execution if no transaction is in progress
[ticket:915]
- The '.c.' attribute on a selectable now gets an entry
for every column expression in its columns clause.
+2 -2
View File
@@ -164,10 +164,10 @@ class DefaultExecutionContext(base.ExecutionContext):
self.isupdate = compiled.isupdate
if isinstance(compiled.statement, expression._TextClause):
self.returns_rows = self.returns_rows_text(self.statement)
self.should_autocommit = self.should_autocommit_text(self.statement)
self.should_autocommit = compiled.statement._autocommit or self.should_autocommit_text(self.statement)
else:
self.returns_rows = self.returns_rows_compiled(compiled)
self.should_autocommit = self.should_autocommit_compiled(compiled)
self.should_autocommit = getattr(compiled.statement, '_autocommit', False) or self.should_autocommit_compiled(compiled)
if not parameters:
self.compiled_parameters = [compiled.construct_params()]
+23 -3
View File
@@ -158,6 +158,11 @@ def select(columns=None, whereclause=None, from_obj=[], **kwargs):
\**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.
prefixes
a list of strings or ``ClauseElement`` objects to include
directly after the SELECT keyword in the generated statement,
@@ -727,6 +732,11 @@ def text(text, bind=None, *args, **kwargs):
bind
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.
bindparams
a list of ``bindparam()`` instances which can be used to define
the types and/or initial values for the bind parameters within
@@ -740,6 +750,7 @@ def text(text, bind=None, *args, **kwargs):
which will be used to perform post-processing on columns within
the result set (for textual statements that produce result
sets).
"""
return _TextClause(text, bind=bind, *args, **kwargs)
@@ -1821,10 +1832,11 @@ class _TextClause(ClauseElement):
_bind_params_regex = re.compile(r'(?<![:\w\x5c]):(\w+)(?!:)', re.UNICODE)
def __init__(self, text = "", bind=None, bindparams=None, typemap=None):
def __init__(self, text = "", bind=None, bindparams=None, typemap=None, autocommit=False):
self._bind = bind
self.bindparams = {}
self.typemap = typemap
self._autocommit = autocommit
if typemap is not None:
for key in typemap.keys():
typemap[key] = sqltypes.to_instance(typemap[key])
@@ -2711,9 +2723,10 @@ class TableClause(FromClause):
class _SelectBaseMixin(object):
"""Base class for ``Select`` and ``CompoundSelects``."""
def __init__(self, use_labels=False, for_update=False, limit=None, offset=None, order_by=None, group_by=None, bind=None):
def __init__(self, use_labels=False, for_update=False, limit=None, offset=None, order_by=None, group_by=None, bind=None, autocommit=False):
self.use_labels = use_labels
self.for_update = for_update
self._autocommit = autocommit
self._limit = limit
self._offset = offset
self._bind = bind
@@ -2734,7 +2747,7 @@ class _SelectBaseMixin(object):
return _ScalarSelect(self)
def apply_labels(self):
"""set the 'labels' flag on this selectable.
"""return a new selectable with the 'use_labels' flag set to True.
This will result in column expressions being generated using labels against their table
name, such as "SELECT somecolumn AS tablename_somecolumn". This allows selectables which
@@ -2760,6 +2773,13 @@ class _SelectBaseMixin(object):
return True
def autocommit(self):
"""return a new selectable with the 'autocommit' flag set to True."""
s = self._generate()
s._autocommit = True
return s
def _generate(self):
s = self._clone()
s._clone_from_clause()
+75
View File
@@ -375,6 +375,81 @@ class AutoRollbackTest(PersistTest):
users.drop(conn2)
conn2.close()
class ExplicitAutoCommitTest(PersistTest):
"""test the 'autocommit' flag on select() and text() objects.
Requires Postgres so that we may define a custom function which modifies the database.
"""
__only_on__ = 'postgres'
def setUpAll(self):
global metadata, foo
metadata = MetaData(testing.db)
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")
def tearDown(self):
foo.delete().execute()
def tearDownAll(self):
testing.db.execute("drop function insert_foo(varchar)")
metadata.drop_all()
def test_control(self):
# test that not using autocommit does not commit
conn1 = testing.db.connect()
conn2 = testing.db.connect()
conn1.execute(select([func.insert_foo('data1')]))
assert conn2.execute(select([foo.c.data])).fetchall() == []
conn1.execute(text("select insert_foo('moredata')"))
assert conn2.execute(select([foo.c.data])).fetchall() == []
trans = conn1.begin()
trans.commit()
assert conn2.execute(select([foo.c.data])).fetchall() == [('data1',), ('moredata',)]
conn1.close()
conn2.close()
def test_explicit_compiled(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',)]
conn1.execute(select([func.insert_foo('data2')]).autocommit())
assert conn2.execute(select([foo.c.data])).fetchall() == [('data1',), ('data2',)]
conn1.close()
conn2.close()
def test_explicit_text(self):
conn1 = testing.db.connect()
conn2 = testing.db.connect()
conn1.execute(text("select insert_foo('moredata')", autocommit=True))
assert conn2.execute(select([foo.c.data])).fetchall() == [('moredata',)]
conn1.close()
conn2.close()
def test_implicit_text(self):
conn1 = testing.db.connect()
conn2 = testing.db.connect()
conn1.execute(text("insert into foo (data) values ('implicitdata')"))
assert conn2.execute(select([foo.c.data])).fetchall() == [('implicitdata',)]
conn1.close()
conn2.close()
class TLTransactionTest(PersistTest):
def setUpAll(self):
global users, metadata, tlengine