Michael Manfre avatar Michael Manfre committed 2451845 Merge

Merged in refactored branch

Comments (0)

Files changed (57)

+.*\.pyc 
+build/ 
+dist/ 
+django_mssql.egg-info/ 

File contents unchanged.

django-patches/README.txt

-This folder contains any patches that should be applied against Django.
-
-* As of Django 1.0-beta, there are no patches needed by this back-end.

extras/django-patches/README.txt

+This folder contains any patches that should be applied against Django.
+
+* As of Django 1.0-beta, there are no patches needed by this back-end.

extras/samples/nodjango.py

+"""An example of using the internal DB-API module without any Django."""
+
+# Adds the relative path for the MS SQL Server backend to Python's import path.
+# We do this so we can run this module from a checkout for demo purposes
+# without having to install it.
+def _hack_backend_path():
+	import os, sys
+	backend_path = os.path.join(os.path.abspath(os.path.dirname(".")), "../source")
+	sys.path.append(backend_path)
+
+# Import the dbapi module, after hacking the import path.
+_hack_backend_path()
+import sqlserver_ado.dbapi as db
+
+def _print_names(results):
+    for item in results:
+        print item[1]
+
+def sproc_1(connection):
+    "Calls a sproc using execute with explicit parameter markers."
+    c = connection.cursor()
+    c.execute('uspAppUser_GetAll %s', ['current_user'])
+    _print_names(c.fetchall())
+    c.close()
+
+def sproc_1b(connection):
+    "Calls a sproc using execute with explicit parameter markers."
+    c = connection.cursor()
+    c.execute('uspAppUser_GetAll %s', [None])
+    _print_names(c.fetchall())
+    c.close()
+
+def sproc_2(connection):
+    "Calls a sproc using 'callproc'."
+    c = connection.cursor()
+    c.callproc('uspAppUser_GetAll', ['current_user'])
+    _print_names(c.fetchall())
+    c.close()
+
+def sproc_2b(connection):
+    "Calls a sproc using 'callproc'."
+    c = connection.cursor()
+    c.callproc('uspAppUser_GetAll', [0])
+    _print_names(c.fetchall())
+    c.close()
+
+
+def main():
+    connection = db.connect("PROVIDER=SQLOLEDB;DATA SOURCE=localhost\\ss2005;Initial Catalog=Ted;Integrated Security=SSPI")
+    sproc_2b(connection)
+    connection.close()
+
+main()

extras/sqlserver/README.txt

+These folders contain add-ins for your SQL Server database to support 
+additional Django features.
+
+== Regular Expression Support ==
+
+There are two options for using regular expressions in SQL Server 2005:
+
+* regex_clr contains a .NET (2.0) assembly that adds regex user functions
+* regex_vbscript contains regex user functions implemented in VBScript

extras/sqlserver/regex_clr/DEPLOY.txt

+To deploy this assembly to your Database, you have a couple of options.
+
+A) You can put in your connection string information in the regex_clr 
+	project's properties. Pressing "play" (ie, running) the solution will
+	then deploy the assembly to the designated server.
+	
+B) You can issue "CREATE ASSEMBLY" statements in a SQL console
+	or build script, as described here:
+	http://technet.microsoft.com/en-us/library/ms345099.aspx
+	
+In both cases you may need to enable CLR support in your database
+as described here:
+	http://technet.microsoft.com/en-us/library/ms131048.aspx
+
+To enable CLR:
+	sp_configure 'show advanced options', 1;
+	GO
+	RECONFIGURE;
+	GO
+	sp_configure 'clr enabled', 1;
+	GO
+	RECONFIGURE;
+	GO
+
+To pull in the UDF from the assembly:
+create function REGEXP_LIKE(
+	@input nvarchar(4000),
+	@pattern nvarchar(4000),
+	@caseSensitive int)
+RETURNS INT 
+AS EXTERNAL NAME regex_clr.UserDefinedFunctions.REGEXP_LIKE

extras/sqlserver/regex_clr/Properties/AssemblyInfo.cs

+using System;
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("regex_clr")]
+[assembly: AssemblyDescription("Provides support for RegEx expressions, suitable for use with Django")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("regex_clr")]
+[assembly: AssemblyCopyright("Copyright © 2007 Adam Vandenberg")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+[assembly: ComVisible(false)]
+
+//
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Revision and Build Numbers
+// by using the '*' as shown below:
+[assembly: AssemblyVersion("1.0.*")]

extras/sqlserver/regex_clr/REGEXP_LIKE.cs

+using System;
+using System.Data.SqlTypes;
+using System.Text.RegularExpressions;
+using Microsoft.SqlServer.Server;
+
+public partial class UserDefinedFunctions
+{
+	private const RegexOptions DefaultRegExOptions =
+		RegexOptions.IgnorePatternWhitespace | RegexOptions.Singleline;
+
+	[SqlFunction(IsDeterministic = true, IsPrecise = true)]
+	public static SqlInt32 REGEXP_LIKE(SqlString input, SqlString pattern, SqlInt32 caseSensitive)
+	{
+		RegexOptions options = DefaultRegExOptions;
+		if (caseSensitive==0)
+			options |= RegexOptions.IgnoreCase;
+
+		return Regex.IsMatch(input.Value, pattern.Value, options) ? 1 : 0;
+	}
+}
Add a comment to this file

extras/sqlserver/regex_clr/bin/Debug/regex_clr.dll

Binary file added.

extras/sqlserver/regex_clr/deploy.sql

+-- Use the proper database
+use [django_test_backend]
+GO
+
+-- Enable CLR in this database
+sp_configure 'show advanced options', 1;
+GO
+RECONFIGURE;
+GO
+sp_configure 'clr enabled', 1;
+GO
+RECONFIGURE;
+GO
+
+-- Create the assembly in the database from the path can this be done via a file path?
+CREATE ASSEMBLY regex_clr from 'C:\Projects\django-mssql\sqlserver\regex_clr\bin\Debug\regex_clr.dll' WITH PERMISSION_SET = SAFE
+GO
+
+-- Pull in the User Defined Function from the assembly
+create function REGEXP_LIKE
+(
+	@input nvarchar(4000),
+	@pattern nvarchar(4000),
+	@caseSensitive int
+) 
+RETURNS INT  AS 
+EXTERNAL NAME regex_clr.UserDefinedFunctions.REGEXP_LIKE
+GO

extras/sqlserver/regex_clr/regex_clr.csproj

+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectTypeGuids>{c252feb5-a946-4202-b1d4-9916a0590387};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <ProductVersion>8.0.50727</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{50E805DA-E117-4073-B1ED-A71CAAB3FCD8}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <NoStandardLibraries>false</NoStandardLibraries>
+    <AssemblyName>regex_clr</AssemblyName>
+    <RootNamespace>regex_clr</RootNamespace>
+    <RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <EnableUnmanagedDebugging>false</EnableUnmanagedDebugging>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugSymbols>false</DebugSymbols>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <EnableUnmanagedDebugging>false</EnableUnmanagedDebugging>
+    <DefineConstants>TRACE</DefineConstants>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+  <Import Project="$(MSBuildBinPath)\SqlServer.targets" />
+  <ItemGroup>
+    <Reference Include="System" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.XML" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="REGEXP_LIKE.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <Content Include="sql_scripts\Test.sql">
+      <SubType>Content</SubType>
+    </Content>
+  </ItemGroup>
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>

extras/sqlserver/regex_clr/regex_clr.sln

+
+Microsoft Visual Studio Solution File, Format Version 9.00
+# Visual Studio 2005
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "regex_clr", "regex_clr.csproj", "{50E805DA-E117-4073-B1ED-A71CAAB3FCD8}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{CC0625B1-B3DD-431C-8BED-806A3170A924}"
+	ProjectSection(SolutionItems) = preProject
+		DEPLOY.txt = DEPLOY.txt
+	EndProjectSection
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{50E805DA-E117-4073-B1ED-A71CAAB3FCD8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{50E805DA-E117-4073-B1ED-A71CAAB3FCD8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{50E805DA-E117-4073-B1ED-A71CAAB3FCD8}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+		{50E805DA-E117-4073-B1ED-A71CAAB3FCD8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{50E805DA-E117-4073-B1ED-A71CAAB3FCD8}.Release|Any CPU.Build.0 = Release|Any CPU
+		{50E805DA-E117-4073-B1ED-A71CAAB3FCD8}.Release|Any CPU.Deploy.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal

extras/sqlserver/regex_clr/sql_scripts/Test.sql

+select 'Running RegEx examples and tests.'
+
+select 'Simple tests.'
+select 
+	dbo.REGEXP_LIKE('abc', 'abc', 1) as [Match case sensitive]
+	, dbo.REGEXP_LIKE('abc', 'abc', 0) as [Match case insensitive]
+	, dbo.REGEXP_LIKE('abc', 'ABC', 1) as [Match case sensitive]
+	, dbo.REGEXP_LIKE('abc', 'ABC', 0) as [Doesn't match case insensitive]

extras/sqlserver/regex_com/enable.sql

+/*
+This script adds Regex support to SQL Server via the VBScript.RegExp object.
+To use this object, OLE Automation needs to be enabled on the server, as 
+described here:
+    http://msdn2.microsoft.com/en-us/library/ms191188.aspx
+
+with these SQL statements:
+*/
+
+sp_configure 'show advanced options', 1;
+GO
+RECONFIGURE;
+GO
+sp_configure 'Ole Automation Procedures', 1;
+GO
+RECONFIGURE;
+GO

extras/sqlserver/regex_com/regexp_like.sql

+CREATE FUNCTION dbo.REGEXP_LIKE
+(
+	@input nvarchar(4000),
+	@pattern nvarchar(4000),
+	@caseSensitive int
+)
+RETURNS bit
+AS
+BEGIN
+	DECLARE @hresult integer
+	DECLARE @oRegexp integer
+	DECLARE @objMatches integer
+	DECLARE @objMatch integer
+	DECLARE @count integer
+	DECLARE @results bit
+	
+	EXEC @hresult = sp_OACreate 'VBScript.RegExp', @oRegexp OUTPUT
+	IF @hresult <> 0 return -1;
+
+	EXEC @hresult = sp_OASetProperty @oRegexp, 'Pattern', @pattern
+	IF @hresult <> 0 return -1;
+
+	EXEC @hresult = sp_OASetProperty @oRegexp, 'Global', false
+	IF @hresult <> 0 return -1;
+
+	IF @caseSensitive = 0 EXEC @hresult = sp_OASetProperty @oRegexp, 'IgnoreCase', 1
+	IF @hresult <> 0 return -1;
+
+	EXEC @hresult = sp_OAMethod @oRegexp, 'Test', @results OUTPUT, @input
+	IF @hresult <> 0 return -1;
+
+	EXEC @hresult = sp_OADestroy @oRegexp
+	IF @hresult <> 0 return -1;
+
+	RETURN @results
+END

samples/nodjango.py

-"""An example of using the internal DB-API module without any Django."""
-
-# Adds the relative path for the MS SQL Server backend to Python's import path.
-# We do this so we can run this module from a checkout for demo purposes
-# without having to install it.
-def _hack_backend_path():
-	import os, sys
-	backend_path = os.path.join(os.path.abspath(os.path.dirname(".")), "../source")
-	sys.path.append(backend_path)
-
-# Import the dbapi module, after hacking the import path.
-_hack_backend_path()
-import sqlserver_ado.dbapi as db
-
-def _print_names(results):
-    for item in results:
-        print item[1]
-
-def sproc_1(connection):
-    "Calls a sproc using execute with explicit parameter markers."
-    c = connection.cursor()
-    c.execute('uspAppUser_GetAll %s', ['current_user'])
-    _print_names(c.fetchall())
-    c.close()
-
-def sproc_1b(connection):
-    "Calls a sproc using execute with explicit parameter markers."
-    c = connection.cursor()
-    c.execute('uspAppUser_GetAll %s', [None])
-    _print_names(c.fetchall())
-    c.close()
-
-def sproc_2(connection):
-    "Calls a sproc using 'callproc'."
-    c = connection.cursor()
-    c.callproc('uspAppUser_GetAll', ['current_user'])
-    _print_names(c.fetchall())
-    c.close()
-
-def sproc_2b(connection):
-    "Calls a sproc using 'callproc'."
-    c = connection.cursor()
-    c.callproc('uspAppUser_GetAll', [0])
-    _print_names(c.fetchall())
-    c.close()
-
-
-def main():
-    connection = db.connect("PROVIDER=SQLOLEDB;DATA SOURCE=localhost\\ss2005;Initial Catalog=Ted;Integrated Security=SSPI")
-    sproc_2b(connection)
-    connection.close()
-
-main()
+from setuptools import setup, find_packages
+
+version = __import__('sqlserver_ado').get_version()
+
+setup(
+    name='django-mssql',
+    version=version.replace(' ', '-'),
+    maintainer='Michael Manfre',
+    maintainer_email='mmanfre@gmail.com',
+    url='http://django-mssql.googlecode.com/',
+    description="Django backend database support for MS SQL 2005 and up.",
+    license='Apache License, Version 2.0',
+    packages=find_packages(),
+    include_package_data=True,
+    classifiers=[
+        "Programming Language :: Python",
+        "Topic :: Software Development :: Libraries :: Python Modules",
+        "Framework :: Django",
+        "Environment :: Web Environment",
+    ],
+)
Add a comment to this file

source/sql_app/__init__.py

Empty file removed.

Add a comment to this file

source/sql_app/management/__init__.py

Empty file removed.

Add a comment to this file

source/sql_app/management/commands/__init__.py

Empty file removed.

source/sql_app/management/commands/dbgui.py

-from django.core.management.base import NoArgsCommand
-
-class Command(NoArgsCommand):
-    help = "Launches SQL Server Management Studio (on Windows)."
-
-    requires_model_validation = False
-
-    def handle_noargs(self, **options):
-        from django.conf import settings
-        import os
-
-        args = ['-nosplash', '-E']
-        
-        host = settings.DATABASE_OPTIONS.get('host', settings.DATABASE_HOST)
-        db = settings.DATABASE_OPTIONS.get('db', settings.DATABASE_NAME)
-        # user = settings.DATABASE_OPTIONS.get('user', settings.DATABASE_USER)
-        # passwd = settings.DATABASE_OPTIONS.get('passwd', settings.DATABASE_PASSWORD)
-        # port = settings.DATABASE_OPTIONS.get('port', settings.DATABASE_PORT)
-    
-        if host:
-            args += ['-S', host]
-
-        if db:
-            args += ["-d", db]
-
-        os.execvp('sqlwb.exe', args)
Add a comment to this file

source/sqlserver_ado/__init__.py

Empty file removed.

source/sqlserver_ado/ado_consts.py

-# ADO enumerated constants documented on MSDN:
-# http://msdn.microsoft.com/en-us/library/ms678353(VS.85).aspx
-
-# IsolationLevelEnum
-adXactUnspecified     = -1
-adXactBrowse          = 0x100
-adXactChaos           = 0x10
-adXactCursorStability = 0x1000
-adXactIsolated        = 0x100000
-adXactReadCommitted   = 0x1000
-adXactReadUncommitted = 0x100
-adXactRepeatableRead  = 0x10000
-adXactSerializable    = 0x100000
-
-# CursorLocationEnum
-adUseClient = 3
-adUseServer = 2
-
-# CursorTypeEnum
-adOpenDynamic       = 2
-adOpenForwardOnly   = 0
-adOpenKeyset        = 1
-adOpenStatic        = 3
-adOpenUnspecified   = -1
-
-# CommandTypeEnum
-adCmdText = 1
-adCmdStoredProc = 4
-
-# ParameterDirectionEnum
-adParamInput       = 1
-adParamInputOutput = 3
-adParamOutput      = 2
-adParamReturnValue = 4
-adParamUnknown     = 0
-
-# ObjectStateEnum
-adStateClosed     = 0
-adStateOpen       = 1
-adStateConnecting = 2
-adStateExecuting  = 4
-adStateFetching   = 8
-
-# FieldAttributeEnum
-adFldMayBeNull = 0x40
-
-# ConnectModeEnum
-adModeUnknown           = 0
-adModeRead              = 1
-adModeWrite             = 2
-adModeReadWrite         = 3
-adModeShareDenyRead     = 4
-adModeShareDenyWrite    = 8
-adModeShareExclusive    = 12
-adModeShareDenyNone     = 16
-adModeRecursive         = 0x400000
-
-# XactAttributeEnum
-adXactCommitRetaining = 131072
-adXactAbortRetaining = 262144
-
-ado_error_TIMEOUT = -2147217871
-
-# DataTypeEnum - ADO Data types documented at:
-# http://msdn2.microsoft.com/en-us/library/ms675318.aspx
-adArray                       = 0x2000
-adEmpty                       = 0x0
-adBSTR                        = 0x8
-adBigInt                      = 0x14
-adBinary                      = 0x80
-adBoolean                     = 0xb
-adChapter                     = 0x88
-adChar                        = 0x81
-adCurrency                    = 0x6
-adDBDate                      = 0x85
-adDBTime                      = 0x86
-adDBTimeStamp                 = 0x87
-adDate                        = 0x7
-adDecimal                     = 0xe
-adDouble                      = 0x5
-adError                       = 0xa
-adFileTime                    = 0x40
-adGUID                        = 0x48
-adIDispatch                   = 0x9
-adIUnknown                    = 0xd
-adInteger                     = 0x3
-adLongVarBinary               = 0xcd
-adLongVarChar                 = 0xc9
-adLongVarWChar                = 0xcb
-adNumeric                     = 0x83
-adPropVariant                 = 0x8a
-adSingle                      = 0x4
-adSmallInt                    = 0x2
-adTinyInt                     = 0x10
-adUnsignedBigInt              = 0x15
-adUnsignedInt                 = 0x13
-adUnsignedSmallInt            = 0x12
-adUnsignedTinyInt             = 0x11
-adUserDefined                 = 0x84
-adVarBinary                   = 0xCC
-adVarChar                     = 0xC8
-adVarNumeric                  = 0x8B
-adVarWChar                    = 0xCA
-adVariant                     = 0xC
-adWChar                       = 0x82
-# Additional constants used by introspection but not ADO itself
-AUTO_FIELD_MARKER = -1000
-
-adTypeNames = {
-    adBSTR: 'adBSTR',
-    adBigInt: 'adBigInt',
-    adBinary: 'adBinary',
-    adBoolean: 'adBoolean',
-    adChapter: 'adChapter',
-    adChar: 'adChar',
-    adCurrency: 'adCurrency',
-    adDBDate: 'adDBDate',
-    adDBTime: 'adDBTime',
-    adDBTimeStamp: 'adDBTimeStamp',
-    adDate: 'adDate',
-    adDecimal: 'adDecimal',
-    adDouble: 'adDouble',
-    adEmpty: 'adEmpty',
-    adError: 'adError',
-    adFileTime: 'adFileTime',
-    adGUID: 'adGUID',
-    adIDispatch: 'adIDispatch',
-    adIUnknown: 'adIUnknown',
-    adInteger: 'adInteger',
-    adLongVarBinary: 'adLongVarBinary',
-    adLongVarChar: 'adLongVarChar',
-    adLongVarWChar: 'adLongVarWChar',
-    adNumeric: 'adNumeric',
-    adPropVariant: 'adPropVariant',
-    adSingle: 'adSingle',
-    adSmallInt: 'adSmallInt',
-    adTinyInt: 'adTinyInt',
-    adUnsignedBigInt: 'adUnsignedBigInt',
-    adUnsignedInt: 'adUnsignedInt',
-    adUnsignedSmallInt: 'adUnsignedSmallInt',
-    adUnsignedTinyInt: 'adUnsignedTinyInt',
-    adUserDefined: 'adUserDefined',
-    adVarBinary: 'adVarBinary',
-    adVarChar: 'adVarChar',
-    adVarNumeric: 'adVarNumeric',
-    adVarWChar: 'adVarWChar',
-    adVariant: 'adVariant',
-    adWChar: 'adWChar',
-   }
-
-def ado_type_name(ado_type):
-    return adTypeNames.get(ado_type, 'unknown type ('+str(ado_type)+')')
-
-# Error codes to names
-adoErrors= {
-    0xe7b      :'adErrBoundToCommand',
-    0xe94      :'adErrCannotComplete',
-    0xea4      :'adErrCantChangeConnection',
-    0xc94      :'adErrCantChangeProvider',
-    0xe8c      :'adErrCantConvertvalue',
-    0xe8d      :'adErrCantCreate',
-    0xea3      :'adErrCatalogNotSet',
-    0xe8e      :'adErrColumnNotOnThisRow',
-    0xd5d      :'adErrDataConversion',
-    0xe89      :'adErrDataOverflow',
-    0xe9a      :'adErrDelResOutOfScope',
-    0xea6      :'adErrDenyNotSupported',
-    0xea7      :'adErrDenyTypeNotSupported',
-    0xcb3      :'adErrFeatureNotAvailable',
-    0xea5      :'adErrFieldsUpdateFailed',
-    0xc93      :'adErrIllegalOperation',
-    0xcae      :'adErrInTransaction',
-    0xe87      :'adErrIntegrityViolation',
-    0xbb9      :'adErrInvalidArgument',
-    0xe7d      :'adErrInvalidConnection',
-    0xe7c      :'adErrInvalidParamInfo',
-    0xe82      :'adErrInvalidTransaction',
-    0xe91      :'adErrInvalidURL',
-    0xcc1      :'adErrItemNotFound',
-    0xbcd      :'adErrNoCurrentRecord',
-    0xe83      :'adErrNotExecuting',
-    0xe7e      :'adErrNotReentrant',
-    0xe78      :'adErrObjectClosed',
-    0xd27      :'adErrObjectInCollection',
-    0xd5c      :'adErrObjectNotSet',
-    0xe79      :'adErrObjectOpen',
-    0xbba      :'adErrOpeningFile',
-    0xe80      :'adErrOperationCancelled',
-    0xe96      :'adErrOutOfSpace',
-    0xe88      :'adErrPermissionDenied',
-    0xe9e      :'adErrPropConflicting',
-    0xe9b      :'adErrPropInvalidColumn',
-    0xe9c      :'adErrPropInvalidOption',
-    0xe9d      :'adErrPropInvalidValue',
-    0xe9f      :'adErrPropNotAllSettable',
-    0xea0      :'adErrPropNotSet',
-    0xea1      :'adErrPropNotSettable',
-    0xea2      :'adErrPropNotSupported',
-    0xbb8      :'adErrProviderFailed',
-    0xe7a      :'adErrProviderNotFound',
-    0xbbb      :'adErrReadFile',
-    0xe93      :'adErrResourceExists',
-    0xe92      :'adErrResourceLocked',
-    0xe97      :'adErrResourceOutOfScope',
-    0xe8a      :'adErrSchemaViolation',
-    0xe8b      :'adErrSignMismatch',
-    0xe81      :'adErrStillConnecting',
-    0xe7f      :'adErrStillExecuting',
-    0xe90      :'adErrTreePermissionDenied',
-    0xe8f      :'adErrURLDoesNotExist',
-    0xe99      :'adErrURLNamedRowDoesNotExist',
-    0xe98      :'adErrUnavailable',
-    0xe84      :'adErrUnsafeOperation',
-    0xe95      :'adErrVolumeNotFound',
-    0xbbc      :'adErrWriteFile'
-    }

source/sqlserver_ado/base.py

-"""Microsoft SQL Server database backend for Django."""
-from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, BaseDatabaseValidation, BaseDatabaseClient
-from django.db.backends.signals import connection_created
-from django.core.exceptions import ImproperlyConfigured
-
-import dbapi as Database
-
-from introspection import DatabaseIntrospection
-from creation import DatabaseCreation
-from operations import DatabaseOperations
-
-DatabaseError = Database.DatabaseError
-IntegrityError = Database.IntegrityError
-
-
-class DatabaseFeatures(BaseDatabaseFeatures):
-    uses_custom_query_class = True
-
-# IP Address recognizer taken from:
-# http://mail.python.org/pipermail/python-list/2006-March/375505.html
-def _looks_like_ipaddress(address):
-    dots = address.split(".")
-    if len(dots) != 4:
-        return False
-    for item in dots:
-        if not 0 <= int(item) <= 255:
-            return False
-    return True
-
-def connection_string_from_settings():
-    from django.conf import settings
-    return make_connection_string(settings)
-
-def make_connection_string(settings):
-    class wrap(object):
-        def __init__(self, mapping):
-            self._dict = mapping
-            
-        def __getattr__(self, name):
-            d = self._dict
-            result = None
-            if hasattr(d, "get"):
-                if d.has_key(name):
-                    result = d.get(name)
-                else:
-                    result = d.get('DATABASE_' + name)    
-            elif hasattr(d, 'DATABASE_' + name):
-                result = getattr(d, 'DATABASE_' + name)
-            else:
-                result = getattr(d, name)
-            return result    
-            
-    settings = wrap(settings) 
-    
-    db_name = settings.NAME.strip()
-    db_host = settings.HOST or '127.0.0.1'
-    if len(db_name) == 0:
-        raise ImproperlyConfigured("You need to specify a DATABASE NAME in your Django settings file.")
-
-    # Connection strings courtesy of:
-    # http://www.connectionstrings.com/?carrier=sqlserver
-
-    # If a port is given, force a TCP/IP connection. The host should be an IP address in this case.
-    if settings.PORT != '':
-        if not _looks_like_ipaddress(db_host):
-            raise ImproperlyConfigured("When using DATABASE PORT, DATABASE HOST must be an IP address.")
-        try:
-            port = int(settings.PORT)
-        except ValueError:
-            raise ImproperlyConfigured("DATABASE PORT must be a number.")
-        datasource = '%s,%i;Network Library=DBMSSOCN' % (db_host, port)
-
-    # If no user is specified, use integrated security.
-    if settings.USER != '':
-        auth_string = "UID=%s;PWD=%s" % (settings.USER, settings.PASSWORD)
-    else:
-        auth_string = "Integrated Security=SSPI"
-
-    parts = [
-        "PROVIDER=SQLOLEDB", 
-        "DATA SOURCE=%s" % (db_host,),
-        "Initial Catalog=%s" % (db_name,),
-        auth_string
-    ]
-    
-    options = settings.OPTIONS
-    if options:
-        if 'use_mars' in options and options['use_mars']:
-            parts.append("MultipleActiveResultSets=true")
-            
-        if 'extra_params' in options:
-            parts.append(options['extra_params'])
-        
-        if 'provider' in options:
-            parts[0] = 'PROVIDER=%s' % (options['provider'],)
-    
-    return ";".join(parts)
-
-class DatabaseWrapper(BaseDatabaseWrapper):
-    operators = {
-        "exact": "= %s",
-        "iexact": "LIKE %s ESCAPE '\\'",
-        "contains": "LIKE %s ESCAPE '\\'",
-        "icontains": "LIKE %s ESCAPE '\\'",
-        "gt": "> %s",
-        "gte": ">= %s",
-        "lt": "< %s",
-        "lte": "<= %s",
-        "startswith": "LIKE %s ESCAPE '\\'",
-        "endswith": "LIKE %s ESCAPE '\\'",
-        "istartswith": "LIKE %s ESCAPE '\\'",
-        "iendswith": "LIKE %s ESCAPE '\\'",
-    }
-
-    def __init__(self, *args, **kwargs):
-        super(DatabaseWrapper, self).__init__(*args, **kwargs)
-        
-        self.features = DatabaseFeatures()
-        self.ops = DatabaseOperations()
-        
-        self.client = BaseDatabaseClient(self)
-        self.creation = DatabaseCreation(self) 
-        self.introspection = DatabaseIntrospection(self)
-        self.validation = BaseDatabaseValidation(self)
-
-        try:
-            self.command_timeout = int(self.settings_dict.get('COMMAND_TIMEOUT', 30))
-        except ValueError:   
-            self.command_timeout = 30
-        
-    def _cursor(self):
-        if self.connection is None:
-            self.connection = Database.connect(
-                                make_connection_string(self.settings_dict),
-                                self.command_timeout
-                              )
-            connection_created.send(sender=self.__class__)
-
-        return Database.Cursor(self.connection)

source/sqlserver_ado/compiler.py

-from django.db.models.sql import compiler
-import re
-
-# query_class returns the base class to use for Django queries.
-# The custom 'SqlServerQuery' class derives from django.db.models.sql.query.Query
-# which is passed in as "QueryClass" by Django itself.
-#
-# SqlServerQuery overrides:
-# ...insert queries to add "SET IDENTITY_INSERT" if needed.
-# ...select queries to emulate LIMIT/OFFSET for sliced queries.
-
-_re_order_limit_offset = re.compile(
-    r'(?:ORDER BY\s+(.+?))?\s*(?:LIMIT\s+(\d+))?\s*(?:OFFSET\s+(\d+))?$')
-
-# Pattern to find the quoted column name at the end of a field specification
-_re_pat_col = re.compile(r"\[([^[]+)\]$")
-
-# Pattern to find each of the parts of a column name (extra_select, table, field)
-_re_pat_col_parts = re.compile(
-    r'(?:' +
-    r'(\([^\)]+\))\s+as\s+' +
-    r'|(\[[^[]+\])\.' +
-    r')?' +
-    r'\[([^[]+)\]$',
-    re.IGNORECASE
-)
-
-# Pattern used in column aliasing to find sub-select placeholders
-_re_col_placeholder = re.compile(r'\{_placeholder_(\d+)\}')
-
-def _break(s, find):
-    """Break a string s into the part before the substring to find, 
-    and the part including and after the substring."""
-    i = s.find(find)
-    return s[:i], s[i:]
-
-def _get_order_limit_offset(sql):
-    return _re_order_limit_offset.search(sql).groups()
-    
-def _remove_order_limit_offset(sql):
-    return _re_order_limit_offset.sub('',sql).split(None, 1)[1]
-
-
-class SQLCompiler(compiler.SQLCompiler):
-    def resolve_columns(self, row, fields=()):
-        # If the results are sliced, the resultset will have an initial 
-        # "row number" column. Remove this column before the ORM sees it.
-        if self._using_row_number:
-            return row[1:]
-        return row
-
-    def as_sql(self, with_limits=True, with_col_aliases=False):
-        self._using_row_number = False
-        
-        # Get out of the way if we're not a select query or there's no limiting involved.
-        check_limits = with_limits and (self.query.low_mark or self.query.high_mark is not None)
-        if not check_limits:
-            return super(SQLCompiler, self).as_sql(with_limits, with_col_aliases)
-
-        raw_sql, fields = super(SQLCompiler, self).as_sql(False, with_col_aliases)
-        
-        # Check for high mark only and replace with "TOP"
-        if self.query.high_mark is not None and not self.query.low_mark:
-            _select = 'SELECT'
-            if self.query.distinct:
-                _select += ' DISTINCT'
-            
-            sql = re.sub(r'(?i)^%s' % _select, '%s TOP %s' % (_select, self.query.high_mark), raw_sql, 1)
-            return sql, fields
-            
-        # Else we have limits; rewrite the query using ROW_NUMBER()
-        self._using_row_number = True
-
-        order, limit_ignore, offset_ignore = _get_order_limit_offset(raw_sql)
-        
-        qn = self.connection.ops.quote_name
-        
-        inner_table_name = qn('AAAA')
-
-        # Using ROW_NUMBER requires an ordering
-        if order is None:
-            meta = self.query.get_meta()                
-            column = meta.pk.db_column or meta.pk.get_attname()
-            order = '%s.%s ASC' % (inner_table_name, qn(column))
-        else:
-            # remap order for injected subselect
-            new_order = []
-            for x in order.split(','):
-                if x.find('.') != -1:
-                    tbl, col = x.rsplit('.', 1)
-                else:
-                    col = x
-                new_order.append('%s.%s' % (inner_table_name, col))
-            order = ', '.join(new_order)
-        
-        where_row_num = "%s < _row_num" % (self.query.low_mark)
-        if self.query.high_mark:
-            where_row_num += " and _row_num <= %s" % (self.query.high_mark)
-            
-        # Lop off ORDER... and the initial "SELECT"
-        inner_select = _remove_order_limit_offset(raw_sql)
-        outer_fields, inner_select = self._alias_columns(inner_select)
-
-        # map a copy of outer_fields for injected subselect
-        f = []
-        for x in outer_fields.split(','):
-            i = x.find(' AS ')
-            if i != -1:
-                x = x[i+4:]
-            if x.find('.') != -1:
-                tbl, col = x.rsplit('.', 1)
-            else:
-                col = x
-            f.append('%s.%s' % (inner_table_name, col.strip()))
-        
-        
-        # inject a subselect to get around OVER requiring ORDER BY to come from FROM
-        inner_select = '%s FROM ( SELECT %s ) AS %s'\
-             % (', '.join(f), inner_select, inner_table_name)
-        
-        sql = "SELECT _row_num, %s FROM ( SELECT ROW_NUMBER() OVER ( ORDER BY %s) as _row_num, %s) as QQQ where %s"\
-             % (outer_fields, order, inner_select, where_row_num)
-        
-        return sql, fields
-
-    def _alias_columns(self, sql):
-        """Return tuple of SELECT and FROM clauses, aliasing duplicate column names."""
-        qn = self.connection.ops.quote_name
-        
-        outer = list()
-        inner = list()
-        names_seen = list()
-        
-        # replace all parens with placeholders
-        paren_depth, paren_buf = 0, ['']
-        parens, i = {}, 0
-        for ch in sql:
-            if ch == '(':
-                i += 1
-                paren_depth += 1
-                paren_buf.append('')
-            elif ch == ')':
-                paren_depth -= 1
-                key = '_placeholder_{0}'.format(i)
-                buf = paren_buf.pop()
-                
-                # store the expanded paren string
-                parens[key] = buf.format(**parens)
-                paren_buf[paren_depth] += '({' + key + '})'
-            else:
-                paren_buf[paren_depth] += ch
-    
-        def _replace_sub(col):
-            """Replace all placeholders with expanded values"""
-            while True:
-                m = _re_col_placeholder.search(col)
-                if m:
-                    try:
-                        key = '_placeholder_{0}'.format(
-                            int(m.group(1))
-                        )
-                        col = col.format(**{
-                            key : parens[key]
-                        })
-                    except:
-                        # not a substituted value
-                        break
-                else:
-                    break
-            return col
-    
-        temp_sql = ''.join(paren_buf)
-    
-        select_list, from_clause = _break(temp_sql, ' FROM [')
-            
-        for col in [x.strip() for x in select_list.split(',')]:
-            match = _re_pat_col.search(col)
-            if match:
-                col_name = match.group(1)
-                col_key = col_name.lower()
-
-                if col_key in names_seen:
-                    alias = qn('%s___%s' % (col_name, names_seen.count(col_key)))
-                    outer.append(alias)
-            
-                    col = _replace_sub(col)
-            
-                    inner.append("%s as %s" % (col, alias))
-                else:
-                    replaced = _replace_sub(col)
-                            
-                    outer.append(qn(col_name))
-                    inner.append(replaced)
-    
-                names_seen.append(col_key)
-            else:
-                raise Exception('Unable to find a column name when parsing SQL: {0}'.format(col))
-
-        return ', '.join(outer), ', '.join(inner) + from_clause.format(**parens)
-
-class SQLInsertCompiler(compiler.SQLInsertCompiler, SQLCompiler):
-    def as_sql(self, *args, **kwargs):
-        # Fix for Django ticket #14019
-        if not hasattr(self, 'return_id'):
-            self.return_id = False
-
-        sql, params = super(SQLInsertCompiler, self).as_sql(*args, **kwargs)
-
-        meta = self.query.get_meta()
-        
-        if meta.has_auto_field:
-            # db_column is None if not explicitly specified by model field
-            auto_field_column = meta.auto_field.db_column or meta.auto_field.column
-
-            if auto_field_column in self.query.columns:
-                quoted_table = self.connection.ops.quote_name(meta.db_table)
-                sql = "SET IDENTITY_INSERT %s ON;%s;SET IDENTITY_INSERT %s OFF" %\
-                    (quoted_table, sql, quoted_table)
-
-        return sql, params
-
-
-class SQLDeleteCompiler(compiler.SQLDeleteCompiler, SQLCompiler):
-    pass
-
-class SQLUpdateCompiler(compiler.SQLUpdateCompiler, SQLCompiler):
-    pass
-
-class SQLAggregateCompiler(compiler.SQLAggregateCompiler, SQLCompiler):
-    pass
-
-class SQLDateCompiler(compiler.SQLDateCompiler, SQLCompiler):
-    pass

source/sqlserver_ado/creation.py

-# This dictionary maps Field objects to their associated Server Server column
-# types, as strings. Column-type strings can contain format strings; they'll
-# be interpolated against the values of Field.__dict__.
-from django.conf import settings
-from django.db.backends.creation import BaseDatabaseCreation, TEST_DATABASE_PREFIX
-import sys
-
-class DatabaseCreation(BaseDatabaseCreation):
-    data_types = {
-        'AutoField':            'int IDENTITY (1, 1)',
-        'BigAutoField':         'bigint IDENTITY (1, 1)',
-        'BigIntegerField':      'bigint',
-        'BooleanField':         'bit',
-        'CharField':            'nvarchar(%(max_length)s)',
-        'CommaSeparatedIntegerField': 'nvarchar(%(max_length)s)',
-        'DateField':            'datetime',
-        'DateTimeField':        'datetime',
-        'DecimalField':         'decimal(%(max_digits)s, %(decimal_places)s)',
-        'FileField':            'nvarchar(%(max_length)s)',
-        'FilePathField':        'nvarchar(%(max_length)s)',
-        'FloatField':           'double precision',
-        'IntegerField':         'int',
-        'IPAddressField':       'nvarchar(15)',
-        'NullBooleanField':     'bit',
-        'OneToOneField':        'int',
-        'PositiveIntegerField': 'int CHECK ([%(column)s] >= 0)',
-        'PositiveSmallIntegerField': 'smallint CHECK ([%(column)s] >= 0)',
-        'SlugField':            'nvarchar(%(max_length)s)',
-        'SmallIntegerField':    'smallint',
-        'TextField':            'nvarchar(max)',
-        'TimeField':            'datetime',
-    }
-
-    def _disable_transactions(self, verbosity=1):
-        """Temporarily turn off transactions for non-transactionable SQL"""
-        if self.connection.connection.supportsTransactions:
-            if verbosity >= 1:
-                print "Disabling Transactions"
-            self._supports_transactions = self.connection.connection.supportsTransactions
-            self.connection._commit()
-            self.connection.connection.supportsTransactions = False
-
-    def _reenable_transactions(self, verbosity=1):
-        """Reset transaction support to state prior to _disable_transactions() call"""
-        if hasattr(self, '_supports_transactions'):
-            if verbosity >= 1:
-                print "Re-enabling Transactions"
-            self.connection.connection.supportsTransactions = self._supports_transactions
-
-    def _create_test_db(self, verbosity=1, autoclobber=False):
-        test_database_name = self._test_database_name(settings)
-        
-        if not self._test_database_create(settings):
-            if verbosity >= 1:
-                print "Skipping Test DB creation"
-            return test_database_name
-
-        # Create the test database and connect to it. We need to autocommit
-        # if the database supports it because PostgreSQL doesn't allow
-        # CREATE/DROP DATABASE statements within transactions.
-        cursor = self.connection.cursor()
-        suffix = self.sql_table_creation_suffix()
-        qn = self.connection.ops.quote_name
-
-        try:
-            self._disable_transactions()
-            cursor.execute("CREATE DATABASE %s %s" % (qn(test_database_name), suffix))
-            self._reenable_transactions()
-        except Exception, e:
-            sys.stderr.write("Got an error creating the test database: %s\n" % e)
-            if not autoclobber:
-                confirm = raw_input("Type 'yes' if you would like to try deleting the test database '%s', or 'no' to cancel: " % test_database_name)
-            if autoclobber or confirm == 'yes':
-                try:
-                    self._disable_transactions()
-                    if verbosity >= 1:
-                        print "Destroying old test database..."
-                    cursor.execute("DROP DATABASE %s" % qn(test_database_name))
-                    if verbosity >= 1:
-                        print "Creating test database..."
-                    cursor.execute("CREATE DATABASE %s %s" % (qn(test_database_name), suffix))
-                    self._reenable_transactions()
-                except Exception, e:
-                    sys.stderr.write("Got an error recreating the test database: %s\n" % e)
-                    sys.exit(2)
-            else:
-                print "Tests cancelled."
-                sys.exit(1)
-
-        return test_database_name
-        
-
-    def _destroy_test_db(self, test_database_name, verbosity=1):
-        "Internal implementation - remove the test db tables."
-
-        if self._test_database_create(settings):
-            qn = self.connection.ops.quote_name
-
-            # Remove the test database to clean up after
-            # ourselves. Connect to the previous database (not the test database)
-            # to do so, because it's not allowed to delete a database while being
-            # connected to it.
-            cursor = self.connection.cursor()
-            self.set_autocommit()
-            import time
-            time.sleep(1) # To avoid "database is being accessed by other users" errors.
-            self._disable_transactions()
-            cursor.execute("DROP DATABASE %s" % self.connection.ops.quote_name(test_database_name))
-            self._reenable_transactions()
-            self.connection.close()
-        else:
-            print "Skipping Test DB destruction"    
-        
-    def _test_database_create(self, settings):
-        if hasattr(settings, 'TEST_DATABASE_CREATE'):
-            return settings.TEST_DATABASE_CREATE
-        else:
-            return True
-
-    def _test_database_name(self, settings):
-        if hasattr(settings, 'TEST_DATABASE_NAME') and settings.TEST_DATABASE_NAME:
-            return settings.TEST_DATABASE_NAME
-        else:
-            return TEST_DATABASE_PREFIX + settings.DATABASE_NAME

source/sqlserver_ado/dbapi.py

-"""A DB API 2.0 interface to SQL Server for Django
-
-Forked from: adodbapi v2.1
-Copyright (C) 2002 Henrik Ekelund, version 2.1 by Vernon Cole
-* http://adodbapi.sourceforge.net/
-* http://sourceforge.net/projects/pywin32/
-
-This library is free software; you can redistribute it and/or
-modify it under the terms of the GNU Lesser General Public
-License as published by the Free Software Foundation; either
-version 2.1 of the License, or (at your option) any later version.
-
-This library is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public
-License along with this library; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-* Version 2.1D by Adam Vandenberg, forked for Django backend use.
-  This module is a db-api 2 interface for ADO, but is Django & SQL Server.
-  It won't work against other ADO sources (like Access.)
-
-DB-API 2.0 specification: http://www.python.org/dev/peps/pep-0249/
-"""
-
-import sys
-import time
-import datetime
-import re
-
-try:
-    import decimal
-except ImportError:
-    from django.utils import _decimal as decimal
-
-from django.db.utils import IntegrityError as DjangoIntegrityError
-
-import pythoncom
-import win32com.client
-
-from ado_consts import *
-
-# DB API default values
-apilevel = '2.0'
-
-# 1: Threads may share the module, but not connections.
-threadsafety = 1
-
-# The underlying ADO library expects parameters as '?', but this wrapper
-# expects '%s' parameters. This wrapper takes care of the conversion.
-paramstyle = 'format'
-
-# Set defaultIsolationLevel on module level before creating the connection.
-# It may be one of "adXact..." consts.
-defaultIsolationLevel = adXactReadCommitted
-
-# Set defaultCursorLocation on module level before creating the connection.
-# It may be one of the "adUse..." consts.
-defaultCursorLocation = adUseServer
-
-# Used for COM to Python date conversions.
-_ordinal_1899_12_31 = datetime.date(1899,12,31).toordinal()-1
-_milliseconds_per_day = 24*60*60*1000
-
-
-class MultiMap(object):
-    def __init__(self, mapping, default=None):
-        """Defines a mapping with multiple keys per value.
-
-        mapping is a dict of: tuple(key, key, key...) => value
-        """
-        self.storage = dict()
-        self.default = default
-
-        for keys, value in mapping.iteritems():
-            for key in keys:
-                self.storage[key] = value
-
-    def __getitem__(self, key):
-        return self.storage.get(key, self.default)
-
-
-def standardErrorHandler(connection, cursor, errorclass, errorvalue):
-    err = (errorclass, errorvalue)
-    connection.messages.append(err)
-    if cursor is not None:
-        cursor.messages.append(err)
-    raise errorclass(errorvalue)
-
-
-class Error(StandardError): pass
-class Warning(StandardError): pass
-
-class InterfaceError(Error): pass
-class DatabaseError(Error): pass
-
-class InternalError(DatabaseError): pass
-class OperationalError(DatabaseError): pass
-class ProgrammingError(DatabaseError): pass
-class IntegrityError(DatabaseError, DjangoIntegrityError): pass
-class DataError(DatabaseError): pass
-class NotSupportedError(DatabaseError): pass
-
-class _DbType(object):
-    def __init__(self,valuesTuple):
-        self.values = valuesTuple
-
-    def __eq__(self, other): return other in self.values
-    def __ne__(self, other): return other not in self.values
-
-
-def connect(connection_string, timeout=30):
-    """Connect to a database.
-
-    connection_string -- An ADODB formatted connection string, see:
-        http://www.connectionstrings.com/?carrier=sqlserver2005
-    timeout -- A command timeout value, in seconds (default 30 seconds)
-    """
-    try:
-        pythoncom.CoInitialize()
-        c = win32com.client.Dispatch('ADODB.Connection')
-        c.CommandTimeout = timeout
-        c.ConnectionString = connection_string
-        c.Open()
-        useTransactions = _use_transactions(c)
-        return Connection(c, useTransactions)
-    except Exception, e:
-        raise OperationalError(e, "Error opening connection: " + connection_string)
-
-def _use_transactions(c):
-    """Return True if the given ADODB.Connection supports transactions."""
-    for prop in c.Properties:
-        if prop.Name == 'Transaction DDL':
-            return prop.Value > 0
-    return False
-
-def format_parameters(parameters, show_value=False):
-    """Format a collection of ADO Command Parameters.
-
-    Used by error reporting in _execute_command.
-    """
-    directions = {
-        0: 'Unknown',
-        1: 'Input',
-        2: 'Output',
-        3: 'In/Out',
-        4: 'Return',
-    }
-
-    if show_value:
-        desc = [
-            "Name: %s, Dir.: %s, Type: %s, Size: %s, Value: \"%s\", Precision: %s, NumericScale: %s" %\
-            (p.Name, directions[p.Direction], adTypeNames.get(p.Type, str(p.Type)+' (unknown type)'), p.Size, p.Value, p.Precision, p.NumericScale)
-            for p in parameters ]
-    else:
-        desc = [
-            "Name: %s, Dir.: %s, Type: %s, Size: %s, Precision: %s, NumericScale: %s" %\
-            (p.Name, directions[p.Direction], adTypeNames.get(p.Type, str(p.Type)+' (unknown type)'), p.Size, p.Precision, p.NumericScale)
-            for p in parameters ]
-
-    return '[' + ', '.join(desc) + ']'
-
-def _configure_parameter(p, value):
-    """Configure the given ADO Parameter 'p' with the Python 'value'."""
-    if p.Direction not in [adParamInput, adParamInputOutput, adParamUnknown]:
-        return
-
-    if isinstance(value, basestring):
-        p.Value = value
-        p.Size = len(value)
-
-    elif isinstance(value, buffer):
-        p.Size = len(value)
-        p.AppendChunk(value)
-
-    elif isinstance(value, decimal.Decimal):
-        p.Value = value
-        exponent = value.as_tuple()[2]
-        digit_count = len(value.as_tuple()[1])
-        
-        if exponent == 0:
-            p.NumericScale = 0
-            p.Precision =  digit_count
-        elif exponent < 0:
-            p.NumericScale = -exponent
-            p.Precision = digit_count
-            if p.Precision < p.NumericScale:
-                p.Precision = p.NumericScale            
-        elif exponent > 0:
-            p.NumericScale = 0
-            p.Precision = digit_count + exponent
-
-    else:
-        # For any other type, set the value and let pythoncom do the right thing.
-        p.Value = value
-
-    # Use -1 instead of 0 for empty strings and buffers
-    if p.Size == 0:
-        p.Size = -1
-
-
-class Connection(object):
-    def __init__(self, adoConn, useTransactions=False):
-        self.adoConn = adoConn
-        self.errorhandler = None
-        self.messages = []
-        self.adoConn.CursorLocation = defaultCursorLocation
-        self.supportsTransactions = useTransactions
-
-        if self.supportsTransactions:
-            self.adoConn.IsolationLevel = defaultIsolationLevel
-            self.adoConn.BeginTrans() # Disables autocommit per DBPAI
-
-    def _raiseConnectionError(self, errorclass, errorvalue):
-        eh = self.errorhandler
-        if eh is None:
-            eh = standardErrorHandler
-        eh(self, None, errorclass, errorvalue)
-
-    def _close_connection(self):
-        """Close the underlying ADO Connection object, rolling back an active transaction if supported."""
-        if self.supportsTransactions:
-            self.adoConn.RollbackTrans()
-        self.adoConn.Close()
-
-    def close(self):
-        """Close the database connection."""
-        self.messages = []
-        try:
-            self._close_connection()
-        except Exception, e:
-            self._raiseConnectionError(InternalError, e)
-        pythoncom.CoUninitialize()
-
-    def commit(self):
-        """Commit a pending transaction to the database.
-
-        Note that if the database supports an auto-commit feature, this must
-        be initially off.
-        """
-        self.messages = []
-        if not self.supportsTransactions:
-            return
-
-        try:
-            self.adoConn.CommitTrans()
-            if not(self.adoConn.Attributes & adXactCommitRetaining):
-                #If attributes has adXactCommitRetaining it performs retaining commits that is,
-                #calling CommitTrans automatically starts a new transaction. Not all providers support this.
-                #If not, we will have to start a new transaction by this command:
-                self.adoConn.BeginTrans()
-        except Exception, e:
-            self._raiseConnectionError(Error, e)
-
-    def rollback(self):
-        """Abort a pending transaction."""
-        self.messages = []
-        if not self.supportsTransactions:
-            self._raiseConnectionError(NotSupportedError, None)
-
-        self.adoConn.RollbackTrans()
-        if not(self.adoConn.Attributes & adXactAbortRetaining):
-            #If attributes has adXactAbortRetaining it performs retaining aborts that is,
-            #calling RollbackTrans automatically starts a new transaction. Not all providers support this.
-            #If not, we will have to start a new transaction by this command:
-            self.adoConn.BeginTrans()
-
-    def cursor(self):
-        """Return a new Cursor object using the current connection."""
-        self.messages = []
-        return Cursor(self)
-
-    def printADOerrors(self):
-        print 'ADO Errors (%i):' % self.adoConn.Errors.Count
-        for e in self.adoConn.Errors:
-            print 'Description: %s' % e.Description
-            print 'Error: %s %s ' % (e.Number, adoErrors.get(e.Number, "unknown"))
-            if e.Number == ado_error_TIMEOUT:
-                print 'Timeout Error: Try using adodbpi.connect(constr,timeout=Nseconds)'
-            print 'Source: %s' % e.Source
-            print 'NativeError: %s' % e.NativeError
-            print 'SQL State: %s' % e.SQLState
-            
-    def _suggest_error_class(self):
-        """Introspect the current ADO Errors and determine an appropriate error class.
-        
-        Error.SQLState is a SQL-defined error condition, per the SQL specification:
-        http://www.contrib.andrew.cmu.edu/~shadow/sql/sql1992.txt
-        
-        The 23000 class of errors are integrity errors.
-        Error 40002 is a transactional integrity error.
-        """
-        if self.adoConn is not None:
-            for e in self.adoConn.Errors:
-                state = str(e.SQLState)
-                if state.startswith('23') or state=='40002':
-                    return IntegrityError
-            
-        return DatabaseError
-
-    def __del__(self):
-        try:
-            self._close_connection()
-        except: pass
-        self.adoConn = None
-
-
-class Cursor(object):
-##    This read-only attribute is a sequence of 7-item sequences.
-##    Each of these sequences contains information describing one result column:
-##        (name, type_code, display_size, internal_size, precision, scale, null_ok).
-##    This attribute will be None for operations that do not return rows or if the
-##    cursor has not had an operation invoked via the executeXXX() method yet.
-##    The type_code can be interpreted by comparing it to the Type Objects specified in the section below.
-    description = None
-
-##    This read-only attribute specifies the number of rows that the last executeXXX() produced
-##    (for DQL statements like select) or affected (for DML statements like update or insert).
-##    The attribute is -1 in case no executeXXX() has been performed on the cursor or
-##    the rowcount of the last operation is not determinable by the interface.[7]
-##    NOTE: -- adodbapi returns "-1" by default for all select statements
-    rowcount = -1
-
-    # Arraysize specifies the number of rows to fetch at a time with fetchmany().
-    arraysize = 1
-
-    def __init__(self, connection):
-        self.messages = []
-        self.connection = connection
-        self.rs = None
-        self.description = None
-        self.errorhandler = connection.errorhandler
-
-    def __iter__(self):
-        return iter(self.fetchone, None)
-        
-    def __enter__(self):
-        "Allow database cursors to be used with context managers."
-        return self
-        
-    def __exit__(self, exc_type, exc_val, exc_tb):
-        "Allow database cursors to be used with context managers."
-        self.close()
-
-    def _raiseCursorError(self, errorclass, errorvalue):
-        eh = self.errorhandler
-        if eh is None:
-            eh = standardErrorHandler
-        eh(self.connection, self, errorclass, errorvalue)
-
-    def _description_from_recordset(self, recordset):
-    	# Abort if closed or no recordset.
-        if (recordset is None) or (recordset.State == adStateClosed):
-            self.rs = None
-            self.description = None
-            return
-
-        # Since we use a forward-only cursor, rowcount will always return -1
-        self.rowcount = -1
-        self.rs = recordset
-        desc = list()
-
-        for f in self.rs.Fields:
-            display_size = None
-            if not(self.rs.EOF or self.rs.BOF):
-                display_size = f.ActualSize
-
-            null_ok = bool(f.Attributes & adFldMayBeNull)
-
-            desc.append( (f.Name, f.Type, display_size, f.DefinedSize, f.Precision, f.NumericScale, null_ok) )
-        self.description = desc
-
-    def close(self):
-        """Close the cursor."""
-        self.messages = []
-        self.connection = None
-        if self.rs and self.rs.State != adStateClosed:
-            self.rs.Close()
-            self.rs = None
-
-    def _new_command(self, command_type=adCmdText):
-        self.cmd = None
-        self.messages = []
-
-        if self.connection is None:
-            self._raiseCursorError(Error, None)
-            return
-
-        try:
-            self.cmd = win32com.client.Dispatch("ADODB.Command")
-            self.cmd.ActiveConnection = self.connection.adoConn
-            self.cmd.CommandTimeout = self.connection.adoConn.CommandTimeout
-            self.cmd.CommandType = command_type
-        except:
-            self._raiseCursorError(DatabaseError, None)
-
-    def _execute_command(self):
-        # Sprocs may have an integer return value
-        self.return_value = None
-
-        try:
-            recordset = self.cmd.Execute()
-            self.rowcount = recordset[1]
-            self._description_from_recordset(recordset[0])
-        except Exception, e:
-            _message = ""
-            if hasattr(e, 'args'): _message += str(e.args)+"\n"
-            _message += "Command:\n%s\nParameters:\n%s" %  (self.cmd.CommandText, format_parameters(self.cmd.Parameters, True))
-            klass = self.connection._suggest_error_class()
-            self._raiseCursorError(klass, _message)
-
-
-    def callproc(self, procname, parameters=None):
-        """Call a stored database procedure with the given name.
-
-        The sequence of parameters must contain one entry for each
-        argument that the sproc expects. The result of the
-        call is returned as modified copy of the input
-        sequence. Input parameters are left untouched, output and
-        input/output parameters replaced with possibly new values.
-
-        The sproc may also provide a result set as output,
-        which is available through the standard .fetch*() methods.
-
-        Extension: A "return_value" property may be set on the
-        cursor if the sproc defines an integer return value.
-        """
-        self._new_command(adCmdStoredProc)
-        self.cmd.CommandText = procname
-        self.cmd.Parameters.Refresh()
-
-        try:
-            # Return value is 0th ADO parameter. Skip it.
-            for i, p in enumerate(tuple(self.cmd.Parameters)[1:]):
-                _configure_parameter(p, parameters[i])
-        except:
-            _message = u'Converting Parameter %s: %s, %s\n' %\
-                (p.Name, ado_type_name(p.Type), repr(parameters[i]))
-
-            self._raiseCursorError(DataError, _message)
-
-        self._execute_command()
-
-        p_return_value = self.cmd.Parameters(0)
-        self.return_value = _convert_to_python(p_return_value.Value, p_return_value.Type)
-
-        return [_convert_to_python(p.Value, p.Type)
-            for p in tuple(self.cmd.Parameters)[1:] ]
-
-
-    def execute(self, operation, parameters=None):
-        """Prepare and execute a database operation (query or command).
-
-        Return value is not defined.
-        """
-        self._new_command()
-
-        if parameters is None:
-            parameters = list()
-
-        parameter_replacements = list()
-        for i, value in enumerate(parameters):
-            if value is None:
-                parameter_replacements.append('NULL')
-                continue
-                
-            if isinstance(value, basestring) and value == "":
-                parameter_replacements.append("''")
-                continue
-
-            # Otherwise, process the non-NULL, non-empty string parameter.
-            parameter_replacements.append('?')
-            try:
-                p = self.cmd.CreateParameter('p%i' % i, _ado_type(value))
-            except KeyError:
-                _message = u'Failed to map python type "%s" to an ADO type' % (value.__class__.__name__,)
-                self._raiseCursorError(DataError, _message)
-            except:    
-                _message = u'Creating Parameter p%i, %s' % (i, _ado_type(value))
-                self._raiseCursorError(DataError, _message)
-
-            try:
-                _configure_parameter(p, value)
-                self.cmd.Parameters.Append(p)
-            except:
-                _message = u'Converting Parameter %s: %s, %s\n' %\
-                    (p.Name, ado_type_name(p.Type), repr(value))
-
-                self._raiseCursorError(DataError, _message)
-
-        # Replace params with ? or NULL
-        if parameter_replacements:
-            operation = operation % tuple(parameter_replacements)
-
-        self.cmd.CommandText = operation
-        self._execute_command()
-
-    def executemany(self, operation, seq_of_parameters):
-        """Execute the given command against all parameter sequences or mappings given in seq_of_parameters."""
-        self.messages = list()
-        total_recordcount = 0
-
-        for params in seq_of_parameters:
-            self.execute(operation, params)
-
-            if self.rowcount == -1:
-                total_recordcount = -1
-
-            if total_recordcount != -1:
-                total_recordcount += self.rowcount
-
-        self.rowcount = total_recordcount
-
-    def _fetch(self, rows=None):
-        """Fetch rows from the current recordset.
-
-        rows -- Number of rows to fetch, or None (default) to fetch all rows.
-        """
-        if self.connection is None or self.rs is None:
-            self._raiseCursorError(Error, None)
-            return
-
-        if self.rs.State == adStateClosed or self.rs.BOF or self.rs.EOF:
-            if rows == 1: # fetchone returns None
-                return None
-            else: # fetchall and fetchmany return empty lists
-                return list()
-
-        if rows:
-            ado_results = self.rs.GetRows(rows)
-        else:
-            ado_results = self.rs.GetRows()
-
-        py_columns = list()
-        column_types = [column_desc[1] for column_desc in self.description]
-        for ado_type, column in zip(column_types, ado_results):
-            py_columns.append( [_convert_to_python(cell, ado_type) for cell in column] )
-
-        return tuple(zip(*py_columns))
-
-    def fetchone(self):
-        """Fetch the next row of a query result set, returning a single sequence, or None when no more data is available.
-
-        An Error (or subclass) exception is raised if the previous call to executeXXX()
-        did not produce any result set or no call was issued yet.
-        """
-        self.messages = list()
-        result = self._fetch(1)
-        if result: # return record (not list of records)
-            return result[0]
-        return None
-
-    def fetchmany(self, size=None):
-        """Fetch the next set of rows of a query result, returning a list of tuples. An empty sequence is returned when no more rows are available."""
-        self.messages = list()
-        if size is None:
-            size = self.arraysize
-        return self._fetch(size)
-
-    def fetchall(self):
-        """Fetch all remaining rows of a query result, returning them as a sequence of sequences."""
-        self.messages = list()
-        return self._fetch()
-
-    def nextset(self):
-        """Skip to the next available recordset, discarding any remaining rows from the current recordset.
-
-        If there are no more sets, the method returns None. Otherwise, it returns a true
-        value and subsequent calls to the fetch methods will return rows from the next result set.
-        """
-        self.messages = list()
-        if self.connection is None or self.rs is None:
-            self._raiseCursorError(Error, None)
-            return None
-
-        recordset = self.rs.NextRecordset()[0]
-        if recordset is None:
-            return None
-            
-        self._description_from_recordset(recordset)
-        return True
-
-    def setinputsizes(self, sizes): pass
-    def setoutputsize(self, size, column=None): pass
-
-# Type specific constructors as required by the DB-API 2 specification.
-Date = datetime.date
-Time = datetime.time
-Timestamp = datetime.datetime
-Binary = buffer
-
-def DateFromTicks(ticks):
-    """Construct an object holding a date value from the given # of ticks."""
-    return Date(*time.localtime(ticks)[:3])
-
-def TimeFromTicks(ticks):
-    """Construct an object holding a time value from the given # of ticks."""
-    return Time(*time.localtime(ticks)[3:6])
-
-def TimestampFromTicks(ticks):
-    """Construct an object holding a timestamp value from the given # of ticks."""
-    return Timestamp(*time.localtime(ticks)[:6])
-
-adoIntegerTypes = (adInteger,adSmallInt,adTinyInt,adUnsignedInt,adUnsignedSmallInt,adUnsignedTinyInt,adError)
-adoRowIdTypes = (adChapter,)
-adoLongTypes = (adBigInt, adUnsignedBigInt, adFileTime)
-adoExactNumericTypes = (adDecimal, adNumeric, adVarNumeric, adCurrency)
-adoApproximateNumericTypes = (adDouble, adSingle)
-adoStringTypes = (adBSTR,adChar,adLongVarChar,adLongVarWChar,adVarChar,adVarWChar,adWChar,adGUID)
-adoBinaryTypes = (adBinary, adLongVarBinary, adVarBinary)
-adoDateTimeTypes = (adDBTime, adDBTimeStamp, adDate, adDBDate)
-
-# Required DBAPI type specifiers
-STRING   = _DbType(adoStringTypes)
-BINARY   = _DbType(adoBinaryTypes)
-NUMBER   = _DbType((adBoolean,) + adoIntegerTypes + adoLongTypes + adoExactNumericTypes + adoApproximateNumericTypes)
-DATETIME = _DbType(adoDateTimeTypes)
-# Not very useful for SQL Server, as normal row ids are usually just integers.
-ROWID    = _DbType(adoRowIdTypes)
-
-
-# Mapping ADO data types to Python objects.
-def _convert_to_python(variant, adType):
-    if variant is None:
-        return None
-    return _variantConversions[adType](variant)
-
-def _cvtDecimal(variant):
-    return _convertNumberWithCulture(variant, decimal.Decimal)
-
-def _cvtFloat(variant):
-    return _convertNumberWithCulture(variant, float)
-
-def _convertNumberWithCulture(variant, f):
-    try:
-        return f(variant)
-    except (ValueError,TypeError,decimal.InvalidOperation):
-        try:
-            europeVsUS = str(variant).replace(",",".")
-            return f(europeVsUS)
-        except (ValueError,TypeError): pass
-
-def _cvtComDate(comDate):
-    date_as_float = float(comDate)
-    day_count = int(date_as_float)
-    fraction_of_day = abs(date_as_float - day_count)
-
-    return (datetime.datetime.fromordinal(day_count + _ordinal_1899_12_31) +
-        datetime.timedelta(milliseconds=fraction_of_day * _milliseconds_per_day))
-
-_variantConversions = MultiMap(
-    {
-        adoDateTimeTypes : _cvtComDate,
-        adoExactNumericTypes: _cvtDecimal,
-        adoApproximateNumericTypes: _cvtFloat,
-        (adBoolean,): bool,
-        adoLongTypes+adoRowIdTypes : long,
-        adoIntegerTypes: int,
-        adoBinaryTypes: buffer, 
-    }, 
-    lambda x: x)
-
-# Mapping Python data types to ADO type codes
-def _ado_type(data):
-    if isinstance(data, basestring):
-        return adBSTR
-    return _map_to_adotype[type(data)]
-
-_map_to_adotype = {
-    buffer: adBinary,
-    float: adDouble,
-    int: adInteger,
-    long: adBigInt,
-    bool: adBoolean,
-    decimal.Decimal: adDecimal,
-    datetime.date: adDate,
-    datetime.datetime: adDate,
-    datetime.time: adDate, 
-}

source/sqlserver_ado/fields.py

-"""This module provides SQL Server specific fields for Django models."""
-from django.db.models import AutoField, ForeignKey, IntegerField
-from django.forms import ValidationError
-from django.utils.translation import ugettext_lazy as _
-
-class BigAutoField(AutoField):
-    """A bigint IDENTITY field"""
-    def get_internal_type(self):
-        return "BigAutoField"
-
-    def to_python(self, value):
-        if value is None:
-            return value
-        try:
-            return long(value)
-        except (TypeError, ValueError):
-            raise ValidationError(
-                _("This value must be a long."))
-
-    def get_db_prep_value(self, value):
-        if value is None:
-            return None
-        return long(value)
-
-class BigForeignKey(ForeignKey):
-    """A ForeignKey field that points to a BigAutoField or BigIntegerField"""
-    def db_type(self):
-        return BigIntegerField().db_type()
-
-class BigIntegerField(IntegerField):
-    """A BigInteger field, until Django ticket #399 lands (if ever.)"""
-    def get_internal_type(self):
-        return "BigIntegerField"
-
-    def to_python(self, value):
-        if value is None:
-            return value
-        try:
-            return long(value)
-        except (TypeError, ValueError):
-            raise ValidationError(
-                _("This value must be a long."))
-
-    def get_db_prep_value(self, value):
-        if value is None:
-            return None
-        return long(value)

source/sqlserver_ado/introspection.py

-from django.db.backends import BaseDatabaseIntrospection
-import ado_consts
-
-class DatabaseIntrospection(BaseDatabaseIntrospection):
-    def get_table_list(self, cursor):
-        "Return a list of table and view names in the current database."
-        cursor.execute("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' UNION SELECT TABLE_NAME FROM INFORMATION_SCHEMA.VIEWS")
-        return [row[0] for row in cursor.fetchall()]
-
-    def _is_auto_field(self, cursor, table_name, column_name):
-        """Check if a column is an identity column.
-
-        See: http://msdn2.microsoft.com/en-us/library/ms174968.aspx
-        """
-        sql = "SELECT COLUMNPROPERTY(OBJECT_ID(N'%s'), N'%s', 'IsIdentity')" % \
-            (table_name, column_name)
-
-        cursor.execute(sql)
-        return cursor.fetchone()[0]
-
-    def get_table_description(self, cursor, table_name, identity_check=True):
-        """Return a description of the table, with DB-API cursor.description interface.
-
-        The 'auto_check' parameter has been added to the function argspec.
-        If set to True, the function will check each of the table's fields for the
-        IDENTITY property (the IDENTITY property is the MSSQL equivalent to an AutoField).
-
-        When a field is found with an IDENTITY property, it is given a custom field number
-        of SQL_AUTOFIELD, which maps to the 'AutoField' value in the DATA_TYPES_REVERSE dict.
-        """
-        cursor.execute("SELECT * FROM [%s] where 1=0" % (table_name))
-        columns = cursor.description
-
-        items = list()
-        for column in columns:
-            column = list(column) # Convert tuple to list
-            if identity_check and self._is_auto_field(cursor, table_name, column[0]):
-                column[1] = ado_consts.AUTO_FIELD_MARKER
-            items.append(column)
-        return items
-
-    def _name_to_index(self, cursor, table_name):
-        """Return a dictionary of {field_name: field_index} for the given table.
-        
-        Indexes are 0-based.
-        """
-        return dict([(d[0], i) for i, d in enumerate(self.get_table_description(cursor, table_name, False))])
-
-    def get_relations(self, cursor, table_name):
-        source_field_dict = self._name_to_index(cursor, table_name)
-
-        sql = """
-select
-    column_name = fk_cols.column_name,
-    referenced_table_name = pk.table_name,
-    referenced_column_name = pk_cols.column_name
-from information_schema.referential_constraints ref_const
-join information_schema.table_constraints fk
-	on ref_const.constraint_catalog = fk.constraint_catalog
-	and ref_const.constraint_schema = fk.constraint_schema
-	and ref_const.constraint_name = fk.constraint_name
-	and fk.constraint_type = 'foreign key'
-
-join information_schema.table_constraints pk
-	on ref_const.unique_constraint_catalog = pk.constraint_catalog
-	and ref_const.unique_constraint_schema = pk.constraint_schema
-	and ref_const.unique_constraint_name = pk.constraint_name
-	and pk.constraint_type = 'primary key'
-
-join information_schema.key_column_usage fk_cols
-	on ref_const.constraint_name = fk_cols.constraint_name
-
-join information_schema.key_column_usage pk_cols
-	on pk.constraint_name = pk_cols.constraint_name
-where
-	fk.table_name = %s"""
-
-        cursor.execute(sql,[table_name])
-        relations = cursor.fetchall()
-        relation_map = dict()
-
-        for source_column, target_table, target_column in relations:
-            target_field_dict = self._name_to_index(cursor, target_table)
-            target_index = target_field_dict[target_column]
-            source_index = source_field_dict[source_column]
-
-            relation_map[source_index] = (target_index, target_table)
-
-        return relation_map
-
-    def get_indexes(self, cursor, table_name):
-    #    Returns a dictionary of fieldname -> infodict for the given table,
-    #    where each infodict is in the format:
-    #        {'primary_key': boolean representing whether it's the primary key,
-    #         'unique': boolean representing whether it's a unique index}
-        sql = """
-select
-	C.name as [column_name],
-	IX.is_unique as [unique],
-    IX.is_primary_key as [primary_key]
-from
-	sys.tables T
-	join sys.index_columns IC on IC.object_id = T.object_id
-	join sys.columns C on C.object_id = T.object_id and C.column_id = IC.column_id
-	join sys.indexes Ix on Ix.object_id = T.object_id and Ix.index_id = IC.index_id
-where
-	T.name = %s
-	and (Ix.is_unique=1 or Ix.is_primary_key=1)
-    -- Omit multi-column keys
-	and not exists (
-		select *
-		from sys.index_columns cols
-		where
-			cols.object_id = T.object_id
-			and cols.index_id = IC.index_id
-			and cols.key_ordinal > 1
-	)
-"""
-        cursor.execute(sql,[table_name])
-        constraints = cursor.fetchall()
-        indexes = dict()
-
-        for column_name, unique, primary_key in constraints:
-            indexes[column_name.lower()] = {"primary_key":primary_key, "unique":unique}
-
-        return indexes
-
-
-    data_types_reverse = {
-        ado_consts.AUTO_FIELD_MARKER: '