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