- 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.
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()]
\**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,
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
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)
_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])
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
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
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()
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