diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2010-04-07 14:00:23 -0400 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2010-04-07 14:00:23 -0400 |
| commit | a998ddf0c5737712dff8e653a10cd749da8dfbc8 (patch) | |
| tree | 79ee9afde1afc9db2f77c413b6fb96eb80c3c8dc | |
| parent | 8b82b506eb6b5c35fcd897af230d41733e3c8444 (diff) | |
| parent | 5d61549f3db69b6edc75cc07cb3d114a5c9aee50 (diff) | |
| download | sqlalchemy-a998ddf0c5737712dff8e653a10cd749da8dfbc8.tar.gz | |
merge default
| -rw-r--r-- | CHANGES | 13 | ||||
| -rw-r--r-- | README.unittests | 7 | ||||
| -rw-r--r-- | lib/sqlalchemy/engine/base.py | 32 | ||||
| -rw-r--r-- | lib/sqlalchemy/sql/expression.py | 17 | ||||
| -rw-r--r-- | test/engine/test_execute.py | 31 | ||||
| -rw-r--r-- | test/engine/test_transaction.py | 13 |
6 files changed, 106 insertions, 7 deletions
@@ -69,6 +69,19 @@ CHANGES - Fixed an error in expression typing which caused an endless loop for expressions with two NULL types. + + - Fixed bug in execution_options() feature whereby the existing + Transaction and other state information from the parent + connection would not be propagated to the sub-connection. + + - Added new 'compiled_cache' execution option. A dictionary + where Compiled objects will be cached when the Connection + compiles a clause expression into a dialect- and parameter- + specific Compiled object. It is the user's responsibility to + manage the size of this dictionary, which will have keys + corresponding to the dialect, clause element, the column + names within the VALUES or SET clause of an INSERT or UPDATE, + as well as the "batch" mode for an INSERT or UPDATE statement. - ext - the compiler extension now allows @compiles decorators diff --git a/README.unittests b/README.unittests index dee34a106..dd2f6ab1b 100644 --- a/README.unittests +++ b/README.unittests @@ -13,6 +13,13 @@ http://somethingaboutorange.com/mrl/projects/nose/0.11.1/index.html SQLAlchemy implements a nose plugin that must be present when tests are run. This plugin is available when SQLAlchemy is installed via setuptools. +NB: You will need to manually install nose, it is unlikely to be pulled + down as a dependency of installing SQLAlchemy. + + Nose can be installed with: + + $ easy_install nose + INSTANT TEST RUNNER ------------------- diff --git a/lib/sqlalchemy/engine/base.py b/lib/sqlalchemy/engine/base.py index dc42ed957..4c5a6a82b 100644 --- a/lib/sqlalchemy/engine/base.py +++ b/lib/sqlalchemy/engine/base.py @@ -794,6 +794,14 @@ class Connection(Connectable): """ return self.engine.Connection(self.engine, self.__connection, _branch=True) + + def _clone(self): + """Create a shallow copy of this Connection. + + """ + c = self.__class__.__new__(self.__class__) + c.__dict__ = self.__dict__.copy() + return c def execution_options(self, **opt): """ Set non-SQL options for the connection which take effect during execution. @@ -811,9 +819,9 @@ class Connection(Connectable): :meth:`sqlalchemy.sql.expression.Executable.execution_options`. """ - return self.engine.Connection( - self.engine, self.__connection, - _branch=self.__branch, _execution_options=opt) + c = self._clone() + c._execution_options = c._execution_options.union(opt) + return c @property def dialect(self): @@ -1142,10 +1150,22 @@ class Connection(Connectable): else: keys = [] + if 'compiled_cache' in self._execution_options: + key = self.dialect, elem, tuple(keys), len(params) > 1 + if key in self._execution_options['compiled_cache']: + compiled_sql = self._execution_options['compiled_cache'][key] + else: + compiled_sql = elem.compile( + dialect=self.dialect, column_keys=keys, + inline=len(params) > 1) + self._execution_options['compiled_cache'][key] = compiled_sql + else: + compiled_sql = elem.compile( + dialect=self.dialect, column_keys=keys, + inline=len(params) > 1) + context = self.__create_execution_context( - compiled_sql=elem.compile( - dialect=self.dialect, column_keys=keys, - inline=len(params) > 1), + compiled_sql=compiled_sql, parameters=params ) return self.__execute_context(context) diff --git a/lib/sqlalchemy/sql/expression.py b/lib/sqlalchemy/sql/expression.py index 5958a0bc4..1222a144f 100644 --- a/lib/sqlalchemy/sql/expression.py +++ b/lib/sqlalchemy/sql/expression.py @@ -2276,6 +2276,23 @@ class Executable(_Generative): of many DBAPIs. The flag is currently understood only by the psycopg2 dialect. + * compiled_cache - a dictionary where :class:`Compiled` objects + will be cached when the :class:`Connection` compiles a clause + expression into a dialect- and parameter-specific + :class:`Compiled` object. It is the user's responsibility to + manage the size of this dictionary, which will have keys + corresponding to the dialect, clause element, the column + names within the VALUES or SET clause of an INSERT or UPDATE, + as well as the "batch" mode for an INSERT or UPDATE statement. + The format of this dictionary is not guaranteed to stay the + same in future releases. + + This option is usually more appropriate + to use via the + :meth:`sqlalchemy.engine.base.Connection.execution_options()` + method of :class:`Connection`, rather than upon individual + statement objects, though the effect is the same. + See also: :meth:`sqlalchemy.engine.base.Connection.execution_options()` diff --git a/test/engine/test_execute.py b/test/engine/test_execute.py index 8fd5e7eb6..e83166c9a 100644 --- a/test/engine/test_execute.py +++ b/test/engine/test_execute.py @@ -111,6 +111,37 @@ class ExecuteTest(TestBase): (1, None) ]) +class CompiledCacheTest(TestBase): + @classmethod + def setup_class(cls): + global users, metadata + metadata = MetaData(testing.db) + users = Table('users', metadata, + Column('user_id', INT, primary_key = True), + Column('user_name', VARCHAR(20)), + ) + metadata.create_all() + + @engines.close_first + def teardown(self): + testing.db.connect().execute(users.delete()) + + @classmethod + def teardown_class(cls): + metadata.drop_all() + + def test_cache(self): + conn = testing.db.connect() + cache = {} + cached_conn = conn.execution_options(compiled_cache=cache) + + ins = users.insert() + cached_conn.execute(ins, {'user_name':'u1'}) + cached_conn.execute(ins, {'user_name':'u2'}) + cached_conn.execute(ins, {'user_name':'u3'}) + assert len(cache) == 1 + eq_(conn.execute("select count(1) from users").scalar(), 3) + class LogTest(TestBase): def _test_logger(self, eng, eng_name, pool_name): buf = logging.handlers.BufferingHandler(100) diff --git a/test/engine/test_transaction.py b/test/engine/test_transaction.py index e8da89438..f6cb9a473 100644 --- a/test/engine/test_transaction.py +++ b/test/engine/test_transaction.py @@ -120,7 +120,18 @@ class TransactionTest(TestBase): finally: connection.close() - + def test_retains_through_options(self): + connection = testing.db.connect() + try: + transaction = connection.begin() + connection.execute(users.insert(), user_id=1, user_name='user1') + conn2 = connection.execution_options(dummy=True) + conn2.execute(users.insert(), user_id=2, user_name='user2') + transaction.rollback() + eq_(connection.scalar("select count(1) from query_users"), 0) + finally: + connection.close() + def test_nesting(self): connection = testing.db.connect() transaction = connection.begin() |
