Code

Ticket #2563: introspection.diff

File introspection.diff, 7.4 KB (added by sdelatorre+django@…, 8 years ago)

The SVN diff of the patch.

Line 
1Index: django/db/backends/ado_mssql/introspection.py
2===================================================================
3--- django/db/backends/ado_mssql/introspection.py       (revision 3600)
4+++ django/db/backends/ado_mssql/introspection.py       (working copy)
5@@ -1,13 +1,147 @@
6-def get_table_list(cursor):
7-    raise NotImplementedError
8+# Tested against MSDE and SQL Server 2000 using adodbapi 2.0.1
9+# Python 2.4.2 and 2.4.3 were used during testing.
10+from django.db.backends.ado_mssql.base import Cursor
11+
12+def get_table_list(cursor):
13+    "Returns a list of table names in the current database."
14+    print "# Note: Any fields that are named 'id', are of type 'AutoField', and"
15+    print "# and are Primary Keys will NOT appear in the model output below."
16+    print "# By default Django assumes that the each model's Primary Key is an "
17+    print "# AutoField with a name of 'id', so there is no need to add it to the"
18+    print "# model description."
19+    print
20+    cursor.execute("SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE'")
21+    return [row[2] for row in cursor.fetchall()]
22+
23+def _is_auto_field(cursor, table_name, column_name):
24+    cursor.execute("SELECT COLUMNPROPERTY( OBJECT_ID('%s'),'%s','IsIdentity')" % (table_name, column_name))
25+    return cursor.fetchall()[0][0]
26 
27-def get_table_description(cursor, table_name):
28-    raise NotImplementedError
29+def get_table_description(cursor, table_name, identity_check=True):
30+    """Returns a description of the table, with the DB-API cursor.description interface.
31+
32+    The 'auto_check' parameter has been added to the function argspec.
33+    If set to True, the function will check each of the table's fields for the
34+    IDENTITY property (the IDENTITY property is the MSSQL equivalent to an AutoField).
35+
36+    When a field is found with an IDENTITY property, it is given a custom field number
37+    of -777, which maps to the 'AutoField' value in the DATA_TYPES_REVERSE dict.
38+    """   
39+    cursor.execute("SELECT TOP 1 * FROM %s" % table_name)
40+    cursor.nextset()
41+    items = []
42+    if identity_check:
43+        for data in cursor.description:
44+            if _is_auto_field(cursor, table_name, data[0]):
45+                data = list(data)
46+                data[1] = -777
47+            items.append(list(data))
48+    else:
49+        items = cursor.description
50+    return items
51+
52+def _name_to_index(cursor, table_name):
53+    """
54+    Returns a dictionary of {field_name: field_index} for the given table.
55+    Indexes are 0-based.
56+    """
57+    return dict([(d[0], i) for i, d in enumerate(get_table_description(cursor, table_name, identity_check=False))])
58 
59-def get_relations(cursor, table_name):
60-    raise NotImplementedError
61-
62-def get_indexes(cursor, table_name):
63-    raise NotImplementedError
64-
65-DATA_TYPES_REVERSE = {}
66+def get_relations(cursor, table_name):
67+    """
68+    Returns a dictionary of {field_index: (field_index_other_table, other_table)}
69+    representing all relationships to the given table. Indexes are 0-based.   
70+    """
71+    table_index = _name_to_index(cursor, table_name)
72+    sql = """SELECT e.COLUMN_NAME AS column_name,
73+                    c.TABLE_NAME AS referenced_table_name,
74+                    d.COLUMN_NAME AS referenced_column_name
75+                    FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS a
76+                        INNER JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS AS b
77+                              ON a.CONSTRAINT_NAME = b.CONSTRAINT_NAME
78+                        INNER JOIN INFORMATION_SCHEMA.CONSTRAINT_TABLE_USAGE AS c
79+                              ON b.UNIQUE_CONSTRAINT_NAME = c.CONSTRAINT_NAME
80+                        INNER JOIN INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE AS d
81+                              ON c.CONSTRAINT_NAME = d.CONSTRAINT_NAME
82+                        INNER JOIN INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE AS e
83+                              ON a.CONSTRAINT_NAME = e.CONSTRAINT_NAME
84+                    WHERE a.TABLE_NAME = ? AND
85+                          a.CONSTRAINT_TYPE = 'FOREIGN KEY'"""
86+    cursor = Cursor(cursor.db.connection)
87+    cursor.execute(sql, (table_name,))
88+    return dict([(table_index[item[0]], (_name_to_index(cursor, item[1])[item[2]], item[1]))
89+                  for item in cursor.fetchall()])
90+   
91+def get_indexes(cursor, table_name):
92+    """
93+    Returns a dictionary of fieldname -> infodict for the given table,
94+    where each infodict is in the format:
95+        {'primary_key': boolean representing whether it's the primary key,
96+         'unique': boolean representing whether it's a unique index}
97+    """
98+    sql = """SELECT b.COLUMN_NAME, a.CONSTRAINT_TYPE
99+               FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS a INNER JOIN
100+                    INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE AS b
101+                    ON a.CONSTRAINT_NAME = b.CONSTRAINT_NAME AND
102+                       a.TABLE_NAME = b.TABLE_NAME
103+               WHERE a.TABLE_NAME = ? AND
104+                     (CONSTRAINT_TYPE = 'PRIMARY KEY' OR
105+                      CONSTRAINT_TYPE = 'UNIQUE')"""
106+    field_names = [item[0] for item in get_table_description(cursor, table_name, identity_check=False)]
107+    cursor = Cursor(cursor.db.connection)
108+    cursor.execute(sql, (table_name,))
109+    indexes = {}
110+    results = {}
111+    data = cursor.fetchall()
112+    if data:
113+        results.update(data)
114+    for field in field_names:
115+        val = results.get(field, None)
116+        indexes[field] = dict(primary_key=(val=='PRIMARY KEY'), unique=(val=='UNIQUE'))
117+    return indexes
118+
119+# A reference for the values below:
120+# http://msdn.microsoft.com/library/default.asp?url=/library/en-us/ado270/htm/mdcstdatatypeenum.asp
121+DATA_TYPES_REVERSE = {
122+# 8192 : Array ,
123+# 128 : Binary ,
124+# 9 : IDispatch ,
125+# 12 : Variant ,
126+# 13 : IUnknown ,
127+# 21  : UnsignedBigInt,
128+# 132 : UserDefined ,
129+# 0   : Empty ,
130+# 136 : Chapter ,
131+# 138 : PropVariant ,
132+# 204 : VarBinary ,
133+# 205 : LongVarBinary ,
134+-777: 'AutoField',                  # Custom number used to identify AutoFields
135+2   : 'SmallIntegerField',          # SmallInt
136+3   : 'IntegerField',               # Integer
137+4   : 'FloatField',                 # Single
138+5   : 'FloatField',                 # Decimal
139+6   : 'FloatField',                 # Currency
140+7   : 'DateField',                  # Date
141+8   : 'CharField',                  # BSTR
142+10  : 'IntegerField',               # Error
143+11  : 'BooleanField',               # Boolean
144+14  : 'FloatField',                 # Decimal
145+16  : 'SmallIntegerField',          # TinyInt
146+17  : 'PositiveSmallIntegerField',  # UnsignedTinyInt
147+18  : 'PositiveSmallIntegerField',  # UnsignedSmallInt
148+19  : 'PositiveIntegerField',       # UnsignedInt
149+20  : 'IntegerField',               # BigInt
150+64  : 'DateTimeField',              # FileTime
151+72  : 'CharField',                  # GUID
152+129 : 'CharField',                  # Char
153+130 : 'CharField',                  # WChar
154+131 : 'FloatField',                 # Numeric
155+133 : 'DateField',                  # DBDate
156+134 : 'TimeField',                  # DBTime
157+135 : 'DateTimeField',              # DBTimeStamp
158+139 : 'FloatField',                 # VarNumeric
159+200 : 'CharField',                  # VarChar
160+201 : 'TextField',                  # LongVarChar
161+202 : 'CharField',                  # VarWChar
162+203 : 'TextField',                  # LongVarWChar
163+}