]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
mssql+zxjdbc support
authorPhilip Jenvey <pjenvey@underboss.org>
Fri, 11 Sep 2009 08:10:32 +0000 (08:10 +0000)
committerPhilip Jenvey <pjenvey@underboss.org>
Fri, 11 Sep 2009 08:10:32 +0000 (08:10 +0000)
original patch from Victor Ng
fixes #1505

README.unittests
lib/sqlalchemy/dialects/mssql/__init__.py
lib/sqlalchemy/dialects/mssql/zxjdbc.py [new file with mode: 0644]
lib/sqlalchemy/test/requires.py
test/orm/test_unitofwork.py
test/sql/test_returning.py

index 92a7521d0280ed21c0128ac192a478e852abb2f9..ac1b7a18e6825490160304ab45f6685c5f71d07b 100644 (file)
@@ -192,10 +192,15 @@ and an additional "owner" named "ed" is required:
 MSSQL: Tests that involve multiple connections require Snapshot Isolation
 ability implented on the test database in order to prevent deadlocks that will
 occur with record locking isolation. This feature is only available with
-MSSQL 2005 and greater. For example::
+MSSQL 2005 and greater. You must enable snapshot isolation at the database level
+and set the default cursor isolation with two SQL commands ::
 
     ALTER DATABASE MyDatabase
     SET ALLOW_SNAPSHOT_ISOLATION ON
     
     ALTER DATABASE MyDatabase
     SET READ_COMMITTED_SNAPSHOT ON
+
+MSSQL+zxJDBC: Trying to run the unit tests on Windows against SQL Server
+requires using a test.cfg configuration file as the cmd.exe shell won't properly
+pass the URL arguments into the nose test runner.
index e3a829047c43fe56900621fccb451947eec7b2e3..292320568d97c9d6fa7f238ac41402ed31e4da68 100644 (file)
@@ -1,3 +1,3 @@
-from sqlalchemy.dialects.mssql import base, pyodbc, adodbapi, pymssql
+from sqlalchemy.dialects.mssql import base, pyodbc, adodbapi, pymssql, zxjdbc
 
-base.dialect = pyodbc.dialect
\ No newline at end of file
+base.dialect = pyodbc.dialect
diff --git a/lib/sqlalchemy/dialects/mssql/zxjdbc.py b/lib/sqlalchemy/dialects/mssql/zxjdbc.py
new file mode 100644 (file)
index 0000000..28b9547
--- /dev/null
@@ -0,0 +1,64 @@
+"""Support for the Microsoft SQL Server database via the zxjdbc JDBC
+connector.
+
+JDBC Driver
+-----------
+
+Requires the jTDS driver, available from: http://jtds.sourceforge.net/
+
+Connecting
+----------
+
+URLs are of the standard form of
+``mssql+zxjdbc://user:pass@host:port/dbname[?key=value&key=value...]``.
+
+Additional arguments which may be specified either as query string
+arguments on the URL, or as keyword arguments to
+:func:`~sqlalchemy.create_engine()` will be passed as Connection
+properties to the underlying JDBC driver.
+
+"""
+from sqlalchemy.connectors.zxJDBC import ZxJDBCConnector
+from sqlalchemy.dialects.mssql.base import MSDialect, MSExecutionContext
+from sqlalchemy.engine import base
+
+class MS_zxjdbcExecutionContext(MSExecutionContext):
+
+    _embedded_scope_identity = False
+
+    def pre_exec(self):
+        super(MS_zxjdbcExecutionContext, self).pre_exec()
+        # scope_identity after the fact returns null in jTDS so we must
+        # embed it
+        if self._select_lastrowid and self.dialect.use_scope_identity:
+            self._embedded_scope_identity = True
+            self.statement += "; SELECT scope_identity()"
+
+    def post_exec(self):
+        if self._embedded_scope_identity:
+            while True:
+                try:
+                    row = self.cursor.fetchall()[0]
+                    break
+                except self.dialect.dbapi.Error, e:
+                    self.cursor.nextset()
+            self._lastrowid = int(row[0])
+
+        if (self.isinsert or self.isupdate or self.isdelete) and self.compiled.returning:
+            self._result_proxy = base.FullyBufferedResultProxy(self)
+
+        if self._enable_identity_insert:
+            table = self.dialect.identifier_preparer.format_table(self.compiled.statement.table)
+            self.cursor.execute("SET IDENTITY_INSERT %s OFF" % table)
+
+
+class MS_zxjdbc(ZxJDBCConnector, MSDialect):
+    jdbc_db_name = 'jtds:sqlserver'
+    jdbc_driver_name = 'net.sourceforge.jtds.jdbc.Driver'
+
+    execution_ctx_cls = MS_zxjdbcExecutionContext
+
+    def _get_server_version_info(self, connection):
+        return tuple(int(x) for x in connection.connection.dbversion.split('.'))
+
+dialect = MS_zxjdbc
index f3f4ec1911c9f0a7243408d282429dee322ba1cd..be6ae9594b329822bb78f59f69f7bd2ee8f7afea 100644 (file)
@@ -70,7 +70,9 @@ def independent_connections(fn):
     # ODBC as well.
     return _chain_decorators_on(
         fn,
-        no_support('sqlite', 'no driver support')
+        no_support('sqlite', 'no driver support'),
+        exclude('mssql', '<', (9, 0, 0),
+                'SQL Server 2005+ is required for independent connections'),
         )
 
 def row_triggers(fn):
index cdf6d39b943c33ee6837b402dddb91b16aae8bdd..b987291ca90936ceda4326428020c5949bd5fa3e 100644 (file)
@@ -243,7 +243,8 @@ class UnicodeSchemaTest(engine_base.AltEngineTest, _base.MappedTest):
     def teardown_class(cls):
         super(UnicodeSchemaTest, cls).teardown_class()
 
-    @testing.fails_on('mssql', 'pyodbc returns a non unicode encoding of the results description.')
+    @testing.fails_on('mssql+pyodbc',
+                      'pyodbc returns a non unicode encoding of the results description.')
     @testing.resolve_artifact_names
     def test_mapping(self):
         class A(_base.ComparableEntity):
@@ -280,7 +281,8 @@ class UnicodeSchemaTest(engine_base.AltEngineTest, _base.MappedTest):
         assert new_a1.t2s[0].d == b1.d
         session.expunge_all()
 
-    @testing.fails_on('mssql', 'pyodbc returns a non unicode encoding of the results description.')
+    @testing.fails_on('mssql+pyodbc',
+                      'pyodbc returns a non unicode encoding of the results description.')
     @testing.resolve_artifact_names
     def test_inheritance_mapping(self):
         class A(_base.ComparableEntity):
index d76c76173a71081191203773526c8043d2e3246c..8ba754c67fc73b97d8307bd5d120f048ff948d8b 100644 (file)
@@ -99,13 +99,16 @@ class ReturningTest(TestBase, AssertsExecutionResults):
             # return value is documented as failing with psycopg2/executemany
             result2 = table.insert().returning(table).execute(
                  [{'persons': 2, 'full': False}, {'persons': 3, 'full': True}])
-            
-            if testing.against('firebird', 'mssql'):
+
+            if testing.against('mssql+zxjdbc'):
+                # jtds apparently returns only the first row
+                eq_(result2.fetchall(), [(2, 2, False, None)])
+            elif testing.against('firebird', 'mssql'):
                 # Multiple inserts only return the last row
-                eq_(result2.fetchall(), [(3,3,True, None)])
+                eq_(result2.fetchall(), [(3, 3, True, None)])
             else:
                 # nobody does this as far as we know (pg8000?)
-                eq_(result2.fetchall(), [(2, 2, False, None), (3,3,True, None)])
+                eq_(result2.fetchall(), [(2, 2, False, None), (3, 3, True, None)])
 
         test_executemany()