Commits

adamv committed 1244c24

- Style changes to source code
- Updated the ADO driver to use a couple of 2.3-isms (figured it was about time.)
- Removed remnants of mxDatetime support to simplify date conversions
- Moved ADO consts to separate module, which is now shared w/ introspection.py
- Added workaround for Issue 6: http://code.google.com/p/django-mssql/issues/detail?id=6
- Added fix for Issue 8: http://code.google.com/p/django-mssql/issues/detail?id=8
- Added more test projects (but nothing automated yet.)

  • Participants
  • Parent commits 891b9f9

Comments (0)

Files changed (22)

src/sqlserver_ado/ado_consts.py

+# IsolationLevelEnum
+adXactBrowse          = 0x100
+adXactChaos           = 0x10
+adXactCursorStability = 0x1000
+adXactIsolated        = 0x100000
+adXactReadCommitted   = 0x1000
+adXactReadUncommitted = 0x100
+adXactRepeatableRead  = 0x10000
+adXactSerializable    = 0x100000
+
+
+# CursorLocationEnum
+adUseClient = 3
+adUseServer = 2
+
+
+# CommandTypeEnum
+adCmdText = 1
+adCmdStoredProc = 4
+
+
+# ParameterDirectionEnum
+adParamInput       = 1
+adParamInputOutput = 3
+adParamOutput      = 2
+adParamReturnValue = 4
+adParamUnknown     = 0
+
+
+# ObjectStateEnum
+adStateClosed     = 0 # Indicates that the object is closed. 
+adStateOpen       = 1 # Indicates that the object is open. 
+adStateConnecting = 2 # Indicates that the object is connecting. 
+adStateExecuting  = 4 # Indicates that the object is executing a command. 
+adStateFetching   = 8 # Indicates that the rows of the object are being retrieved.
+
+
+# FieldAttributeEnum
+adFldMayBeNull = 0x40
+
+
+# ConnectModeEnum
+adModeShareExclusive = 12
+
+
+# XactAttributeEnum
+adXactCommitRetaining = 131072
+adXactAbortRetaining = 262144
+
+
+ado_error_TIMEOUT = -2147217871
+
+
+# ADO Data types documented at:
+# http://msdn2.microsoft.com/en-us/library/ms675318.aspx
+
+# DataTypeEnum
+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
+
+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',
+   }
+
+
+# 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                '
+    }

src/sqlserver_ado/adodb_django.py

     * DateObjectFromCOMDate always returns a DateTime, as this is what Django template
     	filters expect from a DateTimeField
 	* Inlined Django monkeypatching of ConvertVariantToPython
+	--
+	* Removed remnants of mx support, simplifying the datetime conversion code a bit.
+	
+	Python's DB-API 2.0:
+	http://www.python.org/dev/peps/pep-0249/
 """
 
+from ado_consts import *
+
 import string
 import exceptions
 import time
 import sys
 import traceback
 import datetime
+import re
+
+rx_datetime=re.compile(r'^(\d{4}-\d\d?-\d\d? \d\d?:\d\d?:\d\d?.\d{3})\d{3}$')
 
 try:
     import decimal
-except ImportError:  #perhaps running Cpython 2.3
-    import win32com.decimal_23 as decimal
+except ImportError: # for Python 2.3
+    from django.utils import _decimal as decimal
 
 import win32com.client
+
+# Request Python decimal types
 import pythoncom
 pythoncom.__future_currency__ = True
 
+
+apilevel = '2.0' # String constant stating the supported DB API level.
+
+threadsafety = 1
+# Integer constant stating the level of thread safety the interface supports,
+# 1 = Threads may share the module, but not connections.
+## Possible values are:
+##0 = Threads may not share the module.
+##1 = Threads may share the module, but not connections.
+##2 = Threads may share the module and connections.
+##3 = Threads may share the module, connections and cursors.
+
+paramstyle = 'qmark'
+# String constant stating the type of parameter marker formatting expected by the interface.
+# 'qmark' = Question mark style, e.g. '...WHERE name=?'
+
+version = __doc__.split('-',2)[0]
+
+
+# Verbosity of logging code
+# 0/False = off
+# 1/True = on
+# 2 = More on
+# 3... = Really really on for real
+verbose = False
+
+defaultIsolationLevel=adXactReadCommitted
+#  Set defaultIsolationLevel on module level before creating the connection.
+#   It may be one of "adXact..." consts.
+#   if you simply "import adodbapi", you'll say:
+#   "adodbapi.adodbapi.defaultIsolationLevel=adodbapi.adodbapi.adXactBrowse", for example
+
+defaultCursorLocation=adUseServer
+#  Set defaultCursorLocation on module level before creating the connection.
+#   It may be one of the "adUse..." consts.
+
+
+_python_datetime_types = (type(datetime.date), type(datetime.datetime), type(datetime.time))
+
 def standardErrorHandler(connection,cursor,errorclass,errorvalue):
-    err=(errorclass,errorvalue)
+    err = (errorclass,errorvalue)
     connection.messages.append(err)
-    if cursor!=None:
+    if cursor is not None:
         cursor.messages.append(err)
     raise errorclass(errorvalue)
 
 
-class TimeConverter:
-    def __init__(self):
-        #Use self.types to compare if an input parameter is a datetime
-        self.types = (type(self.Date(2000,1,1)),
-                      type(self.Time(12,01,1)),
-                      type(self.Timestamp(2000,1,1,12,1,1)))
-    def COMDate(self,obj):
-        'Returns a ComDate from a datetime in inputformat'
-        raise "Abstract class"
-
-    def DateObjectFromCOMDate(self,comDate):
-        'Returns an object of the wanted type from a ComDate'
-        raise "Abstract class"
-
-    def Date(self,year,month,day):
-        "This function constructs an object holding a date value. "
-        raise "Abstract class"
-
-    def Time(self,hour,minute,second):
-        "This function constructs an object holding a time value. "
-        raise "Abstract class"
-
-    def Timestamp(self,year,month,day,hour,minute,second):
-        "This function constructs an object holding a time stamp value. "
-        raise "Abstract class"
-
-    def DateObjectToIsoFormatString(self,obj):
-        "This function should return a string in the format 'YYYY-MM-dd HH:MM:SS:ms' (ms optional) "
-        raise "Abstract class"
-
-
-class pythonDateTimeConverter(TimeConverter): # datetime module is available in Python 2.3
-    def __init__(self):
-        self._ordinal_1899_12_31=datetime.date(1899,12,31).toordinal()-1
-        TimeConverter.__init__(self)
-
-    def COMDate(self,obj):
-        tt=obj.timetuple()
-        try:
-            ms=obj.microsecond
-        except:
-            ms=0
-        YMDHMSmsTuple=tuple(tt[:6] + (ms,))
-        return self.COMDateFromTuple(YMDHMSmsTuple)
-
-    def DateObjectFromCOMDate(self,comDate):
-        #ComDate is number of days since 1899-12-31
-        fComDate=float(comDate)
-        millisecondsperday=86400000 # 24*60*60*1000
-        integerPart = int(fComDate)
-        floatpart=fComDate-integerPart
-        
-        # adamv: Changed from datetime.date to datetime.datetime,
-        # time will be midnight
-        if floatpart == 0.0:
-            return datetime.datetime.fromordinal(integerPart + self._ordinal_1899_12_31)
-            
-        dte=datetime.datetime.fromordinal(integerPart + self._ordinal_1899_12_31)
-        dte=dte+datetime.timedelta(milliseconds=floatpart*millisecondsperday)
-        return dte
-
-
-    def Date(self,year,month,day):
-        return datetime.date(year,month,day)
-
-    def Time(self,hour,minute,second):
-        return datetime.time(hour,minute,second)
-
-    def Timestamp(self,year,month,day,hour,minute,second):
-        return datetime.datetime(year,month,day,hour,minute,second)
-
-    def COMDateFromTuple(self,YMDHMSmsTuple):
-        t=pythoncom.MakeTime(YMDHMSmsTuple)
-        return float(t)
-
-    def DateObjectToIsoFormatString(self,obj):
-        if isinstance(obj,datetime.datetime):
-            s = obj.strftime('%Y-%m-%d %H:%M:%S')
-        else:
-            try:  #usually datetime.datetime
-                s = obj.isoformat()
-            except:  #but may be mxdatetime
-                s = obj.Format('%Y-%m-%d %H:%M:%S')
-        return s
-
-class pythonTimeConverter(TimeConverter):
-    def __init__(self):
-        year,month,day,hour,minute,second,weekday,julianday,daylightsaving=time.gmtime(0)
-        self.com1970=self.COMDateFromTuple((year,month,day,hour,minute,second))
-        daysInMonth=[31,28,31,30,31,30,31,30,31,30,31,30]
-        cumulative=0
-        self.monthCumDays={}
-        for i in range(12):
-            self.monthCumDays[i+1]=cumulative
-            cumulative+=daysInMonth[i]
-        TimeConverter.__init__(self)
-
-    def COMDate(self,timeobj):
-        return float(pythoncom.MakeTime(time.mktime(timeobj)))
-
-    def COMDateFromTuple(self,YMDHMSmsTuple):
-        t=pythoncom.MakeTime(YMDHMSmsTuple)
-        return float(t)
-
-    def DateObjectFromCOMDate(self,comDate):
-        'Returns ticks since 1970'
-        secondsperday=86400 # 24*60*60
-        #ComDate is number of days since 1899-12-31
-        t=time.gmtime(secondsperday*(float(comDate)-self.com1970 ))
-        return t  #year,month,day,hour,minute,second,weekday,julianday,daylightsaving=t
-
-    def Date(self,year,month,day):
-        return self.Timestamp(year,month,day,0,0,0)
-
-    def Time(self,hour,minute,second):
-        return time.gmtime((hour*60+minute)*60 + second)
-
-    def Timestamp(self,year,month,day,hour,minute,second):
-        julian=self.monthCumDays[month] + day
-        if month>2:
-            julian+=calendar.isleap(year)
-        weekday=calendar.weekday(year,month,day)
-        daylightsaving=-1
-        return time.localtime(time.mktime((year,month,day,hour,minute,second,weekday,julian,daylightsaving)))
-
-    def DateObjectToIsoFormatString(self,obj):
-        try:
-            s = time.strftime('%Y-%m-%d %H:%M:%S',obj)
-        except:
-            s = obj.strftime('%Y-%m-%d')
-        return s
-
 class Error(exceptions.StandardError):
     pass
 
 class NotSupportedError(DatabaseError):
     pass
 
-verbose = False
+def _logger(message, log_level=1):
+	if verbose and verbose>=log_level: print message
 
-version = __doc__.split('-',2)[0] #v2.1 Cole
-
-def connect(connstr, timeout=30):               #v2.1 Simons
+def connect(connstr, timeout=30):
     "Connection string as in the ADO documentation, SQL timeout in seconds"
-    global verbose
-    pythoncom.CoInitialize()                 #v2.1 Paj
+    pythoncom.CoInitialize()
     conn=win32com.client.Dispatch('ADODB.Connection')
-    conn.CommandTimeout=timeout             #v2.1 Simons
+    conn.CommandTimeout=timeout
     conn.ConnectionString=connstr
 
-    if verbose:
-        print '%s attempting: "%s"' % (version,connstr)
+    _logger('%s attempting: "%s"' % (version,connstr))
     try:
         conn.Open()
     except (Exception), e:
         raise DatabaseError(e)
     return Connection(conn)
 
-apilevel='2.0' #String constant stating the supported DB API level.
 
-threadsafety=1
-# Integer constant stating the level of thread safety the interface supports,
-# 1 = Threads may share the module, but not connections.
-## Possible values are:
-##0 = Threads may not share the module.
-##1 = Threads may share the module, but not connections.
-##2 = Threads may share the module and connections.
-##3 = Threads may share the module, connections and cursors.
-
-paramstyle='qmark'
-#String constant stating the type of parameter marker formatting expected by the interface.
-# 'qmark' = Question mark style, e.g. '...WHERE name=?'
-
-
-adXactBrowse                  =0x100      # from enum IsolationLevelEnum
-adXactChaos                   =0x10       # from enum IsolationLevelEnum
-adXactCursorStability         =0x1000     # from enum IsolationLevelEnum
-adXactIsolated                =0x100000   # from enum IsolationLevelEnum
-adXactReadCommitted           =0x1000     # from enum IsolationLevelEnum
-adXactReadUncommitted         =0x100      # from enum IsolationLevelEnum
-adXactRepeatableRead          =0x10000    # from enum IsolationLevelEnum
-adXactSerializable            =0x100000   # from enum IsolationLevelEnum
-
-defaultIsolationLevel=adXactReadCommitted
-#  Set defaultIsolationLevel on module level before creating the connection.
-#   It may be one of the above
-#   if you simply "import adodbapi", you'll say:
-#   "adodbapi.adodbapi.defaultIsolationLevel=adodbapi.adodbapi.adXactBrowse", for example
-
-
-adUseClient = 3     # from enum CursorLocationEnum      #v2.1 Rose
-adUseServer = 2     # from enum CursorLocationEnum      #v2.1 Rose
-
-defaultCursorLocation=adUseServer                       #v2.1 Rose
-#  Set defaultCursorLocation on module level before creating the connection.
-#   It may be one of the above
-
-class Connection:
+class Connection(object):
     def __init__(self,adoConn):
-        self.adoConn=adoConn
-        self.supportsTransactions=False
+        self.adoConn = adoConn
+        self.supportsTransactions = False
+        
         for indx in range(adoConn.Properties.Count):
             if adoConn.Properties(indx).Name == 'Transaction DDL' \
-            and adoConn.Properties(indx).Value != 0:        #v2.1 Albrecht
+            and adoConn.Properties(indx).Value != 0:
                 self.supportsTransactions=True
-        self.adoConn.CursorLocation = defaultCursorLocation #v2.1 Rose
+
+        self.adoConn.CursorLocation = defaultCursorLocation
+        
         if self.supportsTransactions:
             self.adoConn.IsolationLevel=defaultIsolationLevel
             self.adoConn.BeginTrans() #Disables autocommit
+
         self.errorhandler=None
         self.messages=[]
-        global verbose
+
         if verbose:
             print 'adodbapi New connection at %X' % id(self)
 
     def _raiseConnectionError(self,errorclass,errorvalue):
-        eh=self.errorhandler
-        if eh==None:
-            eh=standardErrorHandler
+        eh = self.errorhandler
+        if eh is None:
+            eh = standardErrorHandler
         eh(self,None,errorclass,errorvalue)
 
-    def _closeAdoConnection(self):                  #all v2.1 Rose
+    def _closeAdoConnection(self):
         """close the underlying ADO Connection object,
            rolling it back first if it supports transactions."""
         if self.supportsTransactions:
         an Error (or subclass) exception will be raised if any operation is attempted with the connection.
         The same applies to all cursor objects trying to use the connection.
         """
-        self.messages=[]
+        self.messages = []
 
         try:
-            self._closeAdoConnection()                      #v2.1 Rose
+            self._closeAdoConnection()
         except (Exception), e:
             self._raiseConnectionError(InternalError,e)
-        pythoncom.CoUninitialize()                             #v2.1 Paj
+        pythoncom.CoUninitialize()
 
     def commit(self):
         """Commit any pending transaction to the database.
         this must be initially off. An interface method may be provided to turn it back on.
         Database modules that do not support transactions should implement this method with void functionality.
         """
-        self.messages=[]
+        self.messages = []
         try:
             if self.supportsTransactions:
                 self.adoConn.CommitTrans()
         the method available. These interfaces should then raise a NotSupportedError to indicate the
         non-ability to perform the roll back when the method is invoked.
         """
-        self.messages=[]
+        self.messages = []
         if self.supportsTransactions:
             self.adoConn.RollbackTrans()
             if not(self.adoConn.Attributes & adXactAbortRetaining):
         else:
             self._raiseConnectionError(NotSupportedError,None)
 
-        #TODO: Could implement the prefered method by havins two classes,
+        #TODO: Could implement the prefered method by having two classes,
         # one with trans and one without, and make the connect function choose which one.
         # the one without transactions should not implement rollback
 
     def cursor(self):
         "Return a new Cursor Object using the connection."
-        self.messages=[]
+        self.messages = []
         return Cursor(self)
 
     def printADOerrors(self):
-        j=self.adoConn.Errors.Count
-        print 'ADO Errors:(%i)' % j
-        for i in range(j):
-            e=self.adoConn.Errors(i)
+        error_count = self.adoConn.Errors.Count
+        print 'ADO Errors:(%i)' % error_count
+        for i in range(error_count):
+            e = self.adoConn.Errors(i)
             print 'Description: %s' % e.Description
             print 'Number: %s ' % e.Number
             try:
             print 'Source: %s' %e.Source
             print 'NativeError: %s' %e.NativeError
             print 'SQL State: %s' %e.SQLState
-            if e.Number == -2147217871:
+            if e.Number == ado_error_TIMEOUT:
                 print ' Error means that the Query timed out.\n Try using adodbpi.connect(constr,timeout=Nseconds)'
 
     def __del__(self):
         try:
-            self._closeAdoConnection()                  #v2.1 Rose
+            self._closeAdoConnection()
         except:
             pass
-        self.adoConn=None
+        self.adoConn = None
 
 
-class Cursor:
-    description=None
+def _log_parameters(parameters):
+    """Log parameters for debugging queries."""
+    for i in range(parameters.Count):
+        param = parameters(i)
+        print 'adodbapi parameter attributes before=', param.Name, param.Type, param.Direction, param.Size
+
+def _findReturnValueIndex(isStoredProcedureCall, parameters):
+	if not isStoredProcedureCall:
+		return -1
+		
+	if parameters.Count <> len(parameters):
+		for i in range(parameters.Count):
+			if parameters(i).Direction == adParamReturnValue:
+				return i
+	return -1;
+        
+class Cursor(object):
+    description = None
 ##    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).
 ##    the rowcount of the last operation is not determinable by the interface.[7]
 ##    NOTE: -- adodbapi returns "-1" by default for all select statements
 
-    arraysize=1
+    arraysize = 1
 ##    This read/write attribute specifies the number of rows to fetch at a time with fetchmany().
 ##    It defaults to 1 meaning to fetch a single row at a time.
 ##    Implementations must observe this value with respect to the fetchmany() method,
 ##    It may also be used in the implementation of executemany().
 
     def __init__(self,connection):
-        self.messages=[]
-        self.conn=connection
-        self.rs=None
-        self.description=None
-        self.errorhandler=connection.errorhandler
+        self.messages = []
+        self.conn = connection
+        self.rs = None
+        self.description = None
+        self.errorhandler = connection.errorhandler
         if verbose:
             print 'adodbapi New cursor at %X on conn %X' % (id(self),id(self.conn))
 
-    def __iter__(self):                   # [2.1 Zamarev]
-        return iter(self.fetchone, None)  # [2.1 Zamarev]
+    def __iter__(self):
+        return iter(self.fetchone, None)
 
     def _raiseCursorError(self,errorclass,errorvalue):
-        eh=self.errorhandler
-        if eh==None:
-            eh=standardErrorHandler
+        eh = self.errorhandler
+        if eh is None:
+            eh = standardErrorHandler
         eh(self.conn,self,errorclass,errorvalue)
 
     def callproc(self, procname, parameters=None):
             The procedure may also provide a result set as output, which is
             then available through the standard fetchXXX() methods.
         """
-        self.messages=[]
+        self.messages = []
         return self._executeHelper(procname,True,parameters)
 
     def _returnADOCommandParameters(self,adoCommand):
-        retLst=[]
+        retLst = []
         for i in range(adoCommand.Parameters.Count):
-            p=adoCommand.Parameters(i)
+            p = adoCommand.Parameters(i)
             if verbose > 2:
-                   'return', p.Name, p.Type, p.Direction, repr(p.Value)
-            type_code=p.Type
-            pyObject=convertVariantToPython(p.Value,type_code)
+                print 'return', p.Name, p.Type, p.Direction, repr(p.Value)
+            pyObject = convertVariantToPython(p.Value,p.Type)
             if p.Direction == adParamReturnValue:
-                self.returnValue=pyObject
+                self.returnValue = pyObject
             else:
                 retLst.append(pyObject)
         return retLst
 
     def _makeDescriptionFromRS(self,rs):
-        if (rs == None) or (rs.State == adStateClosed):
-            self.rs=None
-            self.description=None
-        else:
-            #self.rowcount=rs.RecordCount
-            # Since the current implementation has a forward-only cursor, RecordCount will always return -1
-            # The ADO documentation hints that obtaining the recordcount may be timeconsuming
-            #   "If the Recordset object does not support approximate positioning, this property
-            #    may be a significant drain on resources # [ekelund]
-            # Therefore, COM will not return rowcount in most cases. [Cole]
-            # Switching to client-side cursors will force a static cursor,
-            # and rowcount will then be set accurately [Cole]
-            self.rowcount = -1
-            self.rs=rs
-            nOfFields=rs.Fields.Count
-            self.description=[]
-            for i in range(nOfFields):
-                f=rs.Fields(i)
-                name=f.Name
-                type_code=f.Type
-                if not(rs.EOF or rs.BOF):
-                    display_size=f.ActualSize #TODO: Is this the correct defintion according to the DB API 2 Spec ?
-                else:
-                    display_size=None
-                internal_size=f.DefinedSize
-                precision=f.Precision
-                scale=f.NumericScale
-                null_ok= bool(f.Attributes & adFldMayBeNull)          #v2.1 Cole
-                self.description.append((name, type_code, display_size, internal_size, precision, scale, null_ok))
+    	# Abort if closed or no recordset.
+        if (rs is None) or (rs.State == adStateClosed):
+            self.rs = None
+            self.description = None
+            return
+
+        # Since the current implementation has a forward-only cursor, RecordCount will always return -1
+        # The ADO documentation hints that obtaining the recordcount may be timeconsuming
+        #   "If the Recordset object does not support approximate positioning, this property
+        #    may be a significant drain on resources # [ekelund]
+        # Therefore, COM will not return rowcount in most cases. [Cole]
+        # Switching to client-side cursors will force a static cursor,
+        # and rowcount will then be set accurately [Cole]
+        self.rowcount = -1
+        self.rs=rs
+        self.description=[]
+        for i in range(self.rs.Fields.Count):
+            f=rs.Fields(i)
+            
+            display_size = None            
+            if not(self.rs.EOF or self.rs.BOF):
+                #TODO: Is this the correct defintion according to the DB API 2 Spec ?
+                display_size = f.ActualSize
+                
+            null_ok = bool(f.Attributes & adFldMayBeNull)
+            
+            self.description.append((f.Name, f.Type, display_size, f.DefinedSize, f.Precision, f.NumericScale, null_ok))
 
     def close(self):
         """Close the cursor now (rather than whenever __del__ is called).
             The cursor will be unusable from this point forward; an Error (or subclass)
             exception will be raised if any operation is attempted with the cursor.
         """
-        self.messages=[]
-        self.conn = None    #this will make all future method calls on me throw an exception
-        if self.rs and self.rs.State != adStateClosed: # rs exists and is open      #v2.1 Rose
-            self.rs.Close()                                                         #v2.1 Rose
-            self.rs = None  #let go of the recordset os ADO will let it be disposed #v2.1 Rose
+        self.messages = []
+        self.conn = None
+        if self.rs and self.rs.State != adStateClosed:
+            self.rs.Close()
+            self.rs = None
 
-# ------------------------
-# a note about Strategies:
-# Ekelund struggled to find ways to keep ADO happy with passed parameters.
-# He finally decided to try four "strategies" of parameter passing:
-# 1. Let COM define the parameter list & pass date-times in COMdate format
-# 2. Build a parameter list with python & pass date-times in COMdate format
-# 3. Let COM define the parameter list & pass date-times as ISO date strings.
-# 4. Build a parameter list with python & pass date-times as ISO date strings.
-# For each "execute" call, he would try each way, then pass all four sets of error
-#  messages back if all four attempts failed.
-# My optimization is as follows:
-#  1. Try to use the COM parameter list.
-#  2. If the Refresh() call blows up, then build a perameter list
-#  3. If the python data is a date but the ADO is a string,
-#      or we built the parameter list,
-#     then pass date-times as ISO dates,
-#     otherwise, convert to a COM date.
-# -- Vernon Cole v2.1
-# (code in executeHelper freely reformatted by me)
-#---------------------
     def _executeHelper(self,operation,isStoredProcedureCall,parameters=None):
-        if self.conn == None:
+        if self.conn is None:
             self._raiseCursorError(Error,None)
             return
-        returnValueIndex=None
 
         if verbose > 1 and parameters:
             print 'adodbapi parameters=',repr(parameters)
 
-        parmIndx=-1
+        parmIndx = -1
+        convertedAllParameters = False
+        
         try:
             self.cmd=win32com.client.Dispatch("ADODB.Command")
-            self.cmd.ActiveConnection=self.conn.adoConn
-            self.cmd.CommandTimeout = self.conn.adoConn.CommandTimeout  #v2.1 Simons
+            self.cmd.ActiveConnection = self.conn.adoConn
+            self.cmd.CommandTimeout = self.conn.adoConn.CommandTimeout
             self.cmd.CommandText=operation
+            
             if isStoredProcedureCall:
-                self.cmd.CommandType=adCmdStoredProc
+                self.cmd.CommandType = adCmdStoredProc
             else:
-                self.cmd.CommandType=adCmdText
+                self.cmd.CommandType = adCmdText
 
-            returnValueIndex=-1            # Initialize to impossible value
-            if parameters != None:
-                #try: # attempt to use ADO's parameter list
-                #    self.cmd.Parameters.Refresh()
-                #    defaultParameterList = False
-                #except: # if it blows up
-                    #-- build own parameter list
-                if verbose:
-                    print 'error in COM Refresh(). adodbapi building default parameter list'
-                defaultParameterList = True # we don't know whether db is expecting dates
+            if parameters is not None:
                 for i,elem in enumerate(parameters):
-                    name='p%i' % i
+                    name = 'p%i' % i
                     adotype = pyTypeToADOType(elem)
-                    p=self.cmd.CreateParameter(name,adotype) # Name, Type, Direction, Size, Value
+                    p = self.cmd.CreateParameter(name,adotype) # Name, Type, Direction, Size, Value
                     if isStoredProcedureCall:
-                        p.Direction=adParamUnknown
+                        p.Direction = adParamUnknown
                     self.cmd.Parameters.Append(p)
+                    
                 if verbose > 2:
-                       for i in range(self.cmd.Parameters.Count):
-                            P = self.cmd.Parameters(i)
-                            print 'adodbapi parameter attributes before=', P.Name, P.Type, P.Direction, P.Size
-                if isStoredProcedureCall:
-                    cnt=self.cmd.Parameters.Count
-                    if cnt<>len(parameters):
-                        for i in range(cnt):
-                            if self.cmd.Parameters(i).Direction == adParamReturnValue:
-                                returnValueIndex=i
-                                break
+                   _log_parameters(self.cmd.Parameters)
+                   
+                returnValueIndex = _findReturnValueIndex(isStoredProcedureCall, self.cmd.Parameters)            
+                
                 for elem in parameters:
                     parmIndx+=1
+                    
+                    # skip over the return value parameter (adParamReturnValue) if any
                     if parmIndx == returnValueIndex:
                         parmIndx+=1
-                    p=self.cmd.Parameters(parmIndx)
-                    if p.Direction in [adParamInput,adParamInputOutput,adParamUnknown]:
-                        tp = type(elem)
-                        if tp in dateconverter.types:
-                            if not defaultParameterList and p.Type in adoDateTimeTypes:
-                                p.Value=dateconverter.COMDate(elem)
-                            else: #probably a string
-                    #Known problem with JET Provider. Date can not be specified as a COM date.
-                    # See for example..http://support.microsoft.com/default.aspx?scid=kb%3ben-us%3b284843
-                    # One workaround is to provide the date as a string in the format 'YYYY-MM-dd'
-                                s = dateconverter.DateObjectToIsoFormatString(elem)
-                                p.Value = s
-                                p.Size = len(s)
-                        elif tp in types.StringTypes:                  #v2.1 Jevon
-                            if defaultParameterList:
-                                p.Value = elem
-                                L = len(elem)
-                            else:
-                                L = min(len(elem),p.Size) #v2.1 Cole limit data to defined size
-                                p.Value = elem[:L]       #v2.1 Jevon & v2.1 Cole
-                            if L>0:   #v2.1 Cole something does not like p.Size as Zero
-                                p.Size = L           #v2.1 Jevon
-                        elif tp == types.BufferType: #v2.1 Cole -- ADO BINARY
-                            p.Size = len(elem)
-                            p.AppendChunk(elem)
-                        else:
-                            p.Value=elem
-                        if verbose > 2:
-                            print 'Parameter %d type %s stored as: %s' % (parmIndx,adTypeNames.get(p.Type, 'unknown'),repr(p.Value))
-            parmIndx = -2 # kludge to prevent exception handler picking out one parameter
+                        
+                    p = self.cmd.Parameters(parmIndx)
+                    if p.Direction not in [adParamInput, adParamInputOutput, adParamUnknown]:
+                    	continue
 
-            # ----- the actual SQL is executed here ---
-            adoRetVal=self.cmd.Execute()
-            # ----- ------------------------------- ---
+                    python_type = type(elem)
+                    if python_type in _python_datetime_types:
+                        #Known problem with JET Provider. Date can not be specified as a COM date.
+                        # See for example:
+                        # http://support.microsoft.com/default.aspx?scid=kb%3ben-us%3b284843
+                        # One workaround is to provide the date as a string in the format 'YYYY-MM-dd'
+                        s = elem.isoformat()
+                        p.Value = s
+                        p.Size = len(s)
+                        
+                    elif python_type in types.StringTypes:
+                        p.Value = elem
+                        try: # ... only if parameter is a datetime string
+                            p.Value=rx_datetime.findall(p.Value)[0]
+                        except:
+                            pass
+                        L = len(elem)
+                        
+                        #v2.1 Cole something does not like p.Size as Zero
+                        if L>0:
+                            p.Size = L
+                            
+                    elif python_type == types.BufferType:
+                        p.Size = len(elem)
+                        p.AppendChunk(elem)
+                        
+                    else: p.Value = elem
+
+                    if verbose > 2:
+                        print 'Parameter %d type %s stored as: %s' % (parmIndx,adTypeNames.get(p.Type, 'unknown'),repr(p.Value))
+
+            convertedAllParameters = True
+
+            # Execute the SQL
+            adoRetVal = self.cmd.Execute()
+
         except (Exception), e:
             tbk = u'\n--ADODBAPI\n'
-            if parmIndx >= 0:
+            if 0 <= parmIndx and not convertedAllParameters:
                 tbk += u'-- Trying parameter %d, %s: %s\n' \
                     %(parmIndx, adTypeNames.get(self.cmd.Parameters(parmIndx).Type, 'unknown'), repr(parameters[parmIndx]))
+            
             tblist=(traceback.format_exception(sys.exc_type,
                                               sys.exc_value,
                                               sys.exc_traceback,
             self._raiseCursorError(DatabaseError,tracebackhistory)
             return
 
-        rs=adoRetVal[0]
-        ##self.rowcount=adoRetVal[1]    # NOTE: usually will be -1 for SELECT  :-(
-        recs=adoRetVal[1]   #[begin Jorgensen ] ## but it breaks unit test
-        ## skip over empty record sets to get to one that isn't empty
-        ##while rs and rs.State == adStateClosed:
-        ##    try:
-        ##        rs, recs = rs.NextRecordset()
-        ##    except:
-        ##        break
-        self.rowcount=recs  #[end Jorgensen]
-
-        self._makeDescriptionFromRS(rs)
+        self.rowcount=adoRetVal[1]  # May be -1 if NOCOUNT is set.
+        self._makeDescriptionFromRS(adoRetVal[0])
 
         if isStoredProcedureCall and parameters != None:
             return self._returnADOCommandParameters(self.cmd)
 
     def execute(self, operation, parameters=None):
-        """Prepare and execute a database operation (query or command).
-
-            Parameters may be provided as sequence or mapping and will be bound to variables in the operation.
-            Variables are specified in a database-specific notation
-            (see the module's paramstyle attribute for details). [5]
-            A reference to the operation will be retained by the cursor.
-            If the same operation object is passed in again, then the cursor
-            can optimize its behavior. This is most effective for algorithms
-            where the same operation is used, but different parameters are bound to it (many times).
-
-            For maximum efficiency when reusing an operation, it is best to use
-            the setinputsizes() method to specify the parameter types and sizes ahead of time.
-            It is legal for a parameter to not match the predefined information;
-            the implementation should compensate, possibly with a loss of efficiency.
-
-            The parameters may also be specified as list of tuples to e.g. insert multiple rows in
-            a single operation, but this kind of usage is depreciated: executemany() should be used instead.
-
-            Return values are not defined.
-
-            [5] The module will use the __getitem__ method of the parameters object to map either positions
-            (integers) or names (strings) to parameter values. This allows for both sequences and mappings
-            to be used as input.
-            The term "bound" refers to the process of binding an input value to a database execution buffer.
-            In practical terms, this means that the input value is directly used as a value in the operation.
-            The client should not be required to "escape" the value so that it can be used -- the value
-            should be equal to the actual database value.
-        """
-        self.messages=[]
+        """Prepare and execute a database operation (query or command)."""
+        self.messages = []
         self._executeHelper(operation,False,parameters)
 
     def executemany(self, operation, seq_of_parameters):
         """Prepare a database operation (query or command) and then execute it against all parameter sequences or mappings found in the sequence seq_of_parameters.
 
-            Return values are not defined.
+        Return values are not defined.
         """
-        self.messages=[]
-        totrecordcount = 0
+        self.messages = []
+        total_recordcount = 0
         canCount=True
         for params in seq_of_parameters:
             self.execute(operation, params)
             if self.rowcount == -1:
-                canCount=False
+                canCount = False
             elif canCount:
-                totrecordcount+=self.rowcount
+                total_recordcount += self.rowcount
 
         if canCount:
-            self.rowcount=totrecordcount
+            self.rowcount = total_recordcount
         else:
-            self.rowcount=-1
+            self.rowcount = -1
 
     def _fetch(self, rows=None):
         """ Fetch rows from the recordset.
         rows is None gets all (for fetchall).
         """
-        rs=self.rs
-        if self.conn == None:
+        if (self.conn is None) or (self.rs is None):
             self._raiseCursorError(Error,None)
             return
-        if rs == None:
-            self._raiseCursorError(Error,None)
-            return
+            
+        if self.rs.State == adStateClosed or self.rs.BOF or self.rs.EOF:
+            if rows == 1: return None # fetchone can return None
+            else: return [] # fetchall and fetchmany return empty lists
+
+        if rows:
+            ado_results = self.rs.GetRows(rows)
         else:
-            if rs.State == adStateClosed or rs.BOF or rs.EOF:
-                if rows == 1: return None # fetchone can return None
-                else: return [] # fetchall and fetchmany return empty lists
+            ado_results = self.rs.GetRows()
 
-            if rows:
-                ado_results = self.rs.GetRows(rows)
-            else:
-                ado_results = self.rs.GetRows()
+        returnList=[]
+        for i,descTuple in enumerate(self.description):
+            # Desctuple =(name, type_code, display_size, internal_size, precision, scale, null_ok).
+            type_code = descTuple[1]
+            returnList.append([convertVariantToPython(r,type_code) for r in ado_results[i]])
 
-            d=self.description
-            returnList=[]
-            i=0
-            for descTuple in d:
-                # Desctuple =(name, type_code, display_size, internal_size, precision, scale, null_ok).
-                type_code=descTuple[1]
-                returnList.append([convertVariantToPython(r,type_code) for r in ado_results[i]])
-                i+=1
-            return tuple(zip(*returnList))
-
+        return tuple(zip(*returnList))
 
     def fetchone(self):
         """ Fetch the next row of a query result set, returning a single sequence,
             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=[]
+        self.messages = []
         ret = self._fetch(1)
         if ret: # return record (not list of records)
             return ret[0]
-        else:
-            return ret # (in case of None)
-
+        
+        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.
         If the size parameter is used, then it is best for it to retain the same value from
         one fetchmany() call to the next.
         """
-        self.messages=[]
-        if size == None:
+        self.messages = []
+        if size is None:
             size = self.arraysize
         return self._fetch(size)
 
             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=[]
+        self.messages = []
         return self._fetch()
 
     def nextset(self):
             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=[]
-        if self.conn == None:
+        self.messages = []
+        if (self.conn is None) or (self.rs is None):
             self._raiseCursorError(Error,None)
-            return
-        if self.rs == None:
-            self._raiseCursorError(Error,None)
-            return
-        else:
-            try:                                                   #[begin 2.1 ekelund]
-                rsTuple=self.rs.NextRecordset()                    #
-            except pywintypes.com_error, exc:                      # return appropriate error
-                self._raiseCursorError(NotSupportedError, exc.args)#[end 2.1 ekelund]
-            rs=rsTuple[0]
-            if rs==None:
-                return None
-            self._makeDescriptionFromRS(rs)
-            return 1
+            return None
+        
+        try:
+            rsTuple = self.rs.NextRecordset()
+        except pywintypes.com_error, exc:
+            self._raiseCursorError(NotSupportedError, exc.args)
+
+        rs = rsTuple[0]
+        if rs is None:
+            return None
+            
+        self._makeDescriptionFromRS(rs)
+        return True
 
     def setinputsizes(self,sizes):
         pass
     def setoutputsize(self, size, column=None):
         pass
 
+
 #Type Objects and Constructors
 #Many databases need to have the input in a particular format for binding to an operation's input parameters.
 #For example, if an input is destined for a DATE column, then it must be bound to the database in a particular
 #see the Implementation Hints below for details).
 
 def Date(year,month,day):
-    "This function constructs an object holding a date value. "
-    return dateconverter.Date(year,month,day)
+    "This function constructs an object holding a date value."
+    return datetime.date(year,month,day)
 
 def Time(hour,minute,second):
-    "This function constructs an object holding a time value. "
-    return dateconverter.Time(hour,minute,second)
+    "This function constructs an object holding a time value."
+    return datetime.time(hour,minute,second)
 
 def Timestamp(year,month,day,hour,minute,second):
-    "This function constructs an object holding a time stamp value. "
-    return dateconverter.Timestamp(year,month,day,hour,minute,second)
+    "This function constructs an object holding a time stamp value."
+    return datetime.datetime(year,month,day,hour,minute,second)
 
 def DateFromTicks(ticks):
     """This function constructs an object holding a date value from the given ticks value
     (number of seconds since the epoch; see the documentation of the standard Python time module for details). """
-    return apply(Date,time.localtime(ticks)[:3])
-
+    return datetime.date(*time.localtime(ticks)[:3])
 
 def TimeFromTicks(ticks):
     """This function constructs an object holding a time value from the given ticks value
     (number of seconds since the epoch; see the documentation of the standard Python time module for details). """
-    return apply(Time,time.localtime(ticks)[3:6])
+    return datetime.time(*time.localtime(ticks)[3:6])
 
 def TimestampFromTicks(ticks):
     """This function constructs an object holding a time stamp value from the given
     ticks value (number of seconds since the epoch;
     see the documentation of the standard Python time module for details). """
-    return apply(Timestamp,time.localtime(ticks)[:6])
+    return datetime.datetime(*time.localtime(ticks)[:6])
 
 def Binary(aString):
     """This function constructs an object capable of holding a binary (long) string value. """
     return buffer(aString)
 
-#v2.1 Cole comment out: BinaryType = Binary('a')
 
-#SQL NULL values are represented by the Python None singleton on input and output.
-
-#Note: Usage of Unix ticks for database interfacing can cause troubles because of the limited date range they cover.
-
-def pyTypeToADOType(d):
-    tp=type(d)
+def pyTypeToADOType(data):
+    python_type = type(data)
     try:
-        return typeMap[tp]
+        return typeMap[python_type]
     except KeyError:
-        if tp in dateconverter.types:
+        if python_type in _python_datetime_types:
             return adDate
     raise DataError
 
     if lo < 0:
         lo += (2L ** 32)
     # was: return round((float((long(hi) << 32) + lo))/10000.0,decimal)
-    return decimal.Decimal((long(hi) << 32) + lo)/decimal.Decimal(1000) #v2.1 Cole
+    return decimal.Decimal((long(hi) << 32) + lo)/decimal.Decimal(1000)
 
-def cvtNumeric(variant):     #all v2.1 Cole
+def cvtNumeric(variant):
     try:
         return decimal.Decimal(variant)
     except (ValueError,TypeError):
         return float(variant)
     except (ValueError,TypeError):
         pass
-#    if type(variant)==types.TupleType: #v2.1 Cole (should be unnecessary now)
-#        return cvtCurrency(variant)
+
     try:
-        europeVsUS=str(variant).replace(",",".")   #v2.1 Cole
+        europeVsUS = str(variant).replace(",",".")
         return float(europeVsUS)
     except (ValueError,TypeError):
         pass
 
+def convertVariantToPython(variant, adType):
+    if variant is None:
+        return None
+
+    # Don't convert booleans to 1/0 ints.	
+    if type(variant) == bool and adType == adBoolean:
+        return variant
+
+    res = variantConversions[adType](variant)
+    
+    # If the type is float, but there is no decimal part,
+    # then return an integer. 
+    # --Adam V: Why does Django want this?
+    if type(res) == float and str(res)[-2:] == ".0":
+        return int(res) # If float but int, then int.
+
+    return res
+
+
+class DBAPITypeObject:
+  def __init__(self,valuesTuple):
+    self.values = valuesTuple
+
+  def __cmp__(self,other):
+    if other in self.values:
+      return 0
+    if other < self.values:
+      return 1
+    else:
+      return -1
+
+adoIntegerTypes=(adInteger,adSmallInt,adTinyInt,adUnsignedInt,
+                 adUnsignedSmallInt,adUnsignedTinyInt,
+                 adBoolean,adError)
+adoRowIdTypes=(adChapter,)
+adoLongTypes=(adBigInt,adFileTime,adUnsignedBigInt)
+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)
+adoRemainingTypes=(adEmpty,adIDispatch,adIUnknown,
+                   adPropVariant,adArray,adUserDefined,
+                   adVariant)
+
+"""This type object is used to describe columns in a database that are string-based (e.g. CHAR). """
+STRING   = DBAPITypeObject(adoStringTypes)
+
+"""This type object is used to describe (long) binary columns in a database (e.g. LONG, RAW, BLOBs). """
+BINARY   = DBAPITypeObject(adoBinaryTypes)
+
+"""This type object is used to describe numeric columns in a database. """
+NUMBER   = DBAPITypeObject(adoIntegerTypes + adoLongTypes + \
+                           adoExactNumericTypes + adoApproximateNumericTypes)
+
+"""This type object is used to describe date/time columns in a database. """
+DATETIME = DBAPITypeObject(adoDateTimeTypes)
+
+"""This type object is used to describe the "Row ID" column in a database. """
+ROWID    = DBAPITypeObject(adoRowIdTypes)
+
+typeMap= {
+	types.BufferType: adBinary,
+	types.FloatType: adNumeric,
+	types.IntType: adInteger,
+	types.LongType: adBigInt,
+	types.StringType: adBSTR,
+	types.NoneType: adEmpty,
+	types.UnicodeType: adBSTR,
+	types.BooleanType:adBoolean,
+	type(decimal.Decimal): adNumeric,
+}
+
+# Used for COM to Python date conversions.
+_ordinal_1899_12_31 = datetime.date(1899,12,31).toordinal()-1
+
+def datetimeFromCOMDate(comDate):
+	fComDate = float(comDate)
+	integerPart = int(fComDate)
+	fraction_of_day = abs(fComDate-integerPart)
+	
+	datetime_value = datetime.datetime.fromordinal(integerPart + _ordinal_1899_12_31)
+	# 86400000 = 24*60*60*1000 = milliseconds per day
+	datetime_value = datetime_value + datetime.timedelta(milliseconds=fraction_of_day*86400000)
+	return datetime_value
+
 def identity(x): return x
 
 class VariantConversionMap(dict):
         except KeyError:
             return identity
 
-def convertVariantToPython(variant, adType):
-    if variant is None:
-        return None
-
-    # Don't convert booleans to 1/0 ints.	
-    if type(variant) == bool and adType == adBoolean:
-        return variant  # bool not 1/0
-
-    res = variantConversions[adType](variant)
-    
-    # If the type is flaot, but there is no decimal part,
-    # then return an integer. 
-    # --Adam V: Why does Django want this?
-    if type(res) == float and str(res)[-2:] == ".0":
-        return int(res) # If float but int, then int.
-
-    return res
-
-adCmdText = 1
-adCmdStoredProc = 4
-
-adParamInput                  =0x1        # from enum ParameterDirectionEnum
-adParamInputOutput            =0x3        # from enum ParameterDirectionEnum
-adParamOutput                 =0x2        # from enum ParameterDirectionEnum
-adParamReturnValue            =0x4        # from enum ParameterDirectionEnum
-adParamUnknown                =0x0        # from enum ParameterDirectionEnum
-
-
-adStateClosed =0
-
-adFldMayBeNull=0x40
-
-adModeShareExclusive=0xc
-
-adXactCommitRetaining= 131072
-adXactAbortRetaining = 262144
-
-
-adArray                       =0x2000     # from enum DataTypeEnum
-adBSTR                        =0x8        # from enum DataTypeEnum
-adBigInt                      =0x14       # from enum DataTypeEnum
-adBinary                      =0x80       # from enum DataTypeEnum
-adBoolean                     =0xb        # from enum DataTypeEnum
-adChapter                     =0x88       # from enum DataTypeEnum
-adChar                        =0x81       # from enum DataTypeEnum
-adCurrency                    =0x6        # from enum DataTypeEnum
-adDBDate                      =0x85       # from enum DataTypeEnum
-adDBTime                      =0x86       # from enum DataTypeEnum
-adDBTimeStamp                 =0x87       # from enum DataTypeEnum
-adDate                        =0x7        # from enum DataTypeEnum
-adDecimal                     =0xe        # from enum DataTypeEnum
-adDouble                      =0x5        # from enum DataTypeEnum
-adEmpty                       =0x0        # from enum DataTypeEnum
-adError                       =0xa        # from enum DataTypeEnum
-adFileTime                    =0x40       # from enum DataTypeEnum
-adGUID                        =0x48       # from enum DataTypeEnum
-adIDispatch                   =0x9        # from enum DataTypeEnum
-adIUnknown                    =0xd        # from enum DataTypeEnum
-adInteger                     =0x3        # from enum DataTypeEnum
-adLongVarBinary               =0xcd       # from enum DataTypeEnum
-adLongVarChar                 =0xc9       # from enum DataTypeEnum
-adLongVarWChar                =0xcb       # from enum DataTypeEnum
-adNumeric                     =0x83       # from enum DataTypeEnum
-adPropVariant                 =0x8a       # from enum DataTypeEnum
-adSingle                      =0x4        # from enum DataTypeEnum
-adSmallInt                    =0x2        # from enum DataTypeEnum
-adTinyInt                     =0x10       # from enum DataTypeEnum
-adUnsignedBigInt              =0x15       # from enum DataTypeEnum
-adUnsignedInt                 =0x13       # from enum DataTypeEnum
-adUnsignedSmallInt            =0x12       # from enum DataTypeEnum
-adUnsignedTinyInt             =0x11       # from enum DataTypeEnum
-adUserDefined                 =0x84       # from enum DataTypeEnum
-adVarBinary                   =0xcc       # from enum DataTypeEnum
-adVarChar                     =0xc8       # from enum DataTypeEnum
-adVarNumeric                  =0x8b       # from enum DataTypeEnum
-adVarWChar                    =0xca       # from enum DataTypeEnum
-adVariant                     =0xc        # from enum DataTypeEnum
-adWChar                       =0x82       # from enum DataTypeEnum
-
-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',
-   }
-
-#ado DataTypeEnum -- copy of above, here in decimal, sorted by value
-#adEmpty 0 Specifies no value (DBTYPE_EMPTY).
-#adSmallInt 2 Indicates a two-byte signed integer (DBTYPE_I2).
-#adInteger 3 Indicates a four-byte signed integer (DBTYPE_I4).
-#adSingle 4 Indicates a single-precision floating-point value (DBTYPE_R4).
-#adDouble 5 Indicates a double-precision floating-point value (DBTYPE_R8).
-#adCurrency 6 Indicates a currency value (DBTYPE_CY). Currency is a fixed-point number with four digits to the right of the decimal point. It is stored in an eight-byte signed integer scaled by 10,000.
-#adDate 7 Indicates a date value (DBTYPE_DATE). A date is stored as a double, the whole part of which is the number of days since December 30, 1899, and the fractional part of which is the fraction of a day.
-#adBSTR 8 Indicates a null-terminated character string (Unicode) (DBTYPE_BSTR).
-#adIDispatch 9 Indicates a pointer to an IDispatch interface on a COM object (DBTYPE_IDISPATCH).
-#adError 10 Indicates a 32-bit error code (DBTYPE_ERROR).
-#adBoolean 11 Indicates a boolean value (DBTYPE_BOOL).
-#adVariant 12 Indicates an Automation Variant (DBTYPE_VARIANT).
-#adIUnknown 13 Indicates a pointer to an IUnknown interface on a COM object (DBTYPE_IUNKNOWN).
-#adDecimal 14 Indicates an exact numeric value with a fixed precision and scale (DBTYPE_DECIMAL).
-#adTinyInt 16 Indicates a one-byte signed integer (DBTYPE_I1).
-#adUnsignedTinyInt 17 Indicates a one-byte unsigned integer (DBTYPE_UI1).
-#adUnsignedSmallInt 18 Indicates a two-byte unsigned integer (DBTYPE_UI2).
-#adUnsignedInt 19 Indicates a four-byte unsigned integer (DBTYPE_UI4).
-#adBigInt 20 Indicates an eight-byte signed integer (DBTYPE_I8).
-#adUnsignedBigInt 21 Indicates an eight-byte unsigned integer (DBTYPE_UI8).
-#adFileTime 64 Indicates a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601 (DBTYPE_FILETIME).
-#adGUID 72 Indicates a globally unique identifier (GUID) (DBTYPE_GUID).
-#adBinary 128 Indicates a binary value (DBTYPE_BYTES).
-#adChar 129 Indicates a string value (DBTYPE_STR).
-#adWChar 130 Indicates a null-terminated Unicode character string (DBTYPE_WSTR).
-#adNumeric 131 Indicates an exact numeric value with a fixed precision and scale (DBTYPE_NUMERIC). adUserDefined 132 Indicates a user-defined variable (DBTYPE_UDT).
-#adUserDefined 132 Indicates a user-defined variable (DBTYPE_UDT).
-#adDBDate 133 Indicates a date value (yyyymmdd) (DBTYPE_DBDATE).
-#adDBTime 134 Indicates a time value (hhmmss) (DBTYPE_DBTIME).
-#adDBTimeStamp 135 Indicates a date/time stamp (yyyymmddhhmmss plus a fraction in billionths) (DBTYPE_DBTIMESTAMP).
-#adChapter 136 Indicates a four-byte chapter value that identifies rows in a child rowset (DBTYPE_HCHAPTER).
-#adPropVariant 138 Indicates an Automation PROPVARIANT (DBTYPE_PROP_VARIANT).
-#adVarNumeric 139 Indicates a numeric value (Parameter object only).
-#adVarChar 200 Indicates a string value (Parameter object only).
-#adLongVarChar 201 Indicates a long string value (Parameter object only).
-#adVarWChar 202 Indicates a null-terminated Unicode character string (Parameter object only).
-#adLongVarWChar 203 Indicates a long null-terminated Unicode string value (Parameter object only).
-#adVarBinary 204 Indicates a binary value (Parameter object only).
-#adLongVarBinary 205 Indicates a long binary value (Parameter object only).
-#adArray (Does not apply to ADOX.) 0x2000 A flag value, always combined with another data type constant, that indicates an array of that other data type.
-
-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                '
-    }
-
-
-class DBAPITypeObject:
-  def __init__(self,valuesTuple):
-    self.values = valuesTuple
-
-  def __cmp__(self,other):
-    if other in self.values:
-      return 0
-    if other < self.values:
-      return 1
-    else:
-      return -1
-
-adoIntegerTypes=(adInteger,adSmallInt,adTinyInt,adUnsignedInt,
-                 adUnsignedSmallInt,adUnsignedTinyInt,
-                 adBoolean,adError) #max 32 bits
-adoRowIdTypes=(adChapter,)          #v2.1 Rose
-adoLongTypes=(adBigInt,adFileTime,adUnsignedBigInt)
-adoExactNumericTypes=(adDecimal,adNumeric,adVarNumeric,adCurrency)      #v2.1 Cole
-adoApproximateNumericTypes=(adDouble,adSingle)                          #v2.1 Cole
-adoStringTypes=(adBSTR,adChar,adLongVarChar,adLongVarWChar,
-                adVarChar,adVarWChar,adWChar,adGUID)
-adoBinaryTypes=(adBinary,adLongVarBinary,adVarBinary)
-adoDateTimeTypes=(adDBTime, adDBTimeStamp, adDate, adDBDate)
-adoRemainingTypes=(adEmpty,adIDispatch,adIUnknown,
-                   adPropVariant,adArray,adUserDefined,
-                   adVariant)
-
-"""This type object is used to describe columns in a database that are string-based (e.g. CHAR). """
-STRING   = DBAPITypeObject(adoStringTypes)
-
-"""This type object is used to describe (long) binary columns in a database (e.g. LONG, RAW, BLOBs). """
-BINARY   = DBAPITypeObject(adoBinaryTypes)
-
-"""This type object is used to describe numeric columns in a database. """
-NUMBER   = DBAPITypeObject(adoIntegerTypes + adoLongTypes + \
-                           adoExactNumericTypes + adoApproximateNumericTypes)
-
-"""This type object is used to describe date/time columns in a database. """
-
-DATETIME = DBAPITypeObject(adoDateTimeTypes)
-"""This type object is used to describe the "Row ID" column in a database. """
-ROWID    = DBAPITypeObject(adoRowIdTypes)
-
-typeMap= {
-           types.BufferType: adBinary,
-           types.FloatType: adNumeric,
-           types.IntType: adInteger,
-           types.LongType: adBigInt,
-           types.StringType: adBSTR,
-           types.NoneType: adEmpty,
-           types.UnicodeType: adBSTR,
-           types.BooleanType:adBoolean,          #v2.1 Cole
-           type(decimal.Decimal): adNumeric       #v2.1 Cole
-           }
-
-dateconverter=pythonDateTimeConverter()
-
-# variant type : function converting variant to Python value
-
-def variantConvertDate(v):
-    return dateconverter.DateObjectFromCOMDate(v)
-
-variantConversions = VariantConversionMap( {
-    adoDateTimeTypes : variantConvertDate,
+variantConversions = VariantConversionMap({
+    adoDateTimeTypes : datetimeFromCOMDate,
     adoApproximateNumericTypes: cvtFloat,
     adCurrency: cvtCurrency,
     adoExactNumericTypes: identity, # use cvtNumeric to force decimal rather than unicode

src/sqlserver_ado/base.py

 class DatabaseFeatures(BaseDatabaseFeatures):
     supports_tablespaces = True
 
+
 class DatabaseOperations(BaseDatabaseOperations):
     def date_extract_sql(self, lookup_type, field_name):
         return "DATEPART(%s, %s)" % (lookup_type, field_name)

src/sqlserver_ado/creation.py

     'BooleanField':      'bit',
     'CharField':         'varchar(%(max_length)s)',
     'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
-    'DateField':         'smalldatetime',
-    'DateTimeField':     'smalldatetime',
+    'DateField':         'datetime',
+    'DateTimeField':     'datetime',
     'DecimalField':      'numeric(%(max_digits)s, %(decimal_places)s)',
     'FileField':         'varchar(%(max_length)s)',
     'FilePathField':     'varchar(%(max_length)s)',
     'PositiveSmallIntegerField': 'smallint CONSTRAINT [CK_smallint_pos_%(column)s] CHECK ([%(column)s] > 0)',
     'SlugField':         'varchar(%(max_length)s)',
     'SmallIntegerField': 'smallint',
-    'TextField':         'text',
+    'TextField':         'varchar(max)', # SQL Server 2005 and up!
     'TimeField':         'time',
-    'USStateField':      'varchar(2)',
+    'USStateField':      'char(2)',
 }

src/sqlserver_ado/introspection.py

+import ado_consts
+
 def get_table_list(cursor):
     "Returns a list of table names in the current database."
     cursor.execute("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE'")
 
 def get_table_description(cursor, table_name):
     "Returns a description of the table, with the DB-API cursor.description interface."
+    # % in the table name because you can't pass table names as parameters (just where values)
     cursor.execute("SELECT * FROM [%s] where 1=0" % (table_name))
     return cursor.description
 
 def get_relations(cursor, table_name):
     source_field_dict = _name_to_index(cursor, table_name)
     
-    sql = '''
+    sql = """
 SELECT
 COLUMN_NAME = FK_COLS.COLUMN_NAME,
 REFERENCED_TABLE_NAME = PK.TABLE_NAME,
 JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE PK_COLS 
 	ON PK.CONSTRAINT_NAME = PK_COLS.CONSTRAINT_NAME
 where
-	FK.TABLE_NAME = %s'''
+	FK.TABLE_NAME = %s"""
     cursor.execute(sql,[table_name])
     relations = cursor.fetchall()
     relation_map = dict()
 #    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 = '''
+    sql = """
 select
 	C.name as [column_name],
 	IX.is_unique as [unique], 
 			and cols.index_id = IC.index_id
 			and cols.key_ordinal > 1
 	)
-'''
+"""
     
     cursor.execute(sql,[table_name])
     constraints = cursor.fetchall()
     return indexes
     
 
-    # Copied out of adodbapi for reference:
-adArray                       =0x2000     # from enum DataTypeEnum
-adBSTR                        =0x8        # from enum DataTypeEnum
-adBigInt                      =0x14       # from enum DataTypeEnum
-adBinary                      =0x80       # from enum DataTypeEnum
-adBoolean                     =0xb        # from enum DataTypeEnum
-adChapter                     =0x88       # from enum DataTypeEnum
-adChar                        =0x81       # from enum DataTypeEnum
-adCurrency                    =0x6        # from enum DataTypeEnum
-adDBDate                      =0x85       # from enum DataTypeEnum
-adDBTime                      =0x86       # from enum DataTypeEnum
-adDBTimeStamp                 =0x87       # from enum DataTypeEnum
-adDate                        =0x7        # from enum DataTypeEnum
-adDecimal                     =0xe        # from enum DataTypeEnum
-adDouble                      =0x5        # from enum DataTypeEnum
-adEmpty                       =0x0        # from enum DataTypeEnum
-adError                       =0xa        # from enum DataTypeEnum
-adFileTime                    =0x40       # from enum DataTypeEnum
-adGUID                        =0x48       # from enum DataTypeEnum
-adIDispatch                   =0x9        # from enum DataTypeEnum
-adIUnknown                    =0xd        # from enum DataTypeEnum
-adInteger                     =0x3        # from enum DataTypeEnum
-adLongVarBinary               =0xcd       # from enum DataTypeEnum
-adLongVarChar                 =0xc9       # from enum DataTypeEnum
-adLongVarWChar                =0xcb       # from enum DataTypeEnum
-adNumeric                     =0x83       # from enum DataTypeEnum
-adPropVariant                 =0x8a       # from enum DataTypeEnum
-adSingle                      =0x4        # from enum DataTypeEnum
-adSmallInt                    =0x2        # from enum DataTypeEnum
-adTinyInt                     =0x10       # from enum DataTypeEnum
-adUnsignedBigInt              =0x15       # from enum DataTypeEnum
-adUnsignedInt                 =0x13       # from enum DataTypeEnum
-adUnsignedSmallInt            =0x12       # from enum DataTypeEnum
-adUnsignedTinyInt             =0x11       # from enum DataTypeEnum
-adUserDefined                 =0x84       # from enum DataTypeEnum
-adVarBinary                   =0xcc       # from enum DataTypeEnum
-adVarChar                     =0xc8       # from enum DataTypeEnum
-adVarNumeric                  =0x8b       # from enum DataTypeEnum
-adVarWChar                    =0xca       # from enum DataTypeEnum
-adVariant                     =0xc        # from enum DataTypeEnum
-adWChar                       =0x82       # from enum DataTypeEnum
-
-
 DATA_TYPES_REVERSE = {
-    adBoolean: 'BooleanField',
-    adChar: 'CharField',
-    adWChar: 'CharField',
-    adDecimal: 'DecimalField',
-    adNumeric: 'DecimalField',
-    adDBTimeStamp: 'DateTimeField',
-    adDouble: 'FloatField',
-    adSingle: 'FloatField',
-    adInteger: 'IntegerField',
-    adBigInt: 'IntegerField',
-    adSmallInt: 'IntegerField',
-    adTinyInt: 'IntegerField',
-    adVarChar: 'CharField',
-    adVarWChar: 'CharField',
-    adLongVarWChar: 'TextField',
-    adLongVarChar: 'TextField',
+    ado_consts.adBoolean: 'BooleanField',
+    ado_consts.adChar: 'CharField',
+    ado_consts.adWChar: 'CharField',
+    ado_consts.adDecimal: 'DecimalField',
+    ado_consts.adNumeric: 'DecimalField',
+    ado_consts.adDBTimeStamp: 'DateTimeField',
+    ado_consts.adDouble: 'FloatField',
+    ado_consts.adSingle: 'FloatField',
+    ado_consts.adInteger: 'IntegerField',
+    ado_consts.adBigInt: 'IntegerField',
+    ado_consts.adSmallInt: 'IntegerField',
+    ado_consts.adTinyInt: 'IntegerField',
+    ado_consts.adVarChar: 'CharField',
+    ado_consts.adVarWChar: 'CharField',
+    ado_consts.adLongVarWChar: 'TextField',
+    ado_consts.adLongVarChar: 'TextField',
 }

tests/create_empty_database.sql

+IF  EXISTS (SELECT name FROM sys.databases WHERE name = N'django_test_backend')
+DROP DATABASE [django_test_backend]
+go
+
+create database [django_test_backend]
+go

tests/test_bug_008/__init__.py

Empty file added.

tests/test_bug_008/manage.py

+#!/usr/bin/env python
+from django.core.management import execute_manager
+try:
+    import settings # Assumed to be in the same directory.
+except ImportError:
+    import sys
+    sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
+    sys.exit(1)
+
+if __name__ == "__main__":
+    execute_manager(settings)

tests/test_bug_008/myapp/__init__.py

Empty file added.

tests/test_bug_008/myapp/models.py

+from django.db import models
+
+class Choice(models.Model):
+	choice = models.TextField(max_length=200)
+	longchoice =  models.TextField()
+	votes = models.IntegerField()

tests/test_bug_008/settings.py

+# Django settings for testbackend project.
+def hack_backend_path():
+	import os, sys
+	backend_path = os.path.join(os.path.abspath(os.path.dirname(".")), "../../src")
+	sys.path.append(backend_path)
+	
+hack_backend_path()
+
+
+DEBUG = True
+TEMPLATE_DEBUG = DEBUG
+
+ADMINS = (
+    # ('Your Name', 'your_email@domain.com'),
+)
+
+MANAGERS = ADMINS
+
+DATABASE_ENGINE = 'sqlserver_ado'
+DATABASE_MSSQL_REGEX = True
+
+DATABASE_HOST = r'localhost\ss2005'
+DATABASE_PORT = ''
+DATABASE_NAME = r'django_test_backend'
+
+# Use integrated auth.
+DATABASE_USER = ''
+DATABASE_PASSWORD = ''
+
+
+# Local time zone for this installation. Choices can be found here:
+# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
+# although not all choices may be available on all operating systems.
+# If running in a Windows environment this must be set to the same as your
+# system time zone.
+TIME_ZONE = 'America/New York'
+
+# Language code for this installation. All choices can be found here:
+# http://www.i18nguy.com/unicode/language-identifiers.html
+LANGUAGE_CODE = 'en-us'
+
+SITE_ID = 1
+
+# If you set this to False, Django will make some optimizations so as not
+# to load the internationalization machinery.
+USE_I18N = True
+
+# Absolute path to the directory that holds media.
+# Example: "/home/media/media.lawrence.com/"
+MEDIA_ROOT = ''
+
+# URL that handles the media served from MEDIA_ROOT. Make sure to use a
+# trailing slash if there is a path component (optional in other cases).
+# Examples: "http://media.lawrence.com", "http://example.com/media/"
+MEDIA_URL = ''
+
+# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a
+# trailing slash.
+# Examples: "http://foo.com/media/", "/media/".
+ADMIN_MEDIA_PREFIX = '/media/'
+
+# Make this unique, and don't share it with anybody.
+SECRET_KEY = '%(u8uds4xsy0+95cj3o&k49*u@&--yp0t&e&0$!@s2fvea#u4j'
+
+# List of callables that know how to import templates from various sources.
+TEMPLATE_LOADERS = (
+    'django.template.loaders.filesystem.load_template_source',
+    'django.template.loaders.app_directories.load_template_source',
+)
+
+MIDDLEWARE_CLASSES = (
+    'django.middleware.common.CommonMiddleware',
+    'django.contrib.sessions.middleware.SessionMiddleware',
+    'django.contrib.auth.middleware.AuthenticationMiddleware',
+    'django.middleware.doc.XViewMiddleware',
+)
+
+ROOT_URLCONF = 'urls'
+
+TEMPLATE_DIRS = (
+    # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
+    # Always use forward slashes, even on Windows.
+    # Don't forget to use absolute paths, not relative paths.
+)
+
+INSTALLED_APPS = (
+	'django.contrib.admin',
+	'django.contrib.auth',
+	'django.contrib.contenttypes',
+	'django.contrib.sessions',
+	'django.contrib.sites',
+	'myapp',
+)

tests/test_bug_008/urls.py

+from django.conf.urls.defaults import *
+
+urlpatterns = patterns('',
+    # Example:
+    # (r'^test_models/', include('test_models.foo.urls')),
+
+    # Uncomment this for admin:
+     (r'^admin/', include('django.contrib.admin.urls')),
+)

tests/test_models/__init__.py

Empty file added.

tests/test_models/manage.py

+#!/usr/bin/env python
+from django.core.management import execute_manager
+try:
+    import settings # Assumed to be in the same directory.
+except ImportError: