Code

Ticket #2358: mssql_update8a.diff

File mssql_update8a.diff, 14.5 KB (added by wycharon@…, 7 years ago)

full pacth for typo. i am sorry. it's my mistake for the typo. i will re upload the mssql_update7.diff

Line 
1Index: db/models/base.py
2===================================================================
3--- db/models/base.py   (revision 4459)
4+++ db/models/base.py   (working copy)
5@@ -170,7 +170,7 @@
6         record_exists = True
7         if pk_val is not None:
8             # Determine whether a record with the primary key already exists.
9-            cursor.execute("SELECT 1 FROM %s WHERE %s=%%s LIMIT 1" % \
10+            cursor.execute("SELECT 1 FROM %s WHERE %s=%%s" %
11                 (backend.quote_name(self._meta.db_table), backend.quote_name(self._meta.pk.column)), [pk_val])
12             # If it does already exist, do an UPDATE.
13             if cursor.fetchone():
14Index: db/models/fields/__init__.py
15===================================================================
16--- db/models/fields/__init__.py        (revision 4459)
17+++ db/models/fields/__init__.py        (working copy)
18@@ -519,12 +519,15 @@
19         if value is not None:
20             # MySQL will throw a warning if microseconds are given, because it
21             # doesn't support microseconds.
22-            if settings.DATABASE_ENGINE == 'mysql' and hasattr(value, 'microsecond'):
23+            if (settings.DATABASE_ENGINE == 'mysql' or settings.DATABASE_ENGINE == 'ado_mssql') and hasattr(value, 'microsecond'):
24                 value = value.replace(microsecond=0)
25             value = str(value)
26         return Field.get_db_prep_save(self, value)
27 
28     def get_db_prep_lookup(self, lookup_type, value):
29+        # MSSQL doesn't like microseconds.
30+        if settings.DATABASE_ENGINE == 'ado_mssql' and hasattr(value, 'microsecond'):
31+            value = value.replace(microsecond=0)
32         if lookup_type == 'range':
33             value = [str(v) for v in value]
34         else:
35@@ -803,7 +806,7 @@
36         if value is not None:
37             # MySQL will throw a warning if microseconds are given, because it
38             # doesn't support microseconds.
39-            if settings.DATABASE_ENGINE == 'mysql':
40+            if settings.DATABASE_ENGINE == 'mysql' or settings.DATABASE_ENGINE == 'ado_mssql':
41                 value = value.replace(microsecond=0)
42             value = str(value)
43         return Field.get_db_prep_save(self, value)
44Index: db/backends/ado_mssql/base.py
45===================================================================
46--- db/backends/ado_mssql/base.py       (revision 4459)
47+++ db/backends/ado_mssql/base.py       (working copy)
48@@ -3,10 +3,13 @@
49 
50 Requires adodbapi 2.0.1: http://adodbapi.sourceforge.net/
51 """
52+import pythoncom
53 
54+from django.conf import settings
55 from django.db.backends import util
56+
57 try:
58-    import adodbapi as Database
59+    import adodbapi.adodbapi as Database
60 except ImportError, e:
61     from django.core.exceptions import ImproperlyConfigured
62     raise ImproperlyConfigured, "Error loading adodbapi module: %s" % e
63@@ -25,7 +28,9 @@
64     def executeHelper(self, operation, isStoredProcedureCall, parameters=None):
65         if parameters is not None and "%s" in operation:
66             operation = operation.replace("%s", "?")
67+        pythoncom.CoInitialize()
68         Database.Cursor.executeHelper(self, operation, isStoredProcedureCall, parameters)
69+        pythoncom.CoUninitialize()
70 
71 class Connection(Database.Connection):
72     def cursor(self):
73@@ -44,6 +49,8 @@
74         return datetime.datetime(*tuple(tv))
75     if type(res) == float and str(res)[-2:] == ".0":
76         return int(res) # If float but int, then int.
77+    if type(res) == unicode:
78+        return res.encode(settings.DEFAULT_CHARSET)
79     return res
80 Database.convertVariantToPython = variantToPython
81 
82@@ -69,8 +76,11 @@
83                 settings.DATABASE_HOST = "127.0.0.1"
84             # TODO: Handle DATABASE_PORT.
85             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)
86+            pythoncom.CoInitialize()
87             self.connection = Database.connect(conn_string)
88-        cursor = self.connection.cursor()
89+            pythoncom.CoUninitialize()
90+       
91+        cursor = Cursor(self.connection)
92         if settings.DEBUG:
93             return util.CursorDebugWrapper(cursor, self)
94         return cursor
95@@ -99,7 +109,7 @@
96 dictfetchall  = util.dictfetchall
97 
98 def get_last_insert_id(cursor, table_name, pk_name):
99-    cursor.execute("SELECT %s FROM %s WHERE %s = @@IDENTITY" % (pk_name, table_name, pk_name))
100+    cursor.execute("SELECT %s FROM %s WHERE %s = IDENT_CURRENT('%s')" % (pk_name, table_name, pk_name,table_name))
101     return cursor.fetchone()[0]
102 
103 def get_date_extract_sql(lookup_type, table_name):
104@@ -117,10 +127,13 @@
105 
106 def get_limit_offset_sql(limit, offset=None):
107     # TODO: This is a guess. Make sure this is correct.
108-    sql = "LIMIT %s" % limit
109-    if offset and offset != 0:
110-        sql += " OFFSET %s" % offset
111-    return sql
112+    # should be "SELECT TOP %s" % limit
113+    # not LIMIT at the end
114+    return ""
115+    #sql = "LIMIT %s" % limit
116+    #if offset and offset != 0:
117+    #    sql += " OFFSET %s" % offset
118+    #return sql
119 
120 def get_random_function_sql():
121     return "RAND()"
122Index: db/backends/ado_mssql/introspection.py
123===================================================================
124--- db/backends/ado_mssql/introspection.py      (revision 4459)
125+++ db/backends/ado_mssql/introspection.py      (working copy)
126@@ -1,13 +1,147 @@
127+# Tested against MSDE and SQL Server 2000 using adodbapi 2.0.1
128+# Python 2.4.2 and 2.4.3 were used during testing.
129+from django.db.backends.ado_mssql.base import Cursor
130+
131 def get_table_list(cursor):
132-    raise NotImplementedError
133+    "Returns a list of table names in the current database."
134+    print "# Note: Any fields that are named 'id', are of type 'AutoField', and"
135+    print "# and are Primary Keys will NOT appear in the model output below."
136+    print "# By default Django assumes that the each model's Primary Key is an "
137+    print "# AutoField with a name of 'id', so there is no need to add it to the"
138+    print "# model description."
139+    print
140+    cursor.execute("SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE'")
141+    return [row[2] for row in cursor.fetchall()]
142 
143-def get_table_description(cursor, table_name):
144-    raise NotImplementedError
145+def _is_auto_field(cursor, table_name, column_name):
146+    cursor.execute("SELECT COLUMNPROPERTY( OBJECT_ID('%s'),'%s','IsIdentity')" % (table_name, column_name))
147+    return cursor.fetchall()[0][0]
148 
149+def get_table_description(cursor, table_name, identity_check=True):
150+    """Returns a description of the table, with the DB-API cursor.description interface.
151+
152+    The 'auto_check' parameter has been added to the function argspec.
153+    If set to True, the function will check each of the table's fields for the
154+    IDENTITY property (the IDENTITY property is the MSSQL equivalent to an AutoField).
155+
156+    When a field is found with an IDENTITY property, it is given a custom field number
157+    of -777, which maps to the 'AutoField' value in the DATA_TYPES_REVERSE dict.
158+    """   
159+    cursor.execute("SELECT TOP 1 * FROM %s" % table_name)
160+    cursor.nextset()
161+    items = []
162+    if identity_check:
163+        for data in cursor.description:
164+            if _is_auto_field(cursor, table_name, data[0]):
165+                data = list(data)
166+                data[1] = -777
167+            items.append(list(data))
168+    else:
169+        items = cursor.description
170+    return items
171+
172+def _name_to_index(cursor, table_name):
173+    """
174+    Returns a dictionary of {field_name: field_index} for the given table.
175+    Indexes are 0-based.
176+    """
177+    return dict([(d[0], i) for i, d in enumerate(get_table_description(cursor, table_name, identity_check=False))])
178+
179 def get_relations(cursor, table_name):
180-    raise NotImplementedError
181-
182+    """
183+    Returns a dictionary of {field_index: (field_index_other_table, other_table)}
184+    representing all relationships to the given table. Indexes are 0-based.   
185+    """
186+    table_index = _name_to_index(cursor, table_name)
187+    sql = """SELECT e.COLUMN_NAME AS column_name,
188+                    c.TABLE_NAME AS referenced_table_name,
189+                    d.COLUMN_NAME AS referenced_column_name
190+                    FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS a
191+                        INNER JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS AS b
192+                              ON a.CONSTRAINT_NAME = b.CONSTRAINT_NAME
193+                        INNER JOIN INFORMATION_SCHEMA.CONSTRAINT_TABLE_USAGE AS c
194+                              ON b.UNIQUE_CONSTRAINT_NAME = c.CONSTRAINT_NAME
195+                        INNER JOIN INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE AS d
196+                              ON c.CONSTRAINT_NAME = d.CONSTRAINT_NAME
197+                        INNER JOIN INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE AS e
198+                              ON a.CONSTRAINT_NAME = e.CONSTRAINT_NAME
199+                    WHERE a.TABLE_NAME = ? AND
200+                          a.CONSTRAINT_TYPE = 'FOREIGN KEY'"""
201+    cursor = Cursor(cursor.db.connection)
202+    cursor.execute(sql, (table_name,))
203+    return dict([(table_index[item[0]], (_name_to_index(cursor, item[1])[item[2]], item[1]))
204+                  for item in cursor.fetchall()])
205+   
206 def get_indexes(cursor, table_name):
207-    raise NotImplementedError
208+    """
209+    Returns a dictionary of fieldname -> infodict for the given table,
210+    where each infodict is in the format:
211+        {'primary_key': boolean representing whether it's the primary key,
212+         'unique': boolean representing whether it's a unique index}
213+    """
214+    sql = """SELECT b.COLUMN_NAME, a.CONSTRAINT_TYPE
215+               FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS a INNER JOIN
216+                    INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE AS b
217+                    ON a.CONSTRAINT_NAME = b.CONSTRAINT_NAME AND
218+                       a.TABLE_NAME = b.TABLE_NAME
219+               WHERE a.TABLE_NAME = ? AND
220+                     (CONSTRAINT_TYPE = 'PRIMARY KEY' OR
221+                      CONSTRAINT_TYPE = 'UNIQUE')"""
222+    field_names = [item[0] for item in get_table_description(cursor, table_name, identity_check=False)]
223+    cursor = Cursor(cursor.db.connection)
224+    cursor.execute(sql, (table_name,))
225+    indexes = {}
226+    results = {}
227+    data = cursor.fetchall()
228+    if data:
229+        results.update(data)
230+    for field in field_names:
231+        val = results.get(field, None)
232+        indexes[field] = dict(primary_key=(val=='PRIMARY KEY'), unique=(val=='UNIQUE'))
233+    return indexes
234 
235-DATA_TYPES_REVERSE = {}
236+# A reference for the values below:
237+# http://msdn.microsoft.com/library/default.asp?url=/library/en-us/ado270/htm/mdcstdatatypeenum.asp
238+DATA_TYPES_REVERSE = {
239+# 8192 : Array ,
240+# 128 : Binary ,
241+# 9 : IDispatch ,
242+# 12 : Variant ,
243+# 13 : IUnknown ,
244+# 21  : UnsignedBigInt,
245+# 132 : UserDefined ,
246+# 0   : Empty ,
247+# 136 : Chapter ,
248+# 138 : PropVariant ,
249+# 204 : VarBinary ,
250+# 205 : LongVarBinary ,
251+-777: 'AutoField',                  # Custom number used to identify AutoFields
252+2   : 'SmallIntegerField',          # SmallInt
253+3   : 'IntegerField',               # Integer
254+4   : 'FloatField',                 # Single
255+5   : 'FloatField',                 # Decimal
256+6   : 'FloatField',                 # Currency
257+7   : 'DateField',                  # Date
258+8   : 'CharField',                  # BSTR
259+10  : 'IntegerField',               # Error
260+11  : 'BooleanField',               # Boolean
261+14  : 'FloatField',                 # Decimal
262+16  : 'SmallIntegerField',          # TinyInt
263+17  : 'PositiveSmallIntegerField',  # UnsignedTinyInt
264+18  : 'PositiveSmallIntegerField',  # UnsignedSmallInt
265+19  : 'PositiveIntegerField',       # UnsignedInt
266+20  : 'IntegerField',               # BigInt
267+64  : 'DateTimeField',              # FileTime
268+72  : 'CharField',                  # GUID
269+129 : 'CharField',                  # Char
270+130 : 'CharField',                  # WChar
271+131 : 'FloatField',                 # Numeric
272+133 : 'DateField',                  # DBDate
273+134 : 'TimeField',                  # DBTime
274+135 : 'DateTimeField',              # DBTimeStamp
275+139 : 'FloatField',                 # VarNumeric
276+200 : 'CharField',                  # VarChar
277+201 : 'TextField',                  # LongVarChar
278+202 : 'CharField',                  # VarWChar
279+203 : 'TextField',                  # LongVarWChar
280+}
281Index: db/backends/util.py
282===================================================================
283--- db/backends/util.py (revision 4459)
284+++ db/backends/util.py (working copy)
285@@ -1,3 +1,4 @@
286+from django.conf import settings
287 import datetime
288 from time import time
289 
290@@ -16,8 +17,24 @@
291             # formatting with '%' only works with tuples or dicts.
292             if not isinstance(params, (tuple, dict)):
293                 params = tuple(params)
294+            # ado_mssql uses '?' for parameter escaping, so all '?'
295+            # must be replaced with the standard '%s' if the parameter
296+            # substitution is going to work.
297+            if settings.DATABASE_ENGINE == 'ado_mssql':
298+                sql = sql.replace('?', '%s')
299+            # There are many situations that will cause the string
300+            # substituion below to fail (e.g. wildcard characters '%'
301+            # in LIKE queries).  Instead of attempting to figure out
302+            # the many variations that can cause an error, the string substition
303+            # will be attempted first; if it fails, then the sql
304+            # and its parameters will be combined into a string similar to
305+            # the one created in the executemany function below.
306+            try:
307+                sql = sql % tuple(params)
308+            except:
309+                sql = '%s SQL: %s' % (sql, str(tuple(params)))
310             self.db.queries.append({
311-                'sql': sql % params,
312+                'sql': sql,
313                 'time': "%.3f" % (stop - start),
314             })
315 
316Index: contrib/sessions/middleware.py
317===================================================================
318--- contrib/sessions/middleware.py      (revision 4459)
319+++ contrib/sessions/middleware.py      (working copy)
320@@ -53,8 +53,11 @@
321                 self._session_cache = {}
322             else:
323                 try:
324+                    datenow = datetime.datetime.now()
325+                    if hasattr(datenow, 'microsecond'):
326+                        datenow = datenow.replace(microsecond=0)
327                     s = Session.objects.get(session_key=self.session_key,
328-                        expire_date__gt=datetime.datetime.now())
329+                        expire_date__gt=datenow)
330                     self._session_cache = s.get_decoded()
331                 except (Session.DoesNotExist, SuspiciousOperation):
332                     self._session_cache = {}