Ticket #5246: mssql_pyodbc.2.patch
File mssql_pyodbc.2.patch, 46.2 KB (added by , 17 years ago) |
---|
-
test/utils.py
71 71 def _set_autocommit(connection): 72 72 "Make sure a connection is in autocommit mode." 73 73 if hasattr(connection.connection, "autocommit"): 74 connection.connection.autocommit(True) 74 if callable(connection.connection.autocommit): 75 connection.connection.autocommit(True) 76 else: 77 connection.connection.autocommit = True 75 78 elif hasattr(connection.connection, "set_isolation_level"): 76 79 connection.connection.set_isolation_level(0) 77 80 -
db/models/base.py
1 1 import django.db.models.manipulators 2 2 import django.db.models.manager 3 import django.db 3 4 from django.core import validators 4 5 from django.core.exceptions import ObjectDoesNotExist 5 6 from django.db.models.fields import AutoField, ImageField, FieldDoesNotExist … … 246 247 (qn(self._meta.db_table), qn(self._meta.order_with_respect_to.column))) 247 248 db_values.append(getattr(self, self._meta.order_with_respect_to.attname)) 248 249 if db_values: 249 cursor.execute("INSERT INTO %s (%s) VALUES (%s)" % \ 250 (qn(self._meta.db_table), ','.join(field_names), 251 ','.join(placeholders)), db_values) 250 try: 251 if pk_set and settings.DATABASE_ENGINE == "mssql": 252 # You can't insert an auto value into an identity column in MSSQL unless you do it explicitly 253 for f in self._meta.fields: 254 if isinstance(f, AutoField): 255 cursor.execute("SET IDENTITY_INSERT %s ON" % qn(self._meta.db_table)) 256 break 257 cursor.execute("INSERT INTO %s (%s) VALUES (%s)" % \ 258 (qn(self._meta.db_table), ','.join(field_names), ','.join(placeholders)), db_values) 259 finally: 260 if pk_set and settings.DATABASE_ENGINE == "mssql": 261 # Ensure that the table is restored to the normal state 262 try: 263 for f in self._meta.fields: 264 if isinstance(f, AutoField): 265 cursor.execute("SET IDENTITY_INSERT %s OFF" % qn(self._meta.db_table)) 266 break 267 except django.db.DatabaseError: 268 pass 252 269 else: 253 270 # Create a new record with defaults for everything. 254 271 cursor.execute("INSERT INTO %s (%s) VALUES (%s)" % -
db/models/fields/__init__.py
29 29 BLANK_CHOICE_NONE = [("", "None")] 30 30 31 31 # prepares a value for use in a LIKE query 32 prep_for_like_query = lambda x: smart_unicode(x).replace("\\", "\\\\").replace("%", "\%").replace("_", "\_") 32 # TODO: refactor database specific code 33 if settings.DATABASE_ENGINE == 'mssql': 34 # http://msdn2.microsoft.com/en-us/library/ms179859.aspx 35 prep_for_like_query = lambda x: smart_unicode(x).replace('[', '[[]').replace("%", "[%]").replace("_", "[_]") 36 else: 37 prep_for_like_query = lambda x: smart_unicode(x).replace("\\", "\\\\").replace("%", "\%").replace("_", "\_") 33 38 34 39 # returns the <ul> class for a given radio_admin value 35 40 get_ul_class = lambda x: 'radiolist%s' % ((x == HORIZONTAL) and ' inline' or '') … … 80 85 core=False, rel=None, default=NOT_PROVIDED, editable=True, serialize=True, 81 86 prepopulate_from=None, unique_for_date=None, unique_for_month=None, 82 87 unique_for_year=None, validator_list=None, choices=None, radio_admin=None, 83 help_text='', db_column=None, db_tablespace=None): 88 help_text='', db_column=None, db_tablespace=None, 89 db_collation=None): 84 90 self.name = name 85 91 self.verbose_name = verbose_name 86 92 self.primary_key = primary_key … … 102 108 self.help_text = help_text 103 109 self.db_column = db_column 104 110 self.db_tablespace = db_tablespace 105 111 # TODO: refactor database specific code 112 if settings.DATABASE_ENGINE == 'mssql': 113 self.db_collation = (db_collation and "COLLATE %s" % db_collation) or '' 114 106 115 # Set db_index to True if the field has a relationship and doesn't explicitly set db_index. 107 116 self.db_index = db_index 108 117 … … 223 232 value = int(value) 224 233 except ValueError: 225 234 raise ValueError("The __year lookup type requires an integer argument") 226 return ['%s-01-01 00:00:00' % value, '%s-12-31 23:59:59.99 9999' % value]235 return ['%s-01-01 00:00:00' % value, '%s-12-31 23:59:59.99' % value] 227 236 raise TypeError("Field has invalid lookup: %s" % lookup_type) 228 237 229 238 def has_default(self): … … 574 583 if value is not None: 575 584 # MySQL will throw a warning if microseconds are given, because it 576 585 # doesn't support microseconds. 577 if settings.DATABASE_ENGINE == 'mysql'and hasattr(value, 'microsecond'):586 if settings.DATABASE_ENGINE in ('mysql', 'ado_mssql','mssql') and hasattr(value, 'microsecond'): 578 587 value = value.replace(microsecond=0) 579 588 value = smart_unicode(value) 580 589 return Field.get_db_prep_save(self, value) 581 590 582 591 def get_db_prep_lookup(self, lookup_type, value): 592 # MSSQL doesn't like microseconds. 593 if settings.DATABASE_ENGINE in ('mysql', 'ado_mssql','mssql') and hasattr(value, 'microsecond'): 594 value = value.replace(microsecond=0) 583 595 if lookup_type == 'range': 584 596 value = [smart_unicode(v) for v in value] 585 597 else: … … 940 952 Field.__init__(self, verbose_name, name, **kwargs) 941 953 942 954 def get_db_prep_lookup(self, lookup_type, value): 943 if settings.DATABASE_ENGINE == 'oracle':955 if settings.DATABASE_ENGINE in ('oracle', 'mssql'): 944 956 # Oracle requires a date in order to parse. 945 957 def prep(value): 946 958 if isinstance(value, datetime.time): … … 966 978 # Casts dates into string format for entry into database. 967 979 if value is not None: 968 980 # MySQL will throw a warning if microseconds are given, because it 969 # doesn't support microseconds. 970 if settings.DATABASE_ENGINE == 'mysql' and hasattr(value, 'microsecond'): 981 # doesn't support microseconds. Ditto MSSQL 982 if settings.DATABASE_ENGINE in ('mysql', 'ado_mssql','mssql') \ 983 and hasattr(value, 'microsecond'): 971 984 value = value.replace(microsecond=0) 972 if settings.DATABASE_ENGINE == 'oracle':985 if settings.DATABASE_ENGINE in ('oracle', 'mssql'): 973 986 # cx_Oracle expects a datetime.datetime to persist into TIMESTAMP field. 987 # SQL Server does not have TIME type. 974 988 if isinstance(value, datetime.time): 975 989 value = datetime.datetime(1900, 1, 1, value.hour, value.minute, 976 990 value.second, value.microsecond) -
db/backends/mssql/base.py
1 """ 2 MSSQL database backend for Django. 3 4 Requires pyodbc 2.0.38 or higher (http://pyodbc.sourceforge.net/). 5 6 The configurable settings in the settings file are: 7 DATABASE_NAME - Database name. Required. 8 DATABASE_HOST - SQL Server instance in "server\instance" format. 9 DATABASE_PORT - SQL Server instance port. 10 DATABASE_USER - Database user name. If not given then the 11 Integrated Security will be used. 12 DATABASE_PASSWORD - Database user password. 13 DATABASE_ODBC_DSN - A named DSN can be used instead of DATABASE_HOST. 14 DATABASE_ODBC_DRIVER - ODBC Driver. Defalut is "{Sql Server}". 15 DATABASE_ODBC_EXTRA_PARAMS - Additional parameters for the ODBC connection. 16 The format is "param=value;param=value". 17 """ 18 19 from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, util 20 from django.core.exceptions import ImproperlyConfigured 21 from operations import DatabaseOperations 22 23 try: 24 import pyodbc as Database 25 except ImportError, e: 26 raise ImproperlyConfigured("Error loading pyodbc module: %s" % e) 27 28 version = tuple(map(int, Database.version.split('.'))) 29 if version < (2,0,38) : 30 raise ImportError("pyodbc 2.0.38 or newer is required; you have %s" % Database.version) 31 32 try: 33 # Only exists in Python 2.4+ 34 from threading import local 35 except ImportError: 36 # Import copy of _thread_local.py from Python 2.4 37 from django.utils._threading_local import local 38 39 DatabaseError = Database.DatabaseError 40 IntegrityError = Database.IntegrityError 41 42 class DatabaseFeatures(BaseDatabaseFeatures): 43 allows_group_by_ordinal = False 44 allows_unique_and_pk = True 45 autoindexes_primary_keys = True 46 needs_datetime_string_cast = True 47 needs_upper_for_iops = False 48 supports_constraints = True 49 supports_tablespaces = True 50 uses_case_insensitive_names = True 51 uses_custom_queryset = True 52 53 class DatabaseWrapper(BaseDatabaseWrapper): 54 features = DatabaseFeatures() 55 ops = DatabaseOperations() 56 57 # Collations: http://msdn2.microsoft.com/en-us/library/ms184391.aspx 58 # http://msdn2.microsoft.com/en-us/library/ms179886.aspx 59 # T-SQL LIKE: http://msdn2.microsoft.com/en-us/library/ms179859.aspx 60 # Full-Text search: http://msdn2.microsoft.com/en-us/library/ms142571.aspx 61 # CONTAINS: http://msdn2.microsoft.com/en-us/library/ms187787.aspx 62 # FREETEXT: http://msdn2.microsoft.com/en-us/library/ms176078.aspx 63 64 operators = { 65 # Since '=' is used not only for string comparision there is no way 66 # to make it case (in)sensitive. It will simply fallback to the 67 # database collation. 68 'exact': '= %s', 69 'iexact': 'LIKE %s COLLATE Latin1_General_CI_AS', 70 'contains': 'LIKE %s COLLATE Latin1_General_CS_AS', 71 'icontains': 'LIKE %s COLLATE Latin1_General_CI_AS', 72 'gt': '> %s', 73 'gte': '>= %s', 74 'lt': '< %s', 75 'lte': '<= %s', 76 'startswith': 'LIKE %s COLLATE Latin1_General_CS_AS', 77 'endswith': 'LIKE %s COLLATE Latin1_General_CS_AS', 78 'istartswith': 'LIKE %s COLLATE Latin1_General_CI_AS', 79 'iendswith': 'LIKE %s COLLATE Latin1_General_CI_AS', 80 81 # TODO: remove, keep native T-SQL LIKE wildcards support 82 # or use a "compatibility layer" and replace '*' with '%' 83 # and '.' with '_' 84 'regex': 'LIKE %s COLLATE Latin1_General_CS_AS', 85 'iregex': 'LIKE %s COLLATE Latin1_General_CI_AS', 86 87 # TODO: freetext, full-text contains... 88 } 89 90 def __init__(self, autocommit=False, **kwargs): 91 super(DatabaseWrapper, self).__init__(autocommit=autocommit, **kwargs) 92 self.connection = None 93 self.queries = [] 94 95 def cursor(self): 96 from django.conf import settings 97 if self.connection is None: 98 if settings.DATABASE_NAME == '': 99 raise ImproperlyConfigured("You need to specify DATABASE_NAME in your Django settings file.") 100 101 if not settings.DATABASE_HOST and not hasattr(settings, "DATABASE_ODBC_DSN"): 102 raise ImproperlyConfigured("You need to specify DATABASE_HOST or DATABASE_ODBC_DSN in your Django settings file.") 103 104 if settings.DATABASE_PORT: 105 host_str = '%s:%s' % ( settings.DATABASE_HOST ,settings.DATABASE_PORT) 106 else: 107 host_str = settings.DATABASE_HOST 108 109 if hasattr(settings, "DATABASE_ODBC_DRIVER"): 110 odbc_driver = settings.DATABASE_ODBC_DRIVER 111 else: 112 odbc_driver = "{Sql Server}" 113 114 odbc_string = "Driver=%s;" % (odbc_driver) 115 116 if hasattr(settings, "DATABASE_ODBC_DSN"): 117 odbc_string += "DSN=%s;" % settings.DATABASE_ODBC_DSN 118 else: 119 odbc_string += "Server=%s;" % host_str 120 121 if settings.DATABASE_USER: 122 odbc_string += "Uid=%s;Pwd=%s;" % (settings.DATABASE_USER, settings.DATABASE_PASSWORD) 123 else: 124 odbc_string += "Integrated Security=SSPI;" 125 126 odbc_string += "Database=%s" % settings.DATABASE_NAME 127 128 if hasattr(settings, "DATABASE_ODBC_EXTRA_PARAMS"): 129 odbc_string += ";" + settings.DATABASE_ODBC_EXTRA_PARAMS 130 131 self.connection = Database.connect(odbc_string, self.options["autocommit"]) 132 133 self.connection.cursor().execute("SET DATEFORMAT ymd") 134 135 cursor = CursorWrapper(self.connection.cursor()) 136 if settings.DEBUG: 137 return util.CursorDebugWrapper(cursor, self) 138 return cursor 139 140 class CursorWrapper(object): 141 """ 142 A wrapper around the pyodbc cursor that: 143 1. Converts input strings to unicde. 144 2. Replaces '%s' parameter placeholder in sql queries to '?' (pyodbc specific). 145 """ 146 def __init__(self, cursor): 147 self.cursor = cursor 148 149 def format_params(self, params, encoding='utf-8', errors='strict'): 150 new_params = [] 151 for param in params: 152 if isinstance(param, str): 153 # Ensure that plain strings are converted to unicode using proper encoding. 154 # Assumed input encoding is 'utf-8' 155 # TODO: Verify this with upper layers 156 param = unicode(param, encoding, errors) 157 new_params.append(param) 158 return tuple(new_params) 159 160 def format_sql(self, sql): 161 # pyodbc uses '?' instead of '%s' as parameter placeholder. 162 if "%s" in sql: 163 sql = sql.replace('%s', '?') 164 return sql 165 166 def execute(self, sql, params=()): 167 if params: 168 params = self.format_params(params) 169 sql = self.format_sql(sql) 170 return self.cursor.execute(sql, params) 171 172 def executemany(self, sql, param_list): 173 if param_list: 174 param_list = [self.format_params(params) for params in param_list] 175 sql = self.format_sql(sql) 176 return self.cursor.executemany(sql, param_list) 177 178 def fetchone(self): 179 row = self.cursor.fetchone() 180 if row is not None: 181 # Convert row to tuple (pyodbc Rows are not sliceable). 182 return tuple(row) 183 return row 184 185 def fetchmany(self, chunk): 186 return [tuple(row) for row in self.cursor.fetchmany(chunk)] 187 188 def fetchall(self): 189 return [tuple(row) for row in self.cursor.fetchall()] 190 191 def __getattr__(self, attr): 192 if attr in self.__dict__: 193 return self.__dict__[attr] 194 else: 195 return getattr(self.cursor, attr) -
db/backends/mssql/client.py
1 from django.conf import settings 2 import os 3 import sys 4 5 def runshell(): 6 if os.name=='nt': 7 db = settings.DATABASE_OPTIONS.get('db', settings.DATABASE_NAME) 8 user = settings.DATABASE_OPTIONS.get('user', settings.DATABASE_USER) 9 passwdord = settings.DATABASE_OPTIONS.get('passwd', settings.DATABASE_PASSWORD) 10 server = settings.DATABASE_OPTIONS.get('host', settings.DATABASE_HOST) 11 port = settings.DATABASE_OPTIONS.get('port', settings.DATABASE_PORT) 12 defaults_file = settings.DATABASE_OPTIONS.get('read_default_file') 13 14 args = ['osql'] 15 if server: 16 args += ["-S", server] 17 if user: 18 args += ["-U", user] 19 if passwdord: 20 args += ["-P", passwdord] 21 else: 22 args += ["-E"] # Try trusted connection instead 23 if db: 24 args += ["-d", db] 25 if defaults_file: 26 args += ["-i", defaults_file] 27 28 import subprocess 29 try: 30 retcode = subprocess.call(args, shell=True) 31 if retcode: 32 print >>sys.stderr, "error level:", retcode 33 sys.exit(retcode) 34 except KeyboardInterrupt: 35 pass 36 except OSError, e: 37 print >> sys.stderr, "Execution failed:", e 38 else: 39 raise NotImplementedError -
db/backends/mssql/__init__.py
1 # Placeholder -
db/backends/mssql/introspection.py
1 try: 2 import pyodbc as Database 3 except ImportError, e: 4 raise ImproperlyConfigured, "Error loading pyodbc module: %s" % e 5 6 SQL_AUTOFIELD = -777555 7 8 def get_table_list(cursor): 9 """ 10 Returns a list of table names in the current database. 11 """ 12 # TABLES: http://msdn2.microsoft.com/en-us/library/ms186224.aspx 13 14 cursor.execute("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE'") 15 return [row[0] for row in cursor.fetchall()] 16 17 # Or pyodbc specific: 18 #return [row[2] for row in cursor.tables(tableType='TABLE')] 19 20 def _is_auto_field(cursor, table_name, column_name): 21 """ 22 Checks whether column is Identity 23 """ 24 # COLUMNPROPERTY: http://msdn2.microsoft.com/en-us/library/ms174968.aspx 25 26 from django.db import connection 27 cursor.execute("SELECT COLUMNPROPERTY(OBJECT_ID(%s), %s, 'IsIdentity')", 28 (connection.ops.quote_name(table_name), column_name)) 29 return cursor.fetchall()[0][0] 30 31 def get_table_description(cursor, table_name, identity_check=True): 32 """Returns a description of the table, with DB-API cursor.description interface. 33 34 The 'auto_check' parameter has been added to the function argspec. 35 If set to True, the function will check each of the table's fields for the 36 IDENTITY property (the IDENTITY property is the MSSQL equivalent to an AutoField). 37 38 When a field is found with an IDENTITY property, it is given a custom field number 39 of SQL_AUTOFIELD, which maps to the 'AutoField' value in the DATA_TYPES_REVERSE dict. 40 """ 41 42 # map pyodbc's cursor.columns to db-api cursor description 43 columns = [[c[3], c[4], None, c[6], c[6], c[8], c[10]] for c in cursor.columns(table=table_name)] 44 items = [] 45 for column in columns: 46 if identity_check and _is_auto_field(cursor, table_name, column[0]): 47 column[1] = SQL_AUTOFIELD 48 if column[1] == Database.SQL_WVARCHAR and column[3] < 4000: 49 column[1] = Database.SQL_WCHAR 50 items.append(column) 51 return items 52 53 def _name_to_index(cursor, table_name): 54 """ 55 Returns a dictionary of {field_name: field_index} for the given table. 56 Indexes are 0-based. 57 """ 58 return dict([(d[0], i) for i, d in enumerate(get_table_description(cursor, table_name, identity_check=False))]) 59 60 def get_relations(cursor, table_name): 61 """ 62 Returns a dictionary of {field_index: (field_index_other_table, other_table)} 63 representing all relationships to the given table. Indexes are 0-based. 64 """ 65 # CONSTRAINT_COLUMN_USAGE: http://msdn2.microsoft.com/en-us/library/ms174431.aspx 66 # CONSTRAINT_TABLE_USAGE: http://msdn2.microsoft.com/en-us/library/ms179883.aspx 67 # REFERENTIAL_CONSTRAINTS: http://msdn2.microsoft.com/en-us/library/ms179987.aspx 68 # TABLE_CONSTRAINTS: http://msdn2.microsoft.com/en-us/library/ms181757.aspx 69 70 table_index = _name_to_index(cursor, table_name) 71 sql = """ 72 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 = %s AND a.CONSTRAINT_TYPE = 'FOREIGN KEY' 85 """ 86 cursor.execute(sql, (table_name,)) 87 return dict([(table_index[item[0]], (_name_to_index(cursor, item[1])[item[2]], item[1])) 88 for item in cursor.fetchall()]) 89 90 def get_indexes(cursor, table_name): 91 """ 92 Returns a dictionary of fieldname -> infodict for the given table, 93 where each infodict is in the format: 94 {'primary_key': boolean representing whether it's the primary key, 95 'unique': boolean representing whether it's a unique index, 96 'db_index': boolean representing whether it's a non-unique index} 97 """ 98 # CONSTRAINT_COLUMN_USAGE: http://msdn2.microsoft.com/en-us/library/ms174431.aspx 99 # TABLE_CONSTRAINTS: http://msdn2.microsoft.com/en-us/library/ms181757.aspx 100 101 pk_uk_sql = """ 102 SELECT b.COLUMN_NAME, a.CONSTRAINT_TYPE 103 FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS a 104 INNER JOIN INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE AS b 105 ON a.CONSTRAINT_NAME = b.CONSTRAINT_NAME AND a.TABLE_NAME = b.TABLE_NAME 106 WHERE a.TABLE_NAME = %s AND (CONSTRAINT_TYPE = 'PRIMARY KEY' OR CONSTRAINT_TYPE = 'UNIQUE') 107 108 """ 109 # non-unique, non-compound indexes 110 ix_sql = """ 111 SELECT DISTINCT c.name 112 FROM sys.columns c 113 INNER JOIN sys.index_columns ic 114 ON ic.object_id = c.object_id AND ic.column_id = c.column_id 115 INNER JOIN sys.indexes ix 116 ON ix.object_id = ic.object_id AND ix.index_id = ic.index_id 117 INNER JOIN sys.tables t 118 ON t.object_id = ix.object_id 119 WHERE ix.object_id IN ( 120 SELECT ix.object_id 121 FROM sys.indexes ix 122 GROUP BY ix.object_id, ix.index_id 123 HAVING count(1) = 1) 124 AND ix.is_primary_key = 0 125 AND ix.is_unique_constraint = 0 126 AND t.name = %s 127 """ 128 129 field_names = [item[0] for item in get_table_description(cursor, table_name, identity_check=False)] 130 indexes, results = {}, {} 131 cursor.execute(pk_uk_sql, (table_name,)) 132 data = cursor.fetchall() 133 if data: 134 results.update(data) 135 136 # TODO: how should this import look like? from db .. import .. ? 137 from operations import sql_server_version, SQL_SERVER_2005_VERSION 138 if sql_server_version() >= SQL_SERVER_2005_VERSION: 139 cursor.execute(ix_sql, (table_name,)) 140 for column in [r[0] for r in cursor.fetchall()]: 141 if column not in results: 142 results[column] = 'IX' 143 144 for field in field_names: 145 val = results.get(field, None) 146 indexes[field] = dict(primary_key=(val=='PRIMARY KEY'), unique=(val=='UNIQUE'), db_index=(val=='IX')) 147 148 return indexes 149 150 def get_collations_list(cursor): 151 """ 152 Returns list of available collations and theirs descriptions. 153 """ 154 # http://msdn2.microsoft.com/en-us/library/ms184391.aspx 155 # http://msdn2.microsoft.com/en-us/library/ms179886.aspx 156 157 cursor.execute("SELECT name, description FROM ::fn_helpcollations()") 158 return [tuple(row) for row in cursor.fetchall()] 159 160 DATA_TYPES_REVERSE = { 161 SQL_AUTOFIELD: 'AutoField', 162 Database.SQL_BIGINT: 'IntegerField', 163 #Database.SQL_BINARY: , 164 Database.SQL_BIT: 'BooleanField', 165 Database.SQL_CHAR: 'CharField', 166 Database.SQL_DECIMAL: 'DecimalField', 167 Database.SQL_DOUBLE: 'FloatField', 168 Database.SQL_FLOAT: 'FloatField', 169 Database.SQL_GUID: 'TextField', 170 Database.SQL_INTEGER: 'IntegerField', 171 #Database.SQL_LONGVARBINARY: , 172 #Database.SQL_LONGVARCHAR: , 173 Database.SQL_NUMERIC: 'DecimalField', 174 Database.SQL_REAL: 'FloatField', 175 Database.SQL_SMALLINT: 'SmallIntegerField', 176 Database.SQL_TINYINT: 'SmallIntegerField', 177 Database.SQL_TYPE_DATE: 'DateField', 178 Database.SQL_TYPE_TIME: 'TimeField', 179 Database.SQL_TYPE_TIMESTAMP: 'DateTimeField', 180 #Database.SQL_VARBINARY: , 181 Database.SQL_VARCHAR: 'TextField', 182 Database.SQL_WCHAR: 'CharField', 183 Database.SQL_WLONGVARCHAR: 'TextField', 184 Database.SQL_WVARCHAR: 'TextField', 185 } -
db/backends/mssql/operations.py
1 from django.db.backends import BaseDatabaseOperations, util 2 from django.utils.datastructures import SortedDict 3 4 # Version specific queries 5 6 # LIMIT and OFFSET are turned into inlcusive range 7 # start_row = offset + 1 8 # end_row = offset + limit 9 SQL_SERVER_9_LIMIT_QUERY = \ 10 """ 11 SELECT * 12 FROM ( 13 SELECT %(distinc)s TOP %(end_row)s 14 %(fields)s, ROW_NUMBER() 15 OVER( 16 ORDER BY %(orderby)s 17 ) AS row 18 %(sql)s ORDER BY %(orderby)s 19 ) AS x 20 WHERE x.row BETWEEN %(start_row)s AND %(end_row)s 21 """ 22 23 # end_row = offset + limit -- upper range 24 # limit -- chunk size 25 SQL_SERVER_8_LIMIT_QUERY = \ 26 """ 27 SELECT * FROM ( 28 SELECT TOP %(limit)s * FROM ( 29 SELECT TOP %(end_row)s %(distinc)s%(fields)s 30 %(sql)s 31 ORDER BY %(orderby)s 32 ) AS %(table)s 33 ORDER BY %(orderby_reversed)s) AS %(table)s 34 ORDER BY %(orderby)s 35 """ 36 37 ORDER_ASC = "ASC" 38 ORDER_DESC = "DESC" 39 40 SQL_SERVER_2005_VERSION = 9 41 SQL_SERVER_VERSION = None 42 43 def sql_server_version(): 44 """ 45 Returns the major version of the SQL Server: 46 2000 -> 8 47 2005 -> 9 48 """ 49 global SQL_SERVER_VERSION 50 if SQL_SERVER_VERSION is not None: 51 return SQL_SERVER_VERSION 52 else: 53 from django.db import connection 54 cur = connection.cursor() 55 cur.execute("SELECT cast(SERVERPROPERTY('ProductVersion') as varchar)") 56 SQL_SERVER_VERSION = int(cur.fetchone()[0].split('.')[0]) 57 return SQL_SERVER_VERSION 58 59 class DatabaseOperations(BaseDatabaseOperations): 60 def last_insert_id(self, cursor, table_name, pk_name): 61 # TODO: Check how the `last_insert_id` is being used in the upper layers 62 # in context of multithreaded access, compare with other backends 63 64 # IDENT_CURRENT: http://msdn2.microsoft.com/en-us/library/ms175098.aspx 65 # SCOPE_IDENTITY: http://msdn2.microsoft.com/en-us/library/ms190315.aspx 66 # @@IDENTITY: http://msdn2.microsoft.com/en-us/library/ms187342.aspx 67 68 # IDENT_CURRENT is not limited by scope and session; it is limited to 69 # a specified table. IDENT_CURRENT returns the value generated for 70 # a specific table in any session and any scope. 71 # SCOPE_IDENTITY and @@IDENTITY return the last identity values that 72 # are generated in any table in the current session. However, 73 # SCOPE_IDENTITY returns values inserted only within the current scope; 74 # @@IDENTITY is not limited to a specific scope. 75 76 table_name = self.quote_name(table_name) 77 pk_name = self.quote_name(pk_name) 78 #cursor.execute("SELECT %s FROM %s WHERE %s = IDENT_CURRENT(%%s)" % (pk_name, table_name, pk_name), [table_name]) 79 cursor.execute("SELECT CAST(IDENT_CURRENT(%s) as int)", [table_name]) 80 return cursor.fetchone()[0] 81 82 def query_set_class(self, DefaultQuerySet): 83 "Create a custom QuerySet class for SQL Server." 84 from django.db import connection 85 from django.db.models.query import EmptyResultSet, GET_ITERATOR_CHUNK_SIZE, quote_only_if_word 86 87 class SqlServerQuerySet(DefaultQuerySet): 88 89 def iterator(self): 90 "Performs the SELECT database lookup of this QuerySet." 91 92 from django.db.models.query import get_cached_row 93 94 # self._select is a dictionary, and dictionaries' key order is 95 # undefined, so we convert it to a list of tuples. 96 extra_select = self._select.items() 97 98 full_query = None 99 100 try: 101 try: 102 select, sql, params, full_query = self._get_sql_clause(get_full_query=True) 103 except TypeError: 104 select, sql, params = self._get_sql_clause() 105 except EmptyResultSet: 106 raise StopIteration 107 if not full_query: 108 full_query = "SELECT %s%s\n%s" % ((self._distinct and "DISTINCT " or ""), ', '.join(select), sql) 109 110 cursor = connection.cursor() 111 cursor.execute(full_query, params) 112 113 fill_cache = self._select_related 114 fields = self.model._meta.fields 115 index_end = len(fields) 116 117 while 1: 118 rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE) 119 if not rows: 120 raise StopIteration 121 for row in rows: 122 row = self.resolve_columns(row, fields) 123 if fill_cache: 124 obj, index_end = get_cached_row(klass=self.model, row=row, 125 index_start=0, max_depth=self._max_related_depth) 126 else: 127 obj = self.model(*row[:index_end]) 128 for i, k in enumerate(extra_select): 129 setattr(obj, k[0], row[index_end+i]) 130 yield obj 131 132 def _get_sql_clause(self, get_full_query=False): 133 from django.db.models.query import fill_table_cache, \ 134 handle_legacy_orderlist, orderfield2column 135 136 opts = self.model._meta 137 qn = connection.ops.quote_name 138 139 # Construct the fundamental parts of the query: SELECT X FROM Y WHERE Z. 140 select = ["%s.%s" % (qn(opts.db_table), qn(f.column)) for f in opts.fields] 141 # TODO: quote_only_if_word? 142 tables = [quote_only_if_word(t) for t in self._tables] 143 joins = SortedDict() 144 where = self._where[:] 145 params = self._params[:] 146 147 # Convert self._filters into SQL. 148 joins2, where2, params2 = self._filters.get_sql(opts) 149 joins.update(joins2) 150 where.extend(where2) 151 params.extend(params2) 152 153 # Add additional tables and WHERE clauses based on select_related. 154 if self._select_related: 155 fill_table_cache(opts, select, tables, where, opts.db_table, [opts.db_table]) 156 157 # Add any additional SELECTs. 158 # TODO: quote_only_if_word? 159 if self._select: 160 select.extend(['(%s) AS %s' % (quote_only_if_word(s[1]), qn(s[0])) for s in self._select.items()]) 161 162 # Start composing the body of the SQL statement. 163 sql = [" FROM", qn(opts.db_table)] 164 165 # Compose the join dictionary into SQL describing the joins. 166 if joins: 167 sql.append(" ".join(["%s %s %s ON %s" % (join_type, table, alias, condition) 168 for (alias, (table, join_type, condition)) in joins.items()])) 169 170 # Compose the tables clause into SQL. 171 if tables: 172 sql.append(", " + ", ".join(tables)) 173 174 # Compose the where clause into SQL. 175 if where: 176 sql.append(where and "WHERE " + " AND ".join(where)) 177 178 # Copy version suitable for LIMIT 179 sql2 = sql[:] 180 181 # ORDER BY clause 182 order_by = [] 183 if self._order_by is not None: 184 ordering_to_use = self._order_by 185 else: 186 ordering_to_use = opts.ordering 187 for f in handle_legacy_orderlist(ordering_to_use): 188 if f == '?': # Special case. 189 order_by.append(connection.ops.get_random_function_sql()) 190 else: 191 if f.startswith('-'): 192 col_name = f[1:] 193 order = ORDER_DESC 194 else: 195 col_name = f 196 order = ORDER_ASC 197 if "." in col_name: 198 table_prefix, col_name = col_name.split('.', 1) 199 table_prefix = qn(table_prefix) + '.' 200 else: 201 # Use the database table as a column prefix if it wasn't given, 202 # and if the requested column isn't a custom SELECT. 203 if "." not in col_name and col_name not in (self._select or ()): 204 table_prefix = qn(opts.db_table) + '.' 205 else: 206 table_prefix = '' 207 order_by.append('%s%s %s' % (table_prefix, qn(orderfield2column(col_name, opts)), order)) 208 if order_by: 209 sql.append("ORDER BY " + ", ".join(order_by)) 210 211 # Look for column name collisions in the select elements 212 # and fix them with an AS alias. This allows us to do a 213 # SELECT * later in the paging query. 214 cols = [clause.split('.')[-1] for clause in select] 215 for index, col in enumerate(cols): 216 if cols.count(col) > 1: 217 col = '%s%d' % (col.replace('[', '').replace(']',''), index) 218 cols[index] = qn(col) 219 select[index] = '%s AS %s' % (select[index], qn(col)) 220 221 # LIMIT and OFFSET clauses 222 # To support limits and offsets, SQL Server requires some funky rewriting of an otherwise normal looking query. 223 select_clause = ",".join(select) 224 distinct = (self._distinct and "DISTINCT " or "") 225 full_query = None 226 227 # offset: start row (zero indexed) 228 # limit: chunk size 229 230 if self._limit is None: 231 assert self._offset is None, "'offset' is not allowed without 'limit'" # TODO: actually, why not? 232 233 if self._limit is not None: 234 limit = int(self._limit) 235 else: 236 limit = None 237 238 if self._offset is not None and limit > 0: 239 offset = int(self._offset) 240 else: 241 offset = 0 242 243 limit_and_offset_clause = '' 244 245 if limit is not None: 246 limit_and_offset_clause = True 247 elif offset: 248 limit_and_offset_clause = True 249 250 if limit_and_offset_clause: 251 # TOP and ROW_NUMBER in T-SQL requires an order. 252 # If order is not specified the use id column. 253 if len(order_by)==0: 254 order_by.append('%s.%s %s' % (qn(opts.db_table), qn(opts.fields[0].db_column or opts.fields[0].column), ORDER_ASC)) 255 256 order_by_clause = ", ".join(order_by) 257 order_by_clause_reverse = "" 258 259 if sql_server_version() >= SQL_SERVER_2005_VERSION: 260 fmt = SQL_SERVER_9_LIMIT_QUERY 261 else: 262 # Compatibility mode for older versions 263 order_by_clause_reverse = ", ".join(self.change_order_direction(order_by)) 264 fmt = SQL_SERVER_8_LIMIT_QUERY 265 266 full_query = fmt % {'distinc': distinct, 'fields': select_clause, 267 'sql': " ".join(sql2), 'orderby': order_by_clause, 268 'orderby_reversed': order_by_clause_reverse, 269 'table': qn(opts.db_table), 270 'limit': limit, 271 'start_row': offset + 1, 'end_row': offset + limit} 272 if get_full_query: 273 return select, " ".join(sql), params, full_query 274 else: 275 return select, " ".join(sql), params 276 277 def change_order_direction(self, order_by): 278 new_order = [] 279 for order in order_by: 280 if order.endswith(ORDER_ASC): 281 new_order.append(order[:-len(ORDER_ASC)] + ORDER_DESC) 282 elif order.endswith(ORDER_DESC): 283 new_order.append(order[:-len(ORDER_DESC)] + ORDER_ASC) 284 else: 285 # TODO: check special case '?' -- random order 286 new_order.append(order) 287 return new_order 288 289 def resolve_columns(self, row, fields=()): 290 from django.db.models.fields import DateField, DateTimeField, \ 291 TimeField, DecimalField 292 values = [] 293 for value, field in map(None, row, fields): 294 if value is not None: 295 if isinstance(field, DateTimeField): 296 pass # do nothing 297 elif isinstance(field, DateField): 298 value = value.date() # extract date 299 elif isinstance(field, TimeField): 300 value = value.time() # extract time 301 values.append(value) 302 return values 303 304 return SqlServerQuerySet 305 306 def date_extract_sql(self, lookup_type, field_name): 307 """ 308 Given a lookup_type of 'year', 'month' or 'day', returns the SQL that 309 extracts a value from the given date field field_name. 310 """ 311 return "DATEPART(%s, %s)" % (lookup_type, field_name) 312 313 def date_trunc_sql(self, lookup_type, field_name): 314 """ 315 Given a lookup_type of 'year', 'month' or 'day', returns the SQL that 316 truncates the given date field field_name to a DATE object with only 317 the given specificity. 318 """ 319 if lookup_type=='year': 320 return "Convert(datetime, Convert(varchar, DATEPART(year, %s)) + '/01/01')" % field_name 321 if lookup_type=='month': 322 return "Convert(datetime, Convert(varchar, DATEPART(year, %s)) + '/' + Convert(varchar, DATEPART(month, %s)) + '/01')" % (field_name, field_name) 323 if lookup_type=='day': 324 return "Convert(datetime, Convert(varchar(12), %s))" % field_name 325 326 def limit_offset_sql(self, limit, offset=None): 327 # Limits and offset are too complicated to be handled here. 328 # Look for a implementation similar to SQL Server backend 329 return "" 330 331 def quote_name(self, name): 332 """ 333 Returns a quoted version of the given table, index or column name. Does 334 not quote the given name if it's already been quoted. 335 """ 336 if name.startswith('[') and name.endswith(']'): 337 return name # Quoting once is enough. 338 return '[%s]' % name 339 340 def get_random_function_sql(self): 341 """ 342 Returns a SQL expression that returns a random value. 343 """ 344 return "RAND()" 345 346 def tablespace_sql(self, tablespace, inline=False): 347 """ 348 Returns the tablespace SQL, or None if the backend doesn't use 349 tablespaces. 350 """ 351 return "ON %s" % self.quote_name(tablespace) 352 353 def sql_flush(self, style, tables, sequences): 354 """ 355 Returns a list of SQL statements required to remove all data from 356 the given database tables (without actually removing the tables 357 themselves). 358 359 The `style` argument is a Style object as returned by either 360 color_style() or no_style() in django.core.management.color. 361 """ 362 # Cannot use TRUNCATE on tables that are referenced by a FOREIGN KEY 363 # So must use the much slower DELETE 364 from django.db import connection 365 cursor = connection.cursor() 366 cursor.execute("SELECT TABLE_NAME, CONSTRAINT_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS") 367 fks = cursor.fetchall() 368 sql_list = ['ALTER TABLE %s NOCHECK CONSTRAINT %s;' % \ 369 (self.quote_name(fk[0]), self.quote_name(fk[1])) for fk in fks] 370 sql_list.extend(['%s %s %s;' % (style.SQL_KEYWORD('DELETE'), style.SQL_KEYWORD('FROM'), 371 style.SQL_FIELD(self.quote_name(table)) ) for table in tables]) 372 # The reset the counters on each table. 373 sql_list.extend(['%s %s (%s, %s, %s) %s %s;' % ( 374 style.SQL_KEYWORD('DBCC'), 375 style.SQL_KEYWORD('CHECKIDENT'), 376 style.SQL_FIELD(self.quote_name(seq["table"])), 377 style.SQL_KEYWORD('RESEED'), 378 style.SQL_FIELD('1'), 379 style.SQL_KEYWORD('WITH'), 380 style.SQL_KEYWORD('NO_INFOMSGS'), 381 ) for seq in sequences]) 382 sql_list.extend(['ALTER TABLE %s CHECK CONSTRAINT %s;' % \ 383 (self.quote_name(fk[0]), self.quote_name(fk[1])) for fk in fks]) 384 return sql_list 385 386 def start_transaction_sql(self): 387 """ 388 Returns the SQL statement required to start a transaction. 389 """ 390 return "BEGIN TRANSACTION" -
db/backends/mssql/creation.py
1 DATA_TYPES = { 2 'AutoField': 'int IDENTITY (1, 1)', 3 'BooleanField': 'bit', 4 'CharField': 'nvarchar(%(max_length)s) %(db_collation)s', 5 'CommaSeparatedIntegerField': 'nvarchar(%(max_length)s) %(db_collation)s', 6 'DateField': 'datetime', 7 'DateTimeField': 'datetime', 8 'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)', 9 'FileField': 'nvarchar(254) %(db_collation)s', 10 'FilePathField': 'nvarchar(254) %(db_collation)s', 11 'FloatField': 'double precision', 12 'ImageField': 'nvarchar(254) %(db_collation)s', 13 'IntegerField': 'int', 14 'IPAddressField': 'nvarchar(15)', 15 'ManyToManyField': None, 16 'NullBooleanField': 'bit', 17 'OneToOneField': 'int', 18 'PhoneNumberField': 'nvarchar(20) %(db_collation)s', 19 #The check must be unique in for the database. Put random so the regresion test not complain about duplicate names 20 'PositiveIntegerField': 'int CONSTRAINT [CK_int_pos_%(creation_counter)s_%(column)s] CHECK ([%(column)s] > 0)', 21 'PositiveSmallIntegerField': 'smallint CONSTRAINT [CK_smallint_pos_%(creation_counter)s_%(column)s] CHECK ([%(column)s] > 0)', 22 'SlugField': 'nvarchar(%(max_length)s) %(db_collation)s', 23 'SmallIntegerField': 'smallint', 24 'TextField': 'ntext %(db_collation)s', 25 'TimeField': 'datetime', 26 'USStateField': 'nchar(2) %(db_collation)s', 27 } 28 29 # TODO: how should this import look like? from db .. import .. 30 from operations import sql_server_version, SQL_SERVER_2005_VERSION 31 if sql_server_version() >= SQL_SERVER_2005_VERSION: 32 DATA_TYPES['TextField'] = 'nvarchar(max) %(db_collation)s' -
contrib/sessions/middleware.py
61 61 self._session_cache = {} 62 62 else: 63 63 try: 64 datenow = datetime.datetime.now() 65 if hasattr(datenow, 'microsecond'): 66 datenow = datenow.replace(microsecond=0) 64 67 s = Session.objects.get(session_key=self.session_key, 65 expire_date__gt=date time.datetime.now())68 expire_date__gt=datenow) 66 69 self._session_cache = s.get_decoded() 67 70 except (Session.DoesNotExist, SuspiciousOperation): 68 71 self._session_cache = {}