99import time
1010import struct
1111import datetime
12+ from decimal import Decimal
13+ from uuid import UUID
1214
1315from django .core .exceptions import ImproperlyConfigured
1416from django .utils .functional import cached_property
@@ -124,7 +126,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
124126 'SmallIntegerField' : 'smallint' ,
125127 'TextField' : 'nvarchar(max)' ,
126128 'TimeField' : 'time' ,
127- 'UUIDField' : 'char(32) ' ,
129+ 'UUIDField' : 'uniqueidentifier ' ,
128130 }
129131 data_types_suffix = {
130132 'AutoField' : 'IDENTITY (1, 1)' ,
@@ -376,7 +378,6 @@ def get_new_connection(self, conn_params):
376378 break
377379 if not need_to_retry :
378380 raise
379-
380381 # Handling values from DATETIMEOFFSET columns
381382 # source: https://github.com/mkleehammer/pyodbc/wiki/Using-an-Output-Converter-function
382383 conn .add_output_converter (SQL_TIMESTAMP_WITH_TIMEZONE , handle_datetimeoffset )
@@ -431,6 +432,9 @@ def init_connection_state(self):
431432 if (options .get ('return_rows_bulk_insert' , False )):
432433 self .features_class .can_return_rows_from_bulk_insert = True
433434
435+ if (options .get ('has_native_uuid_field' , True )):
436+ Database .native_uuid = True
437+
434438 val = self .get_system_datetime
435439 if isinstance (val , str ):
436440 raise ImproperlyConfigured (
@@ -569,6 +573,36 @@ def __init__(self, cursor, connection):
569573 self .last_sql = ''
570574 self .last_params = ()
571575
576+ def _as_sql_type (self , typ , value ):
577+ if isinstance (value , str ):
578+ length = len (value )
579+ if length == 0 :
580+ return 'NVARCHAR'
581+ elif length > 4000 :
582+ return 'NVARCHAR(max)'
583+ return 'NVARCHAR(%s)' % len (value )
584+ elif typ == int :
585+ if value < 0x7FFFFFFF and value > - 0x7FFFFFFF :
586+ return 'INT'
587+ else :
588+ return 'BIGINT'
589+ elif typ == float :
590+ return 'DOUBLE PRECISION'
591+ elif typ == bool :
592+ return 'BIT'
593+ elif isinstance (value , Decimal ):
594+ return 'NUMERIC'
595+ elif isinstance (value , datetime .datetime ):
596+ return 'DATETIME2'
597+ elif isinstance (value , datetime .date ):
598+ return 'DATE'
599+ elif isinstance (value , datetime .time ):
600+ return 'TIME'
601+ elif isinstance (value , UUID ):
602+ return 'uniqueidentifier'
603+ else :
604+ raise NotImplementedError ('Not supported type %s (%s)' % (type (value ), repr (value )))
605+
572606 def close (self ):
573607 if self .active :
574608 self .active = False
@@ -586,6 +620,27 @@ def format_sql(self, sql, params):
586620
587621 return sql
588622
623+ def format_group_by_params (self , query , params ):
624+ if params :
625+ # Insert None params directly into the query
626+ if None in params :
627+ null_params = ['NULL' if param is None else '%s' for param in params ]
628+ query = query % tuple (null_params )
629+ params = tuple (p for p in params if p is not None )
630+ params = [(param , type (param )) for param in params ]
631+ params_dict = {param : '@var%d' % i for i , param in enumerate (set (params ))}
632+ args = [params_dict [param ] for param in params ]
633+
634+ variables = []
635+ params = []
636+ for key , value in params_dict .items ():
637+ datatype = self ._as_sql_type (key [1 ], key [0 ])
638+ variables .append ("%s %s = %%s " % (value , datatype ))
639+ params .append (key [0 ])
640+ query = ('DECLARE %s \n ' % ',' .join (variables )) + (query % tuple (args ))
641+
642+ return query , params
643+
589644 def format_params (self , params ):
590645 fp = []
591646 if params is not None :
@@ -614,6 +669,8 @@ def format_params(self, params):
614669
615670 def execute (self , sql , params = None ):
616671 self .last_sql = sql
672+ if 'GROUP BY' in sql :
673+ sql , params = self .format_group_by_params (sql , params )
617674 sql = self .format_sql (sql , params )
618675 params = self .format_params (params )
619676 self .last_params = params
0 commit comments