]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- DefaultRunner and subclasses have been removed. The job
authorMike Bayer <mike_mp@zzzcomputing.com>
Thu, 15 Oct 2009 16:09:59 +0000 (16:09 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Thu, 15 Oct 2009 16:09:59 +0000 (16:09 +0000)
of this object has been simplified and moved into
ExecutionContext.  Dialects which support sequences should
add a `fire_sequence()` method to their execution context
implementation.  [ticket:1566]

15 files changed:
CHANGES
lib/sqlalchemy/dialects/firebird/base.py
lib/sqlalchemy/dialects/firebird/kinterbasdb.py
lib/sqlalchemy/dialects/maxdb/base.py
lib/sqlalchemy/dialects/oracle/base.py
lib/sqlalchemy/dialects/oracle/cx_oracle.py
lib/sqlalchemy/dialects/oracle/zxjdbc.py
lib/sqlalchemy/dialects/postgresql/base.py
lib/sqlalchemy/dialects/postgresql/pg8000.py
lib/sqlalchemy/dialects/postgresql/psycopg2.py
lib/sqlalchemy/dialects/postgresql/pypostgresql.py
lib/sqlalchemy/engine/__init__.py
lib/sqlalchemy/engine/base.py
lib/sqlalchemy/engine/default.py
test/sql/test_unicode.py

diff --git a/CHANGES b/CHANGES
index aec67f28c04238efbe9f5820444c7495a646f7f2..6b3c6ba195cb72eeed5e87fb27efdf3ecc98ef17 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -254,6 +254,12 @@ CHANGES
       which need to hardwire a rowcount in for certain calls
       should override the method to provide different behavior.
 
+    - DefaultRunner and subclasses have been removed.  The job
+      of this object has been simplified and moved into 
+      ExecutionContext.  Dialects which support sequences should
+      add a `fire_sequence()` method to their execution context
+      implementation.  [ticket:1566]
+      
     - Functions and operators generated by the compiler now use
       (almost) regular dispatch functions of the form
       "visit_<opname>" and "visit_<funcname>_fn" to provide
index 5cc837848e694b78f3752c363dfa1e00046cd1f7..4d081025eeaeaa04257a8cfcedf63fd0b0ddcb0a 100644 (file)
@@ -72,6 +72,7 @@ from sqlalchemy.sql import expression
 from sqlalchemy.engine import base, default, reflection
 from sqlalchemy.sql import compiler
 
+
 from sqlalchemy.types import (BIGINT, BLOB, BOOLEAN, CHAR, DATE,
                               FLOAT, INTEGER, NUMERIC, SMALLINT,
                               TEXT, TIME, TIMESTAMP, VARCHAR)
@@ -176,6 +177,8 @@ class FBTypeCompiler(compiler.GenericTypeCompiler):
         return "BLOB SUB_TYPE 0"
 
 
+
+
 class FBCompiler(sql.compiler.SQLCompiler):
     """Firebird specific idiosincrasies"""
 
@@ -280,16 +283,6 @@ class FBDDLCompiler(sql.compiler.DDLCompiler):
             return "DROP GENERATOR %s" % self.preparer.format_sequence(drop.element)
 
 
-class FBDefaultRunner(base.DefaultRunner):
-    """Firebird specific idiosincrasies"""
-
-    def visit_sequence(self, seq):
-        """Get the next value from the sequence using ``gen_id()``."""
-
-        return self.execute_string("SELECT gen_id(%s, 1) FROM rdb$database" % \
-            self.dialect.identifier_preparer.format_sequence(seq))
-
-
 class FBIdentifierPreparer(sql.compiler.IdentifierPreparer):
     """Install Firebird specific reserved words."""
 
@@ -298,7 +291,13 @@ class FBIdentifierPreparer(sql.compiler.IdentifierPreparer):
     def __init__(self, dialect):
         super(FBIdentifierPreparer, self).__init__(dialect, omit_schema=True)
 
+class FBExecutionContext(default.DefaultExecutionContext):
+    def fire_sequence(self, seq):
+        """Get the next value from the sequence using ``gen_id()``."""
 
+        return self._execute_scalar("SELECT gen_id(%s, 1) FROM rdb$database" % \
+            self.dialect.identifier_preparer.format_sequence(seq))
+    
 class FBDialect(default.DefaultDialect):
     """Firebird dialect"""
 
@@ -316,10 +315,10 @@ class FBDialect(default.DefaultDialect):
 
     statement_compiler = FBCompiler
     ddl_compiler = FBDDLCompiler
-    defaultrunner = FBDefaultRunner
     preparer = FBIdentifierPreparer
     type_compiler = FBTypeCompiler
-
+    execution_ctx_cls = FBExecutionContext
+    
     colspecs = colspecs
     ischema_names = ischema_names
 
index 4bbd2aafc23a71ccbb67cfb577f9a80f27696a70..c804af05f1552339bc7445650213b0cdb1346fd7 100644 (file)
@@ -28,7 +28,6 @@ __ http://kinterbasdb.sourceforge.net/dist_docs/usage.html#special_issue_concurr
 """
 
 from sqlalchemy.dialects.firebird.base import FBDialect, FBCompiler
-from sqlalchemy.engine.default import DefaultExecutionContext
 
 
 class Firebird_kinterbasdb(FBDialect):
index b116d6df61b3d490b8811006aa289660ffdd178e..c02a9e204400c18715a9aa3e8e030afdb3ebc96e 100644 (file)
@@ -401,6 +401,12 @@ class MaxDBExecutionContext(default.DefaultExecutionContext):
         else:
             return self.cursor.rowcount
 
+    def fire_sequence(self, seq):
+        if seq.optional:
+            return None
+        return self._execute_scalar("SELECT %s.NEXTVAL FROM DUAL" % (
+            self.dialect.identifier_preparer.format_sequence(seq)))
+
 class MaxDBCachedColumnRow(engine_base.RowProxy):
     """A RowProxy that only runs result_processors once per column."""
 
@@ -610,14 +616,6 @@ class MaxDBCompiler(compiler.SQLCompiler):
                          ')'))
 
 
-class MaxDBDefaultRunner(engine_base.DefaultRunner):
-    def visit_sequence(self, seq):
-        if seq.optional:
-            return None
-        return self.execute_string("SELECT %s.NEXTVAL FROM DUAL" % (
-            self.dialect.identifier_preparer.format_sequence(seq)))
-
-
 class MaxDBIdentifierPreparer(compiler.IdentifierPreparer):
     reserved_words = set([
         'abs', 'absolute', 'acos', 'adddate', 'addtime', 'all', 'alpha',
@@ -805,7 +803,6 @@ class MaxDBDialect(default.DefaultDialect):
     preparer = MaxDBIdentifierPreparer
     statement_compiler = MaxDBCompiler
     ddl_compiler = MaxDBDDLCompiler
-    defaultrunner = MaxDBDefaultRunner
     execution_ctx_cls = MaxDBExecutionContext
 
     colspecs = colspecs
index c141515151e6ddc41f3bc72f5dad8a6ff9881cdb..4f37412ebdcc5e4b0a1a774252035150354d4ea5 100644 (file)
@@ -454,12 +454,6 @@ class OracleDDLCompiler(compiler.DDLCompiler):
         
         return text
 
-class OracleDefaultRunner(base.DefaultRunner):
-    def visit_sequence(self, seq):
-        return self.execute_string("SELECT " + 
-                    self.dialect.identifier_preparer.format_sequence(seq) + 
-                    ".nextval FROM DUAL", ())
-
 class OracleIdentifierPreparer(compiler.IdentifierPreparer):
     
     reserved_words = set([x.lower() for x in RESERVED_WORDS])
@@ -477,6 +471,13 @@ class OracleIdentifierPreparer(compiler.IdentifierPreparer):
         name = re.sub(r'^_+', '', savepoint.ident)
         return super(OracleIdentifierPreparer, self).format_savepoint(savepoint, name)
         
+        
+class OracleExecutionContext(default.DefaultExecutionContext):
+    def fire_sequence(self, seq):
+        return self._execute_scalar("SELECT " + 
+                    self.dialect.identifier_preparer.format_sequence(seq) + 
+                    ".nextval FROM DUAL")
+    
 class OracleDialect(default.DefaultDialect):
     name = 'oracle'
     supports_alter = True
@@ -502,7 +503,7 @@ class OracleDialect(default.DefaultDialect):
     ddl_compiler = OracleDDLCompiler
     type_compiler = OracleTypeCompiler
     preparer = OracleIdentifierPreparer
-    defaultrunner = OracleDefaultRunner
+    execution_ctx_cls = OracleExecutionContext
     
     reflection_options = ('oracle_resolve_synonyms', )
     
index 65f3cb928a38d9c7694e6d3a0efcddfe57e7083b..6108d3d660ff473e5e2578c723d7e9cd31e17a5b 100644 (file)
@@ -74,9 +74,8 @@ with this feature but it should be regarded as experimental.
 
 """
 
-from sqlalchemy.dialects.oracle.base import OracleCompiler, OracleDialect, RESERVED_WORDS
+from sqlalchemy.dialects.oracle.base import OracleCompiler, OracleDialect, RESERVED_WORDS, OracleExecutionContext
 from sqlalchemy.dialects.oracle import base as oracle
-from sqlalchemy.engine.default import DefaultExecutionContext
 from sqlalchemy.engine import base
 from sqlalchemy import types as sqltypes, util
 import datetime
@@ -200,7 +199,7 @@ class Oracle_cx_oracleCompiler(OracleCompiler):
         else:
             return OracleCompiler.bindparam_string(self, name)
 
-class Oracle_cx_oracleExecutionContext(DefaultExecutionContext):
+class Oracle_cx_oracleExecutionContext(OracleExecutionContext):
     def pre_exec(self):
         quoted_bind_names = getattr(self.compiled, '_quoted_bind_names', {})
         if quoted_bind_names:
index 82aac080e12cc83c819e28bdcb34a14b4451f90e..6edef301c6184ca6d8ec90b6f0d94e70b3a79f06 100644 (file)
@@ -12,7 +12,7 @@ import re
 
 from sqlalchemy import sql, types as sqltypes, util
 from sqlalchemy.connectors.zxJDBC import ZxJDBCConnector
-from sqlalchemy.dialects.oracle.base import OracleCompiler, OracleDialect
+from sqlalchemy.dialects.oracle.base import OracleCompiler, OracleDialect, OracleExecutionContext
 from sqlalchemy.engine import base, default
 from sqlalchemy.sql import expression
 
@@ -71,7 +71,7 @@ class Oracle_zxjdbcCompiler(OracleCompiler):
         return 'RETURNING ' + ', '.join(columns) +  " INTO " + ", ".join(binds)
 
 
-class Oracle_zxjdbcExecutionContext(default.DefaultExecutionContext):
+class Oracle_zxjdbcExecutionContext(OracleExecutionContext):
 
     def pre_exec(self):
         if hasattr(self.compiled, 'returning_parameters'):
index 19d9224e29736557211694103c6f420b3396b2d0..0bc5f08b028457b78baa0fa8f055d43b8b60b877 100644 (file)
@@ -332,40 +332,6 @@ class PGDDLCompiler(compiler.DDLCompiler):
         return text
 
 
-class PGDefaultRunner(base.DefaultRunner):
-
-    def get_column_default(self, column, isinsert=True):
-        if column.primary_key:
-            if (isinstance(column.server_default, schema.DefaultClause) and
-                column.server_default.arg is not None):
-
-                # pre-execute passive defaults on primary key columns
-                return self.execute_string("select %s" % column.server_default.arg)
-
-            elif column is column.table._autoincrement_column \
-                    and (column.default is None or (isinstance(column.default, schema.Sequence) and column.default.optional)):
-
-                # execute the sequence associated with a SERIAL primary key column.  
-                # for non-primary-key SERIAL, the ID just generates server side.
-                sch = column.table.schema
-
-                if sch is not None:
-                    exc = "select nextval('\"%s\".\"%s_%s_seq\"')" % (sch, column.table.name, column.name)
-                else:
-                    exc = "select nextval('\"%s_%s_seq\"')" % (column.table.name, column.name)
-
-                return self.execute_string(exc)
-        
-        return super(PGDefaultRunner, self).get_column_default(column)
-
-    def visit_sequence(self, seq):
-        if not seq.optional:
-            return self.execute_string(("select nextval('%s')" % \
-                        self.dialect.identifier_preparer.format_sequence(seq)))
-        else:
-            return None
-
-
 class PGTypeCompiler(compiler.GenericTypeCompiler):
     def visit_INET(self, type_):
         return "INET"
@@ -438,6 +404,39 @@ class PGInspector(reflection.Inspector):
                                           info_cache=self.info_cache)
     
 
+
+class PGExecutionContext(default.DefaultExecutionContext):
+    def fire_sequence(self, seq):
+        if not seq.optional:
+            return self._execute_scalar(("select nextval('%s')" % \
+                        self.dialect.identifier_preparer.format_sequence(seq)))
+        else:
+            return None
+
+    def get_insert_default(self, column):
+        if column.primary_key:
+            if (isinstance(column.server_default, schema.DefaultClause) and
+                column.server_default.arg is not None):
+
+                # pre-execute passive defaults on primary key columns
+                return self._execute_scalar("select %s" % column.server_default.arg)
+
+            elif column is column.table._autoincrement_column \
+                    and (column.default is None or (isinstance(column.default, schema.Sequence) and column.default.optional)):
+
+                # execute the sequence associated with a SERIAL primary key column.  
+                # for non-primary-key SERIAL, the ID just generates server side.
+                sch = column.table.schema
+
+                if sch is not None:
+                    exc = "select nextval('\"%s\".\"%s_%s_seq\"')" % (sch, column.table.name, column.name)
+                else:
+                    exc = "select nextval('\"%s_%s_seq\"')" % (column.table.name, column.name)
+
+                return self._execute_scalar(exc)
+
+        return super(PGExecutionContext, self).get_insert_default(column)
+    
 class PGDialect(default.DefaultDialect):
     name = 'postgresql'
     supports_alter = True
@@ -459,7 +458,7 @@ class PGDialect(default.DefaultDialect):
     ddl_compiler = PGDDLCompiler
     type_compiler = PGTypeCompiler
     preparer = PGIdentifierPreparer
-    defaultrunner = PGDefaultRunner
+    execution_ctx_cls = PGExecutionContext
     inspector = PGInspector
     isolation_level = None
 
index 1c45b50f278cb9bbad0a7de50c00ccf5351048a7..17fe86be66bf0de056346e96d5929ddd853e2ed4 100644 (file)
@@ -23,7 +23,7 @@ from sqlalchemy.engine import default
 import decimal
 from sqlalchemy import util
 from sqlalchemy import types as sqltypes
-from sqlalchemy.dialects.postgresql.base import PGDialect, PGCompiler, PGIdentifierPreparer
+from sqlalchemy.dialects.postgresql.base import PGDialect, PGCompiler, PGIdentifierPreparer, PGExecutionContext
 
 class _PGNumeric(sqltypes.Numeric):
     def bind_processor(self, dialect):
@@ -41,7 +41,7 @@ class _PGNumeric(sqltypes.Numeric):
             return process
 
 
-class PostgreSQL_pg8000ExecutionContext(default.DefaultExecutionContext):
+class PostgreSQL_pg8000ExecutionContext(PGExecutionContext):
     pass
 
 
index a09697e79061952db281eef0bb72eb6e90d98b37..50e4bec3b06b744d8adcfb157731626631c78b26 100644 (file)
@@ -42,7 +42,7 @@ from sqlalchemy.engine import base, default
 from sqlalchemy.sql import expression
 from sqlalchemy.sql import operators as sql_operators
 from sqlalchemy import types as sqltypes
-from sqlalchemy.dialects.postgresql.base import PGDialect, PGCompiler, PGIdentifierPreparer
+from sqlalchemy.dialects.postgresql.base import PGDialect, PGCompiler, PGIdentifierPreparer, PGExecutionContext
 
 class _PGNumeric(sqltypes.Numeric):
     def bind_processor(self, dialect):
@@ -65,7 +65,7 @@ SERVER_SIDE_CURSOR_RE = re.compile(
     r'\s*SELECT',
     re.I | re.UNICODE)
 
-class PostgreSQL_psycopg2ExecutionContext(default.DefaultExecutionContext):
+class PostgreSQL_psycopg2ExecutionContext(PGExecutionContext):
     def create_cursor(self):
         # TODO: coverage for server side cursors + select.for_update()
         is_server_side = \
index 975006d927d2f4de076a3194ebfacf698fd8746c..517d41aaf80062e53d9f1eade87cc9dfd71b5108 100644 (file)
@@ -11,7 +11,7 @@ from sqlalchemy.engine import default
 import decimal
 from sqlalchemy import util
 from sqlalchemy import types as sqltypes
-from sqlalchemy.dialects.postgresql.base import PGDialect, PGDefaultRunner
+from sqlalchemy.dialects.postgresql.base import PGDialect, PGExecutionContext
 
 class PGNumeric(sqltypes.Numeric):
     def bind_processor(self, dialect):
@@ -28,13 +28,9 @@ class PGNumeric(sqltypes.Numeric):
                     return value
             return process
 
-class PostgreSQL_pypostgresqlExecutionContext(default.DefaultExecutionContext):
+class PostgreSQL_pypostgresqlExecutionContext(PGExecutionContext):
     pass
 
-class PostgreSQL_pypostgresqlDefaultRunner(PGDefaultRunner):
-    def execute_string(self, stmt, params=None):
-        return PGDefaultRunner.execute_string(self, stmt, params or ())
-        
 class PostgreSQL_pypostgresql(PGDialect):
     driver = 'pypostgresql'
 
@@ -43,8 +39,6 @@ class PostgreSQL_pypostgresql(PGDialect):
     supports_unicode_binds = True
     description_encoding = None
     
-    defaultrunner = PostgreSQL_pypostgresqlDefaultRunner
-    
     default_paramstyle = 'format'
     
     supports_sane_rowcount = False  # alas....posting a bug now
index 694a2f71fa53d5a15fc320abcd0ea413aa6da4fe..fed28c094b89514e97a6ad2f22a10b2e21447bc9 100644 (file)
@@ -60,7 +60,6 @@ from sqlalchemy.engine.base import (
     Compiled,
     Connectable,
     Connection,
-    DefaultRunner,
     Dialect,
     Engine,
     ExecutionContext,
@@ -83,7 +82,6 @@ __all__ = (
     'Compiled',
     'Connectable',
     'Connection',
-    'DefaultRunner',
     'Dialect',
     'Engine',
     'ExecutionContext',
index 26e44dd6b04c64463450c4621e6d4b75518bbcf7..829f975582c4435bdfb0c7c851faeefcc59a928d 100644 (file)
@@ -14,7 +14,7 @@ and result contexts.
 
 __all__ = [
     'BufferedColumnResultProxy', 'BufferedColumnRow', 'BufferedRowResultProxy',
-    'Compiled', 'Connectable', 'Connection', 'DefaultRunner', 'Dialect', 'Engine',
+    'Compiled', 'Connectable', 'Connection', 'Dialect', 'Engine',
     'ExecutionContext', 'NestedTransaction', 'ResultProxy', 'RootTransaction',
     'RowProxy', 'SchemaIterator', 'StringIO', 'Transaction', 'TwoPhaseTransaction',
     'connection_memoize']
@@ -57,10 +57,6 @@ class Dialect(object):
       type of encoding to use for unicode, usually defaults to
       'utf-8'.
 
-    defaultrunner
-      a :class:`~sqlalchemy.schema.SchemaVisitor` class which executes
-      defaults.
-
     statement_compiler
       a :class:`~Compiled` class used to compile SQL statements
 
@@ -1012,9 +1008,8 @@ class Connection(Connectable):
         return self._execute_clauseelement(func.select(), multiparams, params)
 
     def _execute_default(self, default, multiparams, params):
-        ret = self.engine.dialect.\
-                    defaultrunner(self.__create_execution_context()).\
-                    traverse_single(default)
+        ctx = self.__create_execution_context()
+        ret = ctx._exec_default(default)
         if self.__close_with_result:
             self.close()
         return ret
@@ -2154,68 +2149,6 @@ class BufferedColumnResultProxy(ResultProxy):
         return l
 
 
-class DefaultRunner(schema.SchemaVisitor):
-    """A visitor which accepts ColumnDefault objects, produces the
-    dialect-specific SQL corresponding to their execution, and
-    executes the SQL, returning the result value.
-
-    DefaultRunners are used internally by Engines and Dialects.
-    Specific database modules should provide their own subclasses of
-    DefaultRunner to allow database-specific behavior.
-    """
-
-    def __init__(self, context):
-        self.context = context
-        self.dialect = context.dialect
-        self.cursor = context.cursor
-
-    def get_column_default(self, column):
-        if column.default is not None:
-            return self.traverse_single(column.default)
-        else:
-            return None
-
-    def get_column_onupdate(self, column):
-        if column.onupdate is not None:
-            return self.traverse_single(column.onupdate)
-        else:
-            return None
-
-    def visit_passive_default(self, default):
-        return None
-
-    def visit_sequence(self, seq):
-        return None
-
-    def exec_default_sql(self, default):
-        conn = self.context.connection
-        c = expression.select([default.arg]).compile(bind=conn)
-        return conn._execute_compiled(c, (), {}).scalar()
-
-    def execute_string(self, stmt, params=None):
-        """execute a string statement, using the raw cursor, and return a scalar result."""
-
-        conn = self.context._connection
-        if isinstance(stmt, unicode) and not self.dialect.supports_unicode_statements:
-            stmt = stmt.encode(self.dialect.encoding)
-        conn._cursor_execute(self.cursor, stmt, params)
-        return self.cursor.fetchone()[0]
-
-    def visit_column_onupdate(self, onupdate):
-        if isinstance(onupdate.arg, expression.ClauseElement):
-            return self.exec_default_sql(onupdate)
-        elif util.callable(onupdate.arg):
-            return onupdate.arg(self.context)
-        else:
-            return onupdate.arg
-
-    def visit_column_default(self, default):
-        if isinstance(default.arg, expression.ClauseElement):
-            return self.exec_default_sql(default)
-        elif util.callable(default.arg):
-            return default.arg(self.context)
-        else:
-            return default.arg
 
 
 def connection_memoize(key):
index e062abce65ee9efa2d071265056d7871ef10d80b..12ab605e4637b0874b4579a8ee9d692ecafcb634 100644 (file)
@@ -28,7 +28,6 @@ class DefaultDialect(base.Dialect):
     ddl_compiler = compiler.DDLCompiler
     type_compiler = compiler.GenericTypeCompiler
     preparer = compiler.IdentifierPreparer
-    defaultrunner = base.DefaultRunner
     supports_alter = True
 
     supports_sequences = False
@@ -198,10 +197,7 @@ class DefaultExecutionContext(base.ExecutionContext):
             self.result_map = None
             self.cursor = self.create_cursor()
             self.compiled_parameters = []
-            if self.dialect.positional:
-                self.parameters = [()]
-            else:
-                self.parameters = [{}]
+            self.parameters = [self._default_params]
         elif compiled_sql is not None:
             self.compiled = compiled = compiled_sql
 
@@ -273,6 +269,27 @@ class DefaultExecutionContext(base.ExecutionContext):
             bool(self.compiled.returning) and \
             not self.compiled.statement._returning
     
+    @util.memoized_property
+    def _default_params(self):
+        if self.dialect.positional:
+            return ()
+        else:
+            return {}
+        
+    def _execute_scalar(self, stmt):
+        """Execute a string statement on the current cursor, returning a scalar result.
+        
+        Used to fire off sequences, default phrases, and "select lastrowid" types of statements individually
+        or in the context of a parent INSERT or UPDATE statement.
+        
+        """
+
+        conn = self._connection
+        if isinstance(stmt, unicode) and not self.dialect.supports_unicode_statements:
+            stmt = stmt.encode(self.dialect.encoding)
+        conn._cursor_execute(self.cursor, stmt, self._default_params)
+        return self.cursor.fetchone()[0]
+    
     @property
     def connection(self):
         return self._connection._branch()
@@ -286,10 +303,8 @@ class DefaultExecutionContext(base.ExecutionContext):
         if self.dialect.positional or self.dialect.supports_unicode_statements:
             if params:
                 return params
-            elif self.dialect.positional:
-                return [()]
             else:
-                return [{}]
+                return [self._default_params]
         else:
             def proc(d):
                 # sigh, sometimes we get positional arguments with a dialect
@@ -460,6 +475,32 @@ class DefaultExecutionContext(base.ExecutionContext):
                 self._connection._handle_dbapi_exception(e, None, None, None, self)
                 raise
 
+    def _exec_default(self, default):
+        if default.is_sequence:
+            return self.fire_sequence(default)
+        elif default.is_callable:
+            return default.arg(self)
+        elif default.is_clause_element:
+            # TODO: expensive branching here should be 
+            # pulled into _exec_scalar()
+            conn = self.connection  
+            c = expression.select([default.arg]).compile(bind=conn)
+            return conn._execute_compiled(c, (), {}).scalar()
+        else:
+            return default.arg
+        
+    def get_insert_default(self, column):
+        if column.default is None:
+            return None
+        else:
+            return self._exec_default(column.default)
+
+    def get_update_default(self, column):
+        if column.onupdate is None:
+            return None
+        else:
+            return self._exec_default(column.onupdate)
+
     def __process_defaults(self):
         """Generate default values for compiled insert/update statements,
         and generate inserted_primary_key collection.
@@ -467,28 +508,26 @@ class DefaultExecutionContext(base.ExecutionContext):
 
         if self.executemany:
             if len(self.compiled.prefetch):
-                drunner = self.dialect.defaultrunner(self)
                 params = self.compiled_parameters
                 for param in params:
                     self.current_parameters = param
                     for c in self.compiled.prefetch:
                         if self.isinsert:
-                            val = drunner.get_column_default(c)
+                            val = self.get_insert_default(c)
                         else:
-                            val = drunner.get_column_onupdate(c)
+                            val = self.get_update_default(c)
                         if val is not None:
                             param[c.key] = val
                 del self.current_parameters
 
         else:
             self.current_parameters = compiled_parameters = self.compiled_parameters[0]
-            drunner = self.dialect.defaultrunner(self)
 
             for c in self.compiled.prefetch:
                 if self.isinsert:
-                    val = drunner.get_column_default(c)
+                    val = self.get_insert_default(c)
                 else:
-                    val = drunner.get_column_onupdate(c)
+                    val = self.get_update_default(c)
 
                 if val is not None:
                     compiled_parameters[c.key] = val
index 6551594f32034524bf72367974dd7a3b7746a961..0a6ddfd400dfac34f2b8ad4f7e3d6f7656c89ada 100644 (file)
@@ -131,7 +131,7 @@ class EscapesDefaultsTest(testing.TestBase):
             
             # now execute, run the sequence.  it should run in u"Special_col.nextid" or similar as 
             # a unicode object; cx_oracle asserts that this is None or a String (postgresql lets it pass thru).
-            # ensure that base.DefaultRunner is encoding.
+            # ensure that executioncontext._exec_default() is encoding.
             t1.insert().execute(data='foo')
         finally:
             t1.drop()