.. changelog::
:version: 1.1.0
+ .. change::
+ :tags: bug, sql
+ :tickets: 3805
+
+ Execution options can now be propagated from within a
+ statement at compile time to the outermost statement, so that
+ if an embedded element wants to set "autocommit" to be True for example,
+ it can propagate this to the enclosing statement. Currently, this
+ feature is enabled for a DML-oriented CTE embedded inside of a SELECT
+ statement, e.g. INSERT/UPDATE/DELETE inside of SELECT.
+
.. change::
:tags: bug, orm
:tickets: 3802
self.compiled = compiled = compiled_ddl
self.isddl = True
- self.execution_options = compiled.statement._execution_options
+ self.execution_options = compiled.execution_options
if connection._execution_options:
self.execution_options = dict(self.execution_options)
self.execution_options.update(connection._execution_options)
# we get here
assert compiled.can_execute
- self.execution_options = compiled.statement._execution_options.union(
+ self.execution_options = compiled.execution_options.union(
connection._execution_options)
self.result_column_struct = (
_cached_metadata = None
+ execution_options = util.immutabledict()
+ """
+ Execution options propagated from the statement. In some cases,
+ sub-elements of the statement can modify these.
+ """
+
def __init__(self, dialect, statement, bind=None,
schema_translate_map=None,
compile_kwargs=util.immutabledict()):
if statement is not None:
self.statement = statement
self.can_execute = statement.supports_execution
+ if self.can_execute:
+ self.execution_options = statement._execution_options
self.string = self.process(self.statement, **compile_kwargs)
@util.deprecated("0.7", ":class:`.Compiled` objects now compile "
insert_prefetch = update_prefetch = ()
+
def __init__(self, dialect, statement, column_keys=None,
inline=False, **kwargs):
"""Construct a new :class:`.SQLCompiler` object.
self.ctes_by_name[cte_name] = cte
+ # look for embedded DML ctes and propagate autocommit
+ if 'autocommit' in cte.element._execution_options and \
+ 'autocommit' not in self.execution_options:
+ self.execution_options = self.execution_options.union(
+ {"autocommit": cte.element._execution_options['autocommit']})
+
if cte._cte_alias is not None:
orig_cte = cte._cte_alias
if orig_cte not in self.ctes:
self._do_test(c)
+class ExecutionOptionsTest(fixtures.TestBase):
+ def test_non_dml(self):
+ stmt = table1.select()
+ compiled = stmt.compile()
+
+ eq_(compiled.execution_options, {})
+
+ def test_dml(self):
+ stmt = table1.insert()
+ compiled = stmt.compile()
+
+ eq_(compiled.execution_options, {"autocommit": True})
+
+ def test_embedded_element_true_to_none(self):
+ stmt = table1.insert().cte()
+ eq_(stmt._execution_options, {"autocommit": True})
+ s2 = select([table1]).select_from(stmt)
+ eq_(s2._execution_options, {})
+
+ compiled = s2.compile()
+ eq_(compiled.execution_options, {"autocommit": True})
+
+ def test_embedded_element_true_to_false(self):
+ stmt = table1.insert().cte()
+ eq_(stmt._execution_options, {"autocommit": True})
+ s2 = select([table1]).select_from(stmt).\
+ execution_options(autocommit=False)
+ eq_(s2._execution_options, {"autocommit": False})
+
+ compiled = s2.compile()
+ eq_(compiled.execution_options, {"autocommit": False})
+
+
class CRUDTest(fixtures.TestBase, AssertsCompiledSQL):
__dialect__ = 'default'
-from sqlalchemy.testing import fixtures
+from sqlalchemy.testing import fixtures, eq_
from sqlalchemy.testing import AssertsCompiledSQL, assert_raises_message
from sqlalchemy.sql import table, column, select, func, literal, exists, and_
from sqlalchemy.dialects import mssql
t = products.update().values(price='someprice').\
returning(*products.c).cte('t')
stmt = t.select()
+ assert 'autocommit' not in stmt._execution_options
+ eq_(stmt.compile().execution_options['autocommit'], True)
self.assert_compile(
stmt,
stmt = select([cte])
+ assert 'autocommit' not in stmt._execution_options
+ eq_(stmt.compile().execution_options['autocommit'], True)
+
self.assert_compile(
stmt,
"WITH pd AS "
products = table('products', column('id'), column('price'))
cte = products.select().cte('pd')
+ assert 'autocommit' not in cte._execution_options
stmt = products.update().where(products.c.price == cte.c.price)
+ eq_(stmt.compile().execution_options['autocommit'], True)
self.assert_compile(
stmt,