Index: django/db/models/base.py
===================================================================
--- django/db/models/base.py	(revision 4459)
+++ django/db/models/base.py	(working copy)
@@ -170,7 +170,7 @@
         record_exists = True
         if pk_val is not None:
             # Determine whether a record with the primary key already exists.
-            cursor.execute("SELECT 1 FROM %s WHERE %s=%%s LIMIT 1" % \
+            cursor.execute("SELECT 1 FROM %s WHERE %s=%%s" % 
                 (backend.quote_name(self._meta.db_table), backend.quote_name(self._meta.pk.column)), [pk_val])
             # If it does already exist, do an UPDATE.
             if cursor.fetchone():
Index: django/db/models/fields/__init__.py
===================================================================
--- django/db/models/fields/__init__.py	(revision 4459)
+++ django/db/models/fields/__init__.py	(working copy)
@@ -519,12 +519,15 @@
         if value is not None:
             # MySQL will throw a warning if microseconds are given, because it
             # doesn't support microseconds.
-            if settings.DATABASE_ENGINE == 'mysql' and hasattr(value, 'microsecond'):
+            if (settings.DATABASE_ENGINE == 'mysql' or settings.DATABASE_ENGINE == 'ado_mssql') and hasattr(value, 'microsecond'):
                 value = value.replace(microsecond=0)
             value = str(value)
         return Field.get_db_prep_save(self, value)
 
     def get_db_prep_lookup(self, lookup_type, value):
+        # MSSQL doesn't like microseconds.
+        if settings.DATABASE_ENGINE == 'ado_mssql' and hasattr(value, 'microsecond'):
+            value = value.replace(microsecond=0)
         if lookup_type == 'range':
             value = [str(v) for v in value]
         else:
@@ -803,7 +806,7 @@
         if value is not None:
             # MySQL will throw a warning if microseconds are given, because it
             # doesn't support microseconds.
-            if settings.DATABASE_ENGINE == 'mysql':
+            if settings.DATABASE_ENGINE == 'mysql' or settings.DATABASE_ENGINE == 'ado_mssql':
                 value = value.replace(microsecond=0)
             value = str(value)
         return Field.get_db_prep_save(self, value)
Index: django/db/backends/ado_mssql/base.py
===================================================================
--- django/db/backends/ado_mssql/base.py	(revision 4459)
+++ django/db/backends/ado_mssql/base.py	(working copy)
@@ -3,6 +3,7 @@
 
 Requires adodbapi 2.0.1: http://adodbapi.sourceforge.net/
 """
+import pythoncom
 
 from django.db.backends import util
 try:
@@ -25,7 +26,9 @@
     def executeHelper(self, operation, isStoredProcedureCall, parameters=None):
         if parameters is not None and "%s" in operation:
             operation = operation.replace("%s", "?")
+        pythoncom.CoInitialize()
         Database.Cursor.executeHelper(self, operation, isStoredProcedureCall, parameters)
+        pythoncom.CoUninitialize()
 
 class Connection(Database.Connection):
     def cursor(self):
@@ -69,8 +72,11 @@
                 settings.DATABASE_HOST = "127.0.0.1"
             # TODO: Handle DATABASE_PORT.
             conn_string = "PROVIDER=SQLOLEDB;DATA SOURCE=%s;UID=%s;PWD=%s;DATABASE=%s" % (settings.DATABASE_HOST, settings.DATABASE_USER, settings.DATABASE_PASSWORD, settings.DATABASE_NAME)
+            pythoncom.CoInitialize()
             self.connection = Database.connect(conn_string)
-        cursor = self.connection.cursor()
+            pythoncom.CoUninitialize()
+        
+        cursor = Cursor(self.connection) 
         if settings.DEBUG:
             return util.CursorDebugWrapper(cursor, self)
         return cursor
@@ -117,10 +123,13 @@
 
 def get_limit_offset_sql(limit, offset=None):
     # TODO: This is a guess. Make sure this is correct.
-    sql = "LIMIT %s" % limit
-    if offset and offset != 0:
-        sql += " OFFSET %s" % offset
-    return sql
+    # should be "SELECT TOP %s" % limit 
+    # not LIMIT at the end 
+    return "" 
+    #sql = "LIMIT %s" % limit 
+    #if offset and offset != 0: 
+    #    sql += " OFFSET %s" % offset 
+    #return sql 
 
 def get_random_function_sql():
     return "RAND()"
Index: django/db/backends/ado_mssql/introspection.py
===================================================================
--- django/db/backends/ado_mssql/introspection.py	(revision 4459)
+++ django/db/backends/ado_mssql/introspection.py	(working copy)
@@ -1,13 +1,147 @@
+# Tested against MSDE and SQL Server 2000 using adodbapi 2.0.1
+# Python 2.4.2 and 2.4.3 were used during testing.
+from django.db.backends.ado_mssql.base import Cursor
+
 def get_table_list(cursor):
-    raise NotImplementedError
+    "Returns a list of table names in the current database."
+    print "# Note: Any fields that are named 'id', are of type 'AutoField', and"
+    print "# and are Primary Keys will NOT appear in the model output below."
+    print "# By default Django assumes that the each model's Primary Key is an "
+    print "# AutoField with a name of 'id', so there is no need to add it to the" 
+    print "# model description."
+    print
+    cursor.execute("SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE'")
+    return [row[2] for row in cursor.fetchall()]
 
-def get_table_description(cursor, table_name):
-    raise NotImplementedError
+def _is_auto_field(cursor, table_name, column_name):
+    cursor.execute("SELECT COLUMNPROPERTY( OBJECT_ID('%s'),'%s','IsIdentity')" % (table_name, column_name))
+    return cursor.fetchall()[0][0]
 
+def get_table_description(cursor, table_name, identity_check=True):
+    """Returns a description of the table, with the 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 -777, which maps to the 'AutoField' value in the DATA_TYPES_REVERSE dict.
+    """    
+    cursor.execute("SELECT TOP 1 * FROM %s" % table_name)
+    cursor.nextset()
+    items = []
+    if identity_check:
+        for data in cursor.description:
+            if _is_auto_field(cursor, table_name, data[0]):
+                data = list(data)
+                data[1] = -777
+            items.append(list(data))
+    else:
+        items = cursor.description
+    return items
+
+def _name_to_index(cursor, table_name):
+    """
+    Returns 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(get_table_description(cursor, table_name, identity_check=False))])
+
 def get_relations(cursor, table_name):
-    raise NotImplementedError
-
+    """
+    Returns a dictionary of {field_index: (field_index_other_table, other_table)}
+    representing all relationships to the given table. Indexes are 0-based.    
+    """
+    table_index = _name_to_index(cursor, table_name)
+    sql = """SELECT e.COLUMN_NAME AS column_name,
+                    c.TABLE_NAME AS referenced_table_name,
+                    d.COLUMN_NAME AS referenced_column_name
+                    FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS a
+                        INNER JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS AS b
+                              ON a.CONSTRAINT_NAME = b.CONSTRAINT_NAME
+                        INNER JOIN INFORMATION_SCHEMA.CONSTRAINT_TABLE_USAGE AS c
+                              ON b.UNIQUE_CONSTRAINT_NAME = c.CONSTRAINT_NAME
+                        INNER JOIN INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE AS d
+                              ON c.CONSTRAINT_NAME = d.CONSTRAINT_NAME
+                        INNER JOIN INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE AS e
+                              ON a.CONSTRAINT_NAME = e.CONSTRAINT_NAME
+                    WHERE a.TABLE_NAME = ? AND
+                          a.CONSTRAINT_TYPE = 'FOREIGN KEY'"""
+    cursor = Cursor(cursor.db.connection)
+    cursor.execute(sql, (table_name,))
+    return dict([(table_index[item[0]], (_name_to_index(cursor, item[1])[item[2]], item[1]))
+                  for item in cursor.fetchall()])
+    
 def get_indexes(cursor, table_name):
-    raise NotImplementedError
+    """
+    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 b.COLUMN_NAME, a.CONSTRAINT_TYPE
+               FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS a INNER JOIN
+                    INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE AS b
+                    ON a.CONSTRAINT_NAME = b.CONSTRAINT_NAME AND
+                       a.TABLE_NAME = b.TABLE_NAME
+               WHERE a.TABLE_NAME = ? AND
+                     (CONSTRAINT_TYPE = 'PRIMARY KEY' OR
+                      CONSTRAINT_TYPE = 'UNIQUE')"""
+    field_names = [item[0] for item in get_table_description(cursor, table_name, identity_check=False)]
+    cursor = Cursor(cursor.db.connection)
+    cursor.execute(sql, (table_name,))
+    indexes = {}
+    results = {}
+    data = cursor.fetchall()
+    if data:
+        results.update(data)
+    for field in field_names:
+        val = results.get(field, None)
+        indexes[field] = dict(primary_key=(val=='PRIMARY KEY'), unique=(val=='UNIQUE'))
+    return indexes
 
-DATA_TYPES_REVERSE = {}
+# A reference for the values below:
+# http://msdn.microsoft.com/library/default.asp?url=/library/en-us/ado270/htm/mdcstdatatypeenum.asp
+DATA_TYPES_REVERSE = {
+# 8192 : Array ,
+# 128 : Binary ,
+# 9 : IDispatch ,
+# 12 : Variant ,
+# 13 : IUnknown ,
+# 21  : UnsignedBigInt,
+# 132 : UserDefined ,
+# 0   : Empty ,
+# 136 : Chapter ,
+# 138 : PropVariant ,
+# 204 : VarBinary ,
+# 205 : LongVarBinary ,
+-777: 'AutoField',                  # Custom number used to identify AutoFields
+2   : 'SmallIntegerField',          # SmallInt
+3   : 'IntegerField',               # Integer
+4   : 'FloatField',                 # Single
+5   : 'FloatField',                 # Decimal
+6   : 'FloatField',                 # Currency
+7   : 'DateField',                  # Date
+8   : 'CharField',                  # BSTR
+10  : 'IntegerField',               # Error
+11  : 'BooleanField',               # Boolean
+14  : 'FloatField',                 # Decimal
+16  : 'SmallIntegerField',          # TinyInt
+17  : 'PositiveSmallIntegerField',  # UnsignedTinyInt
+18  : 'PositiveSmallIntegerField',  # UnsignedSmallInt
+19  : 'PositiveIntegerField',       # UnsignedInt
+20  : 'IntegerField',               # BigInt
+64  : 'DateTimeField',              # FileTime
+72  : 'CharField',                  # GUID
+129 : 'CharField',                  # Char
+130 : 'CharField',                  # WChar
+131 : 'FloatField',                 # Numeric
+133 : 'DateField',                  # DBDate
+134 : 'TimeField',                  # DBTime
+135 : 'DateTimeField',              # DBTimeStamp
+139 : 'FloatField',                 # VarNumeric
+200 : 'CharField',                  # VarChar
+201 : 'TextField',                  # LongVarChar
+202 : 'CharField',                  # VarWChar
+203 : 'TextField',                  # LongVarWChar
+}
Index: django/db/backends/util.py
===================================================================
--- django/db/backends/util.py	(revision 4459)
+++ django/db/backends/util.py	(working copy)
@@ -1,3 +1,4 @@
+from django.conf import settings
 import datetime
 from time import time
 
@@ -16,8 +17,24 @@
             # formatting with '%' only works with tuples or dicts.
             if not isinstance(params, (tuple, dict)):
                 params = tuple(params)
+            # ado_mssql uses '?' for parameter escaping, so all '?'
+            # must be replaced with the standard '%s' if the parameter
+            # substitution is going to work.
+            if settings.DATABASE_ENGINE == 'ado_mssql':
+                sql = sql.replace('?', '%s')
+            # There are many situations that will cause the string
+            # substituion below to fail (e.g. wildcard characters '%'
+            # in LIKE queries).  Instead of attempting to figure out
+            # the many variations that can cause an error, the string substition
+            # will be attempted first; if it fails, then the sql
+            # and its parameters will be combined into a string similar to
+            # the one created in the executemany function below.
+            try:
+                sql = sql % tuple(params)
+            except:
+                sql = '%s SQL: %s' % (sql, str(tuple(params)))
             self.db.queries.append({
-                'sql': sql % params,
+                'sql': sql,
                 'time': "%.3f" % (stop - start),
             })
 
Index: django/contrib/sessions/middleware.py
===================================================================
--- django/contrib/sessions/middleware.py	(revision 4459)
+++ django/contrib/sessions/middleware.py	(working copy)
@@ -53,8 +53,11 @@
                 self._session_cache = {}
             else:
                 try:
+                    datenow = datetime.datetime.now()
+                    if hasattr(datenow, 'microsecond'):
+                        datenow = datenow.replace(microsecond=0)
                     s = Session.objects.get(session_key=self.session_key,
-                        expire_date__gt=datetime.datetime.now())
+                        expire_date__gt=datenow)
                     self._session_cache = s.get_decoded()
                 except (Session.DoesNotExist, SuspiciousOperation):
                     self._session_cache = {}
