summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2010-04-07 14:00:23 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2010-04-07 14:00:23 -0400
commita998ddf0c5737712dff8e653a10cd749da8dfbc8 (patch)
tree79ee9afde1afc9db2f77c413b6fb96eb80c3c8dc
parent8b82b506eb6b5c35fcd897af230d41733e3c8444 (diff)
parent5d61549f3db69b6edc75cc07cb3d114a5c9aee50 (diff)
downloadsqlalchemy-a998ddf0c5737712dff8e653a10cd749da8dfbc8.tar.gz
merge default
-rw-r--r--CHANGES13
-rw-r--r--README.unittests7
-rw-r--r--lib/sqlalchemy/engine/base.py32
-rw-r--r--lib/sqlalchemy/sql/expression.py17
-rw-r--r--test/engine/test_execute.py31
-rw-r--r--test/engine/test_transaction.py13
6 files changed, 106 insertions, 7 deletions
diff --git a/CHANGES b/CHANGES
index cfba6f4a4..550428a44 100644
--- a/CHANGES
+++ b/CHANGES
@@ -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()