]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
mssql+mxodbc should use executedirect for all selects and execute for insert/update...
authorMike Bayer <mike_mp@zzzcomputing.com>
Fri, 26 Mar 2010 20:47:53 +0000 (14:47 -0600)
committerMike Bayer <mike_mp@zzzcomputing.com>
Fri, 26 Mar 2010 20:47:53 +0000 (14:47 -0600)
lib/sqlalchemy/connectors/mxodbc.py
lib/sqlalchemy/engine/default.py
test/dialect/test_mssql.py
test/dialect/test_mxodbc.py [new file with mode: 0644]

index 4476ffd789d6da2c58a15d149d165d1588205945..f50bff7dac8b4088e2908090ac089423bedcc766 100644 (file)
@@ -97,10 +97,10 @@ class MxODBCConnector(Connector):
         """
         opts = url.translate_connect_args(username='user')
         opts.update(url.query)
-        args = opts['host'],
-        kwargs = {'user':opts['user'],
-                  'password': opts['password']}
-        return args, kwargs
+        args = opts.pop('host')
+        opts.pop('port', None)
+        opts.pop('database', None)
+        return (args,), opts
 
     def is_disconnect(self, e):
         # eGenix recommends checking connection.closed here,
@@ -126,10 +126,20 @@ class MxODBCConnector(Connector):
         return tuple(version)
 
     def do_execute(self, cursor, statement, parameters, context=None):
-        # temporary workaround until a more comprehensive solution can
-        # be found for controlling when to use executedirect
-        try:
-            cursor.execute(statement, parameters)
-        except (InterfaceError, ProgrammingError), e:
-            warnings.warn("cursor.execute failed; falling back to executedirect")
+        if context:
+            native_odbc_execute = context.execution_options.\
+                                        get('native_odbc_execute', 'auto')
+            if native_odbc_execute is True:
+                # user specified native_odbc_execute=True
+                cursor.execute(statement, parameters)
+            elif native_odbc_execute is False:
+                # user specified native_odbc_execute=False
+                cursor.executedirect(statement, parameters)
+            elif context.is_crud:
+                # statement is UPDATE, DELETE, INSERT
+                cursor.execute(statement, parameters)
+            else:
+                # all other statements
+                cursor.executedirect(statement, parameters)
+        else:
             cursor.executedirect(statement, parameters)
index 720edf66c87e2d0e25c2d946466844dbda5cb22a..6fb0a14a518d6d6b8f72afc6d7d086bf0247bf9d 100644 (file)
@@ -381,7 +381,10 @@ class DefaultExecutionContext(base.ExecutionContext):
                 self.execution_options = self.execution_options.union(connection._execution_options)
             self.cursor = self.create_cursor()
         
-            
+    @util.memoized_property
+    def is_crud(self):
+        return self.isinsert or self.isupdate or self.isdelete
+        
     @util.memoized_property
     def should_autocommit(self):
         autocommit = self.execution_options.get('autocommit', 
index 7a4e4dc4256c5f68d253c0f4ff41d88488521cac..21395bd3666ccc5de29b9e3c7d0cae3917024fc4 100644 (file)
@@ -49,8 +49,7 @@ class CompileTest(TestBase, AssertsCompiledSQL):
             )
         ]:
             self.assert_compile(expr, compile, dialect=mxodbc_dialect)
-        
-        
+    
     def test_in_with_subqueries(self):
         """Test that when using subqueries in a binary expression
         the == and != are changed to IN and NOT IN respectively.
diff --git a/test/dialect/test_mxodbc.py b/test/dialect/test_mxodbc.py
new file mode 100644 (file)
index 0000000..938d457
--- /dev/null
@@ -0,0 +1,69 @@
+from sqlalchemy import *
+from sqlalchemy.test.testing import eq_, TestBase
+from sqlalchemy.test import engines
+
+# TODO: we should probably build mock bases for
+# these to share with test_reconnect, test_parseconnect
+class MockDBAPI(object):
+    paramstyle = 'qmark'
+    def __init__(self):
+        self.log = []
+    def connect(self, *args, **kwargs):
+        return MockConnection(self)
+    
+class MockConnection(object):
+    def __init__(self, parent):
+        self.parent = parent
+    def cursor(self):
+        return MockCursor(self)
+    def close(self):
+        pass
+    def rollback(self):
+        pass
+    def commit(self):
+        pass
+
+class MockCursor(object):
+    description = None
+    rowcount = None
+    def __init__(self, parent):
+        self.parent = parent
+    def execute(self, *args, **kwargs):
+        self.parent.parent.log.append('execute')
+    def executedirect(self, *args, **kwargs):
+        self.parent.parent.log.append('executedirect')
+    def close(self):
+        pass
+
+
+class MxODBCTest(TestBase):
+    def test_native_odbc_execute(self):
+        t1 = Table('t1', MetaData(), Column('c1', Integer))
+
+        dbapi = MockDBAPI()
+        engine = engines.testing_engine(
+                                'mssql+mxodbc://localhost', 
+                                options={'module':dbapi, 
+                                        '_initialize':False}
+                            )
+        conn = engine.connect()
+        
+        # crud: uses execute
+        conn.execute(t1.insert().values(c1='foo'))
+        conn.execute(t1.delete().where(t1.c.c1=='foo'))
+        conn.execute(t1.update().where(t1.c.c1=='foo').values(c1='bar'))
+        
+        # select: uses executedirect
+        conn.execute(t1.select())
+        
+        # manual flagging
+        conn.execution_options(native_odbc_execute=True).execute(t1.select())
+        conn.execution_options(native_odbc_execute=False).execute(t1.insert().values(c1='foo'))
+        
+        eq_(
+            dbapi.log,
+            ['execute', 'execute', 'execute', 
+                'executedirect', 'execute', 'executedirect']
+        )
+        
+        
\ No newline at end of file