Ticket #1429: adodbapi-from-0.91.patch
File adodbapi-from-0.91.patch, 19.5 KB (added by , 19 years ago) |
---|
-
django/core/db/backends/mssql.py
=== django/core/db/backends/mssql.py ==================================================================
1 """ 2 SQL Server database backend for Django. 3 4 Requires ADODBAPI: http://adodbapi.sf.net 5 """ 6 7 import re 8 9 from django.core.db import base 10 from django.core.db.dicthelpers import * 11 import pymssql as Database 12 from itertools import repeat 13 14 DatabaseError = Database.DatabaseError 15 16 class CursorWrapper: 17 def __init__(self, conn, cursor): 18 self._conn = conn 19 self._cursor = cursor 20 self._limit_re = re.compile(r'(?:LIMIT\s+(\d+))?\s*(?:OFFSET\s+(\d+))?$') 21 self.execute('') 22 23 def execute(self, sql, params=None): 24 self._conn.queries.append((sql, params)) 25 sql, offset = self._parse_sql(sql, params) 26 result = self._cursor.execute(sql, params) 27 if offset:` 28 self._cursor.fetchmany(offset) # discard offset amount 29 return result 30 31 def executemany(self, sql, paramseq): 32 self._conn.queries.append((sql, params)) 33 sql, offset = self._parse_sql(sql, paramseq[0]) 34 return self._cursor.executemany(sql, paramseq) 35 36 def _parse_sql(self, sql, params): 37 limit, offset = self._limit_re.search(sql).groups() 38 sql = self._limit_re.sub('', sql) 39 40 if offset: 41 offset = int(offset) 42 else: 43 offset = 0 44 45 if not limit: 46 return sql, offset 47 48 limit = int(limit) + offset 49 50 return (' TOP %s ' % limit).join(sql.split(None, 1)), offset 51 52 def __getattr__(self, attr): 53 return getattr(self._cursor, attr) 54 55 56 class DatabaseWrapper: 57 def __init__(self): 58 self.connection = None 59 self.queries = [] 60 61 def cursor(self): 62 from django.conf.settings import DEBUG 63 if DEBUG: 64 return base.CursorDebugWrapper(self._cursor(), self) 65 return self._cursor() 66 67 def _cursor(self): 68 if self.connection is None: 69 from django.conf.settings import DATABASE_USER, DATABASE_NAME, \ 70 DATABASE_HOST, DATABASE_PASSWORD 71 self.connection = Database.connect(host=DATABASE_HOST, 72 database=DATABASE_NAME, 73 user=DATABASE_USER, 74 password=DATABASE_PASSWORD) 75 return CursorWrapper(self, self.connection.cursor()) 76 77 def commit(self): 78 self.connection.commit() 79 80 def rollback(self): 81 if self.connection is not None: 82 self.connection.rollback() 83 84 def close(self): 85 if self.connection is not None: 86 self.connection.close() 87 self.connection = None 88 89 def quote_name(self, name): 90 if name.startswith('[') and name.endswith(']'): 91 return name # Quoting once is enough. 92 return '[%s]' % name 93 94 def get_last_insert_id(cursor, table_name, pk_name): 95 cursor.execute("SELECT cast(@@IDENTITY AS int)") # or possibly SCOPE_IDENTITY() if necessary 96 return cursor.fetchone()[0] 97 98 def get_date_extract_sql(lookup_type, table_name): 99 # lookup_type is 'year', 'month', 'day' 100 return "DATEPART(%s, %s)" % (lookup_type, table_name) 101 102 def get_date_trunc_sql(lookup_type, field_name): 103 # lookup_type is 'year', 'month', 'day' 104 if lookup_type=='year': 105 return "Convert(datetime, Convert(varchar, DATEPART(year, %s)) + '/01/01')" % field_name 106 if lookup_type=='month': 107 return "Convert(datetime, Convert(varchar, DATEPART(year, %s)) + '/' + Convert(varchar, DATEPART(month, %s)) + '/01')" % (field_name, field_name) 108 if lookup_type=='day': 109 return "Convert(datetime, Convert(varchar(12), %s))" % field_name 110 111 def get_limit_offset_sql(limit, offset=None): 112 sql = "LIMIT %s" % limit 113 if offset and offset != 0: 114 sql += " OFFSET %s" % offset 115 return sql 116 117 def get_random_function_sql(): 118 return 'rand()' 119 120 def get_table_list(cursor): 121 cursor.execute("select name from sysobjects where type='U'") 122 return [row[0] for row in cursor.fetchall()] 123 124 def get_table_description(cursor, table_name): 125 raise NotImplementedError 126 127 def get_relations(cursor, table_name): 128 raise NotImplementedError 129 130 OPERATOR_MAPPING = { 131 'exact': '= %s', 132 'iexact': 'LIKE %s', 133 'contains': 'LIKE %s', 134 'icontains': 'LIKE %s', 135 'ne': '!= %s', 136 'gt': '> %s', 137 'gte': '>= %s', 138 'lt': '< %s', 139 'lte': '<= %s', 140 'startswith': 'LIKE %s', 141 'endswith': 'LIKE %s', 142 'istartswith': 'LIKE %s', 143 'iendswith': 'LIKE %s', 144 } 145 146 def get_relations(cursor, table_name): 147 raise NotImplementedError 148 """FIXME: something like below is a start on SQL: 149 source: 150 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnsqlpro03/html/sp03l12.asp 151 152 SELECT 153 KCU1.CONSTRAINT_NAME AS 'FK_CONSTRAINT_NAME' 154 , KCU1.TABLE_NAME AS 'FK_TABLE_NAME' 155 , KCU1.COLUMN_NAME AS 'FK_COLUMN_NAME' 156 , KCU1.ORDINAL_POSITION AS 'FK_ORDINAL_POSITION' 157 , KCU2.CONSTRAINT_NAME AS 'UQ_CONSTRAINT_NAME' 158 , KCU2.TABLE_NAME AS 'UQ_TABLE_NAME' 159 , KCU2.COLUMN_NAME AS 'UQ_COLUMN_NAME' 160 , KCU2.ORDINAL_POSITION AS 'UQ_ORDINAL_POSITION' 161 FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS RC 162 JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU1 163 ON KCU1.CONSTRAINT_CATALOG = RC.CONSTRAINT_CATALOG 164 AND KCU1.CONSTRAINT_SCHEMA = RC.CONSTRAINT_SCHEMA 165 AND KCU1.CONSTRAINT_NAME = RC.CONSTRAINT_NAME 166 JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU2 167 ON KCU2.CONSTRAINT_CATALOG = 168 RC.UNIQUE_CONSTRAINT_CATALOG 169 AND KCU2.CONSTRAINT_SCHEMA = 170 RC.UNIQUE_CONSTRAINT_SCHEMA 171 AND KCU2.CONSTRAINT_NAME = 172 RC.UNIQUE_CONSTRAINT_NAME 173 AND KCU2.ORDINAL_POSITION = KCU1.ORDINAL_POSITION 174 175 """ 176 177 def quote_name(name): 178 # TODO: Figure out how MS-SQL quotes database identifiers. 179 return name 180 181 182 # This dictionary maps Field objects to their associated SQL Server column 183 # types, as strings. Column-type strings can contain format strings; they'll 184 # be interpolated against the values of Field.__dict__ before being output. 185 # If a column type is set to None, it won't be included in the output. 186 DATA_TYPES = { 187 'AutoField': 'int identity', 188 'BooleanField': 'bit', 189 'CharField': 'varchar(%(maxlength)s)', 190 'CommaSeparatedIntegerField': 'varchar(%(maxlength)s)', 191 'DateField': 'smalldatetime', 192 'DateTimeField': 'smalldatetime', 193 'FileField': 'varchar(100)', 194 'FilePathField': 'varchar(100)', 195 'FloatField': 'numeric(%(max_digits)s, %(decimal_places)s)', 196 'ImageField': 'nvarchar(100)', 197 'IntegerField': 'int', 198 'IPAddressField': 'nchar(15)', 199 'ManyToManyField': None, 200 'NullBooleanField': 'bit', 201 'OneToOneField': 'int', 202 'PhoneNumberField': 'varchar(20)', 203 'PositiveIntegerField': 'int CONSTRAINT [CK_int_pos_%(column)s] CHECK ([%(column)s] > 0)', 204 'PositiveSmallIntegerField': 'smallint CONSTRAINT [CK_smallint_pos_%(column)s] CHECK ([%(column)s] > 0)', 205 'SlugField': 'varchar(50)', 206 'SmallIntegerField': 'smallint', 207 'TextField': 'ntext', 208 'TimeField': 'datetime', 209 'URLField': 'nvarchar(200)', 210 'USStateField': 'nvarchar(2)', 211 'XMLField': 'ntext', 212 } 213 214 DATA_TYPES_REVERSE = { } -
django/core/db/backends/ado_mssql.py
Property changes on: django/core/db/backends/mssql.py ___________________________________________________________________ Name: svn:eol-style +native === django/core/db/backends/ado_mssql.py ==================================================================
1 1 """ 2 ADO MSSQLdatabase backend for Django.2 SQL Server database backend for Django. 3 3 4 Requires adodbapi 2.0.1: http://adodbapi.sourceforge.net/4 Requires ADODBAPI: http://adodbapi.sf.net 5 5 """ 6 6 7 import re 8 import threading 9 7 10 from django.core.db import base 8 11 from django.core.db.dicthelpers import * 9 12 import adodbapi as Database 10 import datetime 11 try: 12 import mx 13 except ImportError: 14 mx = None 13 from itertools import repeat 15 14 16 15 DatabaseError = Database.DatabaseError 17 16 18 # We need to use a special Cursor class because adodbapi expects question-mark 19 # param style, but Django expects "%s". This cursor converts question marks to 20 # format-string style. 21 class Cursor(Database.Cursor): 22 def executeHelper(self, operation, isStoredProcedureCall, parameters=None): 23 if parameters is not None and "%s" in operation: 24 operation = operation.replace("%s", "?") 25 Database.Cursor.executeHelper(self, operation, isStoredProcedureCall, parameters) 17 class CursorWrapper: 18 def __init__(self, cursor): 19 self._cursor = cursor 20 self._limit_re = re.compile(r'(?:LIMIT\s+(\d+))?\s*(?:OFFSET\s+(\d+))?$') 26 21 27 class Connection(Database.Connection): 28 def cursor(self): 29 return Cursor(self) 30 Database.Connection = Connection 22 def execute(self, sql, params=None): 23 sql, offset = self._parse_sql(sql, params) 24 result = self._cursor.execute(sql, params) 25 if offset: 26 self._cursor.fetchmany(offset) # discard offset amount 27 return result 31 28 32 origCVtoP = Database.convertVariantToPython 33 def variantToPython(variant, adType): 34 if type(variant) == bool and adType == 11: 35 return variant # bool not 1/0 36 res = origCVtoP(variant, adType) 37 if mx is not None and type(res) == mx.DateTime.mxDateTime.DateTimeType: 38 # Convert ms.DateTime objects to Python datetime.datetime objects. 39 tv = list(res.tuple()[:7]) 40 tv[-2] = int(tv[-2]) 41 return datetime.datetime(*tuple(tv)) 42 if type(res) == float and str(res)[-2:] == ".0": 43 return int(res) # If float but int, then int. 44 return res 45 Database.convertVariantToPython = variantToPython 29 def executemany(self, sql, paramseq): 30 sql, offset = self._parse_sql(sql, paramseq[0]) 31 return self._cursor.executemany(sql, paramseq) 46 32 33 def _parse_sql(self, sql, params): 34 sql = sql.replace('%s', '?') 35 #if params: 36 #sql = sql % tuple(repeat('?', len(params))) 37 38 limit, offset = self._limit_re.search(sql).groups() 39 sql = self._limit_re.sub('', sql) 40 41 if offset: 42 offset = int(offset) 43 else: 44 offset = 0 45 46 if not limit: 47 return sql, offset 48 49 limit = int(limit) + offset 50 51 return (' TOP %s ' % limit).join(sql.split(None, 1)), offset 52 53 def __getattr__(self, attr): 54 return getattr(self._cursor, attr) 55 56 def connect(host, database, user, password): 57 import pythoncom 58 pythoncom.CoInitialize() # Make sure COM is initialized for this thread 59 provider = 'sqlncli' 60 #provider = 'sqloledb' 61 return Database.connect('Provider=%s;Data Source=%s;Initial Catalog=%s;User Id=%s;Password=%s' 62 % (provider, host, database, user, password)) 63 47 64 class DatabaseWrapper: 48 65 def __init__(self): 49 self. connection = None66 self.local = threading.local() 50 67 self.queries = [] 51 68 69 def connection(): 70 def fget(self): 71 return getattr(self.local, 'connection', None) 72 def fset(self, value): 73 self.local.connection = value 74 def fdel(self): 75 del self.local.connection 76 return property(fget, fset, fdel) 77 connection = connection() 78 52 79 def cursor(self): 53 from django.conf.settings import DATABASE_USER, DATABASE_NAME, DATABASE_HOST, DATABASE_PORT, DATABASE_PASSWORD, DEBUG 54 if self.connection is None: 55 if DATABASE_NAME == '' or DATABASE_USER == '': 56 from django.core.exceptions import ImproperlyConfigured 57 raise ImproperlyConfigured, "You need to specify both DATABASE_NAME and DATABASE_USER in your Django settings file." 58 if not DATABASE_HOST: 59 DATABASE_HOST = "127.0.0.1" 60 # TODO: Handle DATABASE_PORT. 61 conn_string = "PROVIDER=SQLOLEDB;DATA SOURCE=%s;UID=%s;PWD=%s;DATABASE=%s" % (DATABASE_HOST, DATABASE_USER, DATABASE_PASSWORD, DATABASE_NAME) 62 self.connection = Database.connect(conn_string) 63 cursor = self.connection.cursor() 80 from django.conf.settings import DEBUG 64 81 if DEBUG: 65 return base.CursorDebugWrapper( cursor, self)66 return cursor82 return base.CursorDebugWrapper(self._cursor(), self) 83 return self._cursor() 67 84 85 def _cursor(self): 86 if self.connection is None: 87 from django.conf.settings import DATABASE_USER, DATABASE_NAME, DATABASE_HOST, DATABASE_PASSWORD 88 self.connection = connect(DATABASE_HOST, DATABASE_NAME, DATABASE_USER, DATABASE_PASSWORD) 89 return CursorWrapper(self.connection.cursor()) 90 68 91 def commit(self): 69 returnself.connection.commit()92 self.connection.commit() 70 93 71 94 def rollback(self): 72 if self.connection :73 returnself.connection.rollback()95 if self.connection is not None: 96 self.connection.rollback() 74 97 75 98 def close(self): 76 99 if self.connection is not None: 77 self.connection.close() 100 try: 101 self.connection.close() 102 except Database.InternalError: 103 # ignore connection errors for 'No transaction is active.' 104 pass 78 105 self.connection = None 79 106 80 107 def quote_name(self, name): … … 83 110 return '[%s]' % name 84 111 85 112 def get_last_insert_id(cursor, table_name, pk_name): 86 cursor.execute("SELECT %s FROM %s WHERE %s = @@IDENTITY" % (pk_name, table_name, pk_name))113 cursor.execute("SELECT cast(@@IDENTITY AS int)") # or possibly SCOPE_IDENTITY() if necessary 87 114 return cursor.fetchone()[0] 88 115 89 116 def get_date_extract_sql(lookup_type, table_name): … … 100 127 return "Convert(datetime, Convert(varchar(12), %s))" % field_name 101 128 102 129 def get_limit_offset_sql(limit, offset=None): 103 # TODO: This is a guess. Make sure this is correct.104 130 sql = "LIMIT %s" % limit 105 131 if offset and offset != 0: 106 132 sql += " OFFSET %s" % offset 107 133 return sql 108 134 109 135 def get_random_function_sql(): 110 return "RAND()"136 return 'rand()' 111 137 112 138 def get_table_list(cursor): 113 raise NotImplementedError 139 cursor.execute("select name from sysobjects where type='U'") 140 return [row[0] for row in cursor.fetchall()] 114 141 115 142 def get_table_description(cursor, table_name): 116 143 raise NotImplementedError … … 134 161 'iendswith': 'LIKE %s', 135 162 } 136 163 164 def get_relations(cursor, table_name): 165 raise NotImplementedError 166 """FIXME: something like below is a start on SQL: 167 source: 168 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnsqlpro03/html/sp03l12.asp 169 170 SELECT 171 KCU1.CONSTRAINT_NAME AS 'FK_CONSTRAINT_NAME' 172 , KCU1.TABLE_NAME AS 'FK_TABLE_NAME' 173 , KCU1.COLUMN_NAME AS 'FK_COLUMN_NAME' 174 , KCU1.ORDINAL_POSITION AS 'FK_ORDINAL_POSITION' 175 , KCU2.CONSTRAINT_NAME AS 'UQ_CONSTRAINT_NAME' 176 , KCU2.TABLE_NAME AS 'UQ_TABLE_NAME' 177 , KCU2.COLUMN_NAME AS 'UQ_COLUMN_NAME' 178 , KCU2.ORDINAL_POSITION AS 'UQ_ORDINAL_POSITION' 179 FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS RC 180 JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU1 181 ON KCU1.CONSTRAINT_CATALOG = RC.CONSTRAINT_CATALOG 182 AND KCU1.CONSTRAINT_SCHEMA = RC.CONSTRAINT_SCHEMA 183 AND KCU1.CONSTRAINT_NAME = RC.CONSTRAINT_NAME 184 JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU2 185 ON KCU2.CONSTRAINT_CATALOG = 186 RC.UNIQUE_CONSTRAINT_CATALOG 187 AND KCU2.CONSTRAINT_SCHEMA = 188 RC.UNIQUE_CONSTRAINT_SCHEMA 189 AND KCU2.CONSTRAINT_NAME = 190 RC.UNIQUE_CONSTRAINT_NAME 191 AND KCU2.ORDINAL_POSITION = KCU1.ORDINAL_POSITION 192 193 """ 194 195 def quote_name(name): 196 # TODO: Figure out how MS-SQL quotes database identifiers. 197 return name 198 199 200 # This dictionary maps Field objects to their associated SQL Server column 201 # types, as strings. Column-type strings can contain format strings; they'll 202 # be interpolated against the values of Field.__dict__ before being output. 203 # If a column type is set to None, it won't be included in the output. 137 204 DATA_TYPES = { 138 'AutoField': 'int IDENTITY (1, 1)',205 'AutoField': 'int identity', 139 206 'BooleanField': 'bit', 140 207 'CharField': 'varchar(%(maxlength)s)', 141 208 'CommaSeparatedIntegerField': 'varchar(%(maxlength)s)', … … 144 211 'FileField': 'varchar(100)', 145 212 'FilePathField': 'varchar(100)', 146 213 'FloatField': 'numeric(%(max_digits)s, %(decimal_places)s)', 147 'ImageField': ' varchar(100)',214 'ImageField': 'nvarchar(100)', 148 215 'IntegerField': 'int', 149 'IPAddressField': ' char(15)',216 'IPAddressField': 'nchar(15)', 150 217 'ManyToManyField': None, 151 218 'NullBooleanField': 'bit', 152 219 'OneToOneField': 'int', … … 155 222 'PositiveSmallIntegerField': 'smallint CONSTRAINT [CK_smallint_pos_%(column)s] CHECK ([%(column)s] > 0)', 156 223 'SlugField': 'varchar(50)', 157 224 'SmallIntegerField': 'smallint', 158 'TextField': 'text', 159 'TimeField': 'time', 160 'URLField': 'varchar(200)', 161 'USStateField': 'varchar(2)', 225 'TextField': 'ntext', 226 'TimeField': 'datetime', 227 'URLField': 'nvarchar(200)', 228 'USStateField': 'nvarchar(2)', 229 'XMLField': 'ntext', 162 230 } 163 231 164 DATA_TYPES_REVERSE = { }232 DATA_TYPES_REVERSE = { } -
django/core/meta/__init__.py
=== django/core/meta/__init__.py ==================================================================
1021 1021 placeholders.append('(SELECT COUNT(*) FROM %s WHERE %s = %%s)' % \ 1022 1022 (db.db.quote_name(opts.db_table), db.db.quote_name(opts.order_with_respect_to.column))) 1023 1023 db_values.append(getattr(self, opts.order_with_respect_to.attname)) 1024 if settings.DATABASE_ENGINE == 'mssql' and opts.has_auto_field and pk_set: 1025 cursor.execute("SET IDENTITY_INSERT %s ON" % db.db.quote_name(opts.db_table)) 1024 1026 cursor.execute("INSERT INTO %s (%s) VALUES (%s)" % \ 1025 1027 (db.db.quote_name(opts.db_table), ','.join(field_names), 1026 1028 ','.join(placeholders)), db_values) … … 1103 1105 val = getattr(self, field_with_rel.attname) 1104 1106 mod = field_with_rel.rel.to.get_model_module() 1105 1107 if val is None: 1106 raise getattr(mod, '%sDoesNotExist' % field_with_rel.rel.to.object_name) 1107 other_field = field_with_rel.rel.get_related_field() 1108 if other_field.rel: 1109 params = {'%s__%s__exact' % (field_with_rel.rel.field_name, other_field.rel.field_name): val} 1108 if not field_with_rel.null: 1109 raise getattr(mod, '%sDoesNotExist' % field_with_rel.rel.to.object_name) 1110 retrieved_obj = None 1110 1111 else: 1111 params = {'%s__exact'% field_with_rel.rel.field_name: val} 1112 retrieved_obj = mod.get_object(**params) 1112 other_field = field_with_rel.rel.get_related_field() 1113 if other_field.rel: 1114 params = {'%s__%s__exact' % (field_with_rel.rel.field_name, other_field.rel.field_name): val} 1115 else: 1116 params = {'%s__exact'% field_with_rel.rel.field_name: val} 1117 retrieved_obj = mod.get_object(**params) 1113 1118 setattr(self, cache_var, retrieved_obj) 1114 1119 return getattr(self, cache_var) 1115 1120