Ticket #1429: adodbapi-from-0.91.patch

File adodbapi-from-0.91.patch, 19.5 KB (added by anonymous, 18 years ago)
  • django/core/db/backends/mssql.py

    === django/core/db/backends/mssql.py
    ==================================================================
     
     1"""
     2SQL Server database backend for Django.
     3
     4Requires ADODBAPI: http://adodbapi.sf.net
     5"""
     6
     7import re
     8
     9from django.core.db import base
     10from django.core.db.dicthelpers import *
     11import pymssql as Database
     12from itertools import repeat
     13
     14DatabaseError = Database.DatabaseError
     15
     16class 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
     56class 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
     94def 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
     98def 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
     102def 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
     111def 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
     117def get_random_function_sql():
     118    return 'rand()'
     119
     120def get_table_list(cursor):
     121    cursor.execute("select name from sysobjects where type='U'")
     122    return [row[0] for row in cursor.fetchall()]
     123
     124def get_table_description(cursor, table_name):
     125    raise NotImplementedError
     126
     127def get_relations(cursor, table_name):
     128    raise NotImplementedError
     129
     130OPERATOR_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
     146def 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
     152SELECT
     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'
     161FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS RC
     162JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU1
     163ON KCU1.CONSTRAINT_CATALOG = RC.CONSTRAINT_CATALOG
     164   AND KCU1.CONSTRAINT_SCHEMA = RC.CONSTRAINT_SCHEMA
     165   AND KCU1.CONSTRAINT_NAME = RC.CONSTRAINT_NAME
     166JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU2
     167ON KCU2.CONSTRAINT_CATALOG =
     168RC.UNIQUE_CONSTRAINT_CATALOG
     169   AND KCU2.CONSTRAINT_SCHEMA =
     170RC.UNIQUE_CONSTRAINT_SCHEMA
     171   AND KCU2.CONSTRAINT_NAME =
     172RC.UNIQUE_CONSTRAINT_NAME
     173   AND KCU2.ORDINAL_POSITION = KCU1.ORDINAL_POSITION
     174
     175    """
     176
     177def 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.
     186DATA_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
     214DATA_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
    ==================================================================
     
    11"""
    2 ADO MSSQL database backend for Django.
     2SQL Server database backend for Django.
    33
    4 Requires adodbapi 2.0.1: http://adodbapi.sourceforge.net/
     4Requires ADODBAPI: http://adodbapi.sf.net
    55"""
    66
     7import re
     8import threading
     9
    710from django.core.db import base
    811from django.core.db.dicthelpers import *
    912import adodbapi as Database
    10 import datetime
    11 try:
    12     import mx
    13 except ImportError:
    14     mx = None
     13from itertools import repeat
    1514
    1615DatabaseError = Database.DatabaseError
    1716
    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)
     17class CursorWrapper:
     18    def __init__(self, cursor):
     19        self._cursor = cursor
     20        self._limit_re = re.compile(r'(?:LIMIT\s+(\d+))?\s*(?:OFFSET\s+(\d+))?$')
    2621
    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
    3128
    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)
    4632
     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
     56def 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         
    4764class DatabaseWrapper:
    4865    def __init__(self):
    49         self.connection = None
     66        self.local = threading.local()
    5067        self.queries = []
    5168
     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
    5279    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
    6481        if DEBUG:
    65             return base.CursorDebugWrapper(cursor, self)
    66         return cursor
     82            return base.CursorDebugWrapper(self._cursor(), self)
     83        return self._cursor()
    6784
     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
    6891    def commit(self):
    69         return self.connection.commit()
     92        self.connection.commit()
    7093
    7194    def rollback(self):
    72         if self.connection:
    73             return self.connection.rollback()
     95        if self.connection is not None:
     96            self.connection.rollback()
    7497
    7598    def close(self):
    7699        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
    78105            self.connection = None
    79106
    80107    def quote_name(self, name):
     
    83110        return '[%s]' % name
    84111
    85112def 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
    87114    return cursor.fetchone()[0]
    88115
    89116def get_date_extract_sql(lookup_type, table_name):
     
    100127        return "Convert(datetime, Convert(varchar(12), %s))" % field_name
    101128
    102129def get_limit_offset_sql(limit, offset=None):
    103     # TODO: This is a guess. Make sure this is correct.
    104130    sql = "LIMIT %s" % limit
    105131    if offset and offset != 0:
    106132        sql += " OFFSET %s" % offset
    107133    return sql
    108134
    109135def get_random_function_sql():
    110     return "RAND()"
     136    return 'rand()'
    111137
    112138def 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()]
    114141
    115142def get_table_description(cursor, table_name):
    116143    raise NotImplementedError
     
    134161    'iendswith': 'LIKE %s',
    135162}
    136163
     164def 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
     170SELECT
     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'
     179FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS RC
     180JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU1
     181ON KCU1.CONSTRAINT_CATALOG = RC.CONSTRAINT_CATALOG
     182   AND KCU1.CONSTRAINT_SCHEMA = RC.CONSTRAINT_SCHEMA
     183   AND KCU1.CONSTRAINT_NAME = RC.CONSTRAINT_NAME
     184JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU2
     185ON KCU2.CONSTRAINT_CATALOG =
     186RC.UNIQUE_CONSTRAINT_CATALOG
     187   AND KCU2.CONSTRAINT_SCHEMA =
     188RC.UNIQUE_CONSTRAINT_SCHEMA
     189   AND KCU2.CONSTRAINT_NAME =
     190RC.UNIQUE_CONSTRAINT_NAME
     191   AND KCU2.ORDINAL_POSITION = KCU1.ORDINAL_POSITION
     192
     193    """
     194
     195def 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.
    137204DATA_TYPES = {
    138     'AutoField':         'int IDENTITY (1, 1)',
     205    'AutoField':         'int identity',
    139206    'BooleanField':      'bit',
    140207    'CharField':         'varchar(%(maxlength)s)',
    141208    'CommaSeparatedIntegerField': 'varchar(%(maxlength)s)',
     
    144211    'FileField':         'varchar(100)',
    145212    'FilePathField':     'varchar(100)',
    146213    'FloatField':        'numeric(%(max_digits)s, %(decimal_places)s)',
    147     'ImageField':        'varchar(100)',
     214    'ImageField':        'nvarchar(100)',
    148215    'IntegerField':      'int',
    149     'IPAddressField':    'char(15)',
     216    'IPAddressField':    'nchar(15)',
    150217    'ManyToManyField':   None,
    151218    'NullBooleanField':  'bit',
    152219    'OneToOneField':     'int',
     
    155222    'PositiveSmallIntegerField': 'smallint CONSTRAINT [CK_smallint_pos_%(column)s] CHECK ([%(column)s] > 0)',
    156223    'SlugField':         'varchar(50)',
    157224    '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',
    162230}
    163231
    164 DATA_TYPES_REVERSE = {}
     232DATA_TYPES_REVERSE = { }
  • django/core/meta/__init__.py

    === django/core/meta/__init__.py
    ==================================================================
     
    10211021            placeholders.append('(SELECT COUNT(*) FROM %s WHERE %s = %%s)' % \
    10221022                (db.db.quote_name(opts.db_table), db.db.quote_name(opts.order_with_respect_to.column)))
    10231023            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))
    10241026        cursor.execute("INSERT INTO %s (%s) VALUES (%s)" % \
    10251027            (db.db.quote_name(opts.db_table), ','.join(field_names),
    10261028            ','.join(placeholders)), db_values)
     
    11031105        val = getattr(self, field_with_rel.attname)
    11041106        mod = field_with_rel.rel.to.get_model_module()
    11051107        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
    11101111        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)
    11131118        setattr(self, cache_var, retrieved_obj)
    11141119    return getattr(self, cache_var)
    11151120
Back to Top