Django

Code

Ticket #1261: firebird-6669-update.diff

File firebird-6669-update.diff, 98.2 kB (added by i_i, 10 months ago)

django-on-fire patch against django svn 6669

  • django/db/models/base.py

    old new  
    44from django.core.exceptions import ObjectDoesNotExist 
    55from django.db.models.fields import AutoField, ImageField, FieldDoesNotExist 
    66from django.db.models.fields.related import OneToOneRel, ManyToOneRel 
     7from django.db.models.fields.computed import ComputedField 
    78from django.db.models.query import delete_objects 
    89from django.db.models.options import Options, AdminOptions 
    910from django.db import connection, transaction 
     
    227228                self._meta.pk.get_db_prep_lookup('exact', pk_val)) 
    228229            # If it does already exist, do an UPDATE. 
    229230            if cursor.fetchone(): 
    230                 db_values = [f.get_db_prep_save(raw and getattr(self, f.attname) or f.pre_save(self, False)) for f in non_pks
     231                db_values = [f.get_db_prep_save(raw and getattr(self, f.attname) or f.pre_save(self, False)) for f in non_pks if not isinstance(f, ComputedField)
    231232                if db_values: 
    232233                    cursor.execute("UPDATE %s SET %s WHERE %s=%%s" % \ 
    233234                        (qn(self._meta.db_table), 
    234                         ','.join(['%s=%%s' % qn(f.column) for f in non_pks]), 
     235                        ','.join(['%s=%%s' % qn(f.column) for f in non_pks if not isinstance(f, ComputedField)]), 
    235236                        qn(self._meta.pk.column)), 
    236237                        db_values + self._meta.pk.get_db_prep_lookup('exact', pk_val)) 
    237238            else: 
    238239                record_exists = False 
    239240        if not pk_set or not record_exists: 
    240             field_names = [qn(f.column) for f in self._meta.fields if not isinstance(f, AutoField)] 
    241             db_values = [f.get_db_prep_save(raw and getattr(self, f.attname) or f.pre_save(self, True)) for f in self._meta.fields if not isinstance(f, AutoField)] 
     241            field_names = [qn(f.column) for f in self._meta.fields if not isinstance(f, (AutoField, ComputedField))] 
     242            db_values = [f.get_db_prep_save(raw and getattr(self, f.attname) or f.pre_save(self, True)) for f in self._meta.fields if not isinstance(f, (AutoField, ComputedField))] 
    242243            # If the PK has been manually set, respect that. 
    243244            if pk_set: 
    244                 field_names += [f.column for f in self._meta.fields if isinstance(f, AutoField)] 
     245                if connection.features.quote_autofields: 
     246                    field_names += [qn(f.column) for f in self._meta.fields if isinstance(f, AutoField)] 
     247                else: 
     248                    field_names += [f.column for f in self._meta.fields if isinstance(f, AutoField)] 
    245249                db_values += [f.get_db_prep_save(raw and getattr(self, f.attname) or f.pre_save(self, True)) for f in self._meta.fields if isinstance(f, AutoField)] 
    246250            placeholders = ['%s'] * len(field_names) 
    247251            if self._meta.order_with_respect_to: 
  • django/db/models/manager.py

    old new  
    7777    def filter(self, *args, **kwargs): 
    7878        return self.get_query_set().filter(*args, **kwargs) 
    7979 
     80    def firefilter(self, *args, **kwargs): 
     81        return self.get_query_set().firefilter(*args, **kwargs) 
     82     
    8083    def complex_filter(self, *args, **kwargs): 
    8184        return self.get_query_set().complex_filter(*args, **kwargs) 
    8285 
  • django/db/models/fields/__init__.py

    old new  
    66except ImportError: 
    77    from django.utils import _decimal as decimal    # for Python 2.3 
    88 
    9 from django.db import get_creation_module 
     9from django.db import connection, get_creation_module 
    1010from django.db.models import signals 
    1111from django.dispatch import dispatcher 
    1212from django.conf import settings 
     
    6666# 
    6767#     getattr(obj, opts.pk.attname) 
    6868 
    69 class Field(object): 
     69class _Field(object): 
    7070    # Provide backwards compatibility for the maxlength attribute and 
    7171    # argument for this class and all subclasses. 
    7272    __metaclass__ = LegacyMaxlength 
     
    8383        core=False, rel=None, default=NOT_PROVIDED, editable=True, serialize=True, 
    8484        prepopulate_from=None, unique_for_date=None, unique_for_month=None, 
    8585        unique_for_year=None, validator_list=None, choices=None, radio_admin=None, 
    86         help_text='', db_column=None, db_tablespace=None): 
     86        help_text='', db_column=None, db_tablespace=None, encoding=None, 
     87        on_update=None, on_delete=None): 
    8788        self.name = name 
    8889        self.verbose_name = verbose_name 
    8990        self.primary_key = primary_key 
    9091        self.max_length, self.unique = max_length, unique 
     92        self.encoding = encoding 
    9193        self.blank, self.null = blank, null 
    9294        # Oracle treats the empty string ('') as null, so coerce the null 
    9395        # option whenever '' is a possible value. 
     
    105107        self.help_text = help_text 
    106108        self.db_column = db_column 
    107109        self.db_tablespace = db_tablespace 
     110        self.on_update = on_update 
     111        self.on_delete = on_delete 
    108112 
    109113        # Set db_index to True if the field has a relationship and doesn't explicitly set db_index. 
    110114        self.db_index = db_index 
     
    148152        data_types = get_creation_module().DATA_TYPES 
    149153        internal_type = self.get_internal_type() 
    150154        if internal_type not in data_types: 
    151             return None 
     155            return None     
    152156        return data_types[internal_type] % self.__dict__ 
    153157 
    154158    def validate_full(self, field_data, all_data): 
     
    402406        "Returns the value of this field in the given model instance." 
    403407        return getattr(obj, self.attname) 
    404408 
     409# Use the backend's Field class if it defines one. Otherwise, use _Field. 
     410if connection.features.uses_custom_field: 
     411    Field = connection.ops.field_class(_Field) 
     412else: 
     413    Field = _Field 
     414 
    405415class AutoField(Field): 
    406416    empty_strings_allowed = False 
    407417    def __init__(self, *args, **kwargs): 
     
    688698        defaults.update(kwargs) 
    689699        return super(DecimalField, self).formfield(**defaults) 
    690700 
     701class DefaultCharField(CharField): 
     702    def __init__(self, *args, **kwargs): 
     703        DEFAULT_MAX_LENGTH = 100 
     704        if hasattr(settings, 'DEFAULT_MAX_LENGTH'): 
     705           DEFAULT_MAX_LENGTH = settings.DEFAULT_MAX_LENGT 
     706        kwargs['max_length'] = kwargs.get('max_length', DEFAULT_MAX_LENGTH) 
     707        CharField.__init__(self, *args, **kwargs) 
     708 
    691709class EmailField(CharField): 
    692710    def __init__(self, *args, **kwargs): 
    693711        kwargs['max_length'] = kwargs.get('max_length', 75) 
     
    890908        defaults.update(kwargs) 
    891909        return super(IPAddressField, self).formfield(**defaults) 
    892910 
     911class LargeTextField(Field): 
     912    def get_manipulator_field_objs(self): 
     913        return [oldforms.LargeTextField] 
     914 
     915    def formfield(self, **kwargs): 
     916        defaults = {'widget': forms.Textarea} 
     917        defaults.update(kwargs) 
     918        return super(LargeTextField, self).formfield(**defaults) 
     919 
    893920class NullBooleanField(Field): 
    894921    empty_strings_allowed = False 
    895922    def __init__(self, *args, **kwargs): 
     
    9971024            # doesn't support microseconds. 
    9981025            if settings.DATABASE_ENGINE == 'mysql' and hasattr(value, 'microsecond'): 
    9991026                value = value.replace(microsecond=0) 
    1000             if settings.DATABASE_ENGINE == 'oracle'
    1001                 # cx_Oracle expects a datetime.datetime to persist into TIMESTAMP field. 
     1027            if settings.DATABASE_ENGINE in ('oracle', 'firebird')
     1028                # cx_Oracle and kinterbasdb expect a datetime.datetime to persist into TIMESTAMP field. 
    10021029                if isinstance(value, datetime.time): 
    10031030                    value = datetime.datetime(1900, 1, 1, value.hour, value.minute, 
    10041031                                              value.second, value.microsecond) 
  • django/db/models/fields/related.py

    old new  
    331331                        new_ids.add(obj) 
    332332                # Add the newly created or already existing objects to the join table. 
    333333                # First find out which items are already added, to avoid adding them twice 
     334                qn = connection.ops.quote_name 
    334335                cursor = connection.cursor() 
    335336                cursor.execute("SELECT %s FROM %s WHERE %s = %%s AND %s IN (%s)" % \ 
    336                     (target_col_name, self.join_table, source_col_name
    337                     target_col_name, ",".join(['%s'] * len(new_ids))), 
     337                    (qn(target_col_name), qn(self.join_table), qn(source_col_name)
     338                    qn(target_col_name), ",".join(['%s'] * len(new_ids))), 
    338339                    [self._pk_val] + list(new_ids)) 
    339340                existing_ids = set([row[0] for row in cursor.fetchall()]) 
    340341 
    341342                # Add the ones that aren't there already 
    342343                for obj_id in (new_ids - existing_ids): 
    343                     cursor.execute("INSERT INTO %s (%s, %s) VALUES (%%s, %%s)" % \ 
    344                         (self.join_table, source_col_name, target_col_name), 
    345                         [self._pk_val, obj_id]
     344                    cursor.execute('INSERT INTO %s (%s, %s) VALUES (%%s, %%s)' % \ 
     345                        (qn(self.join_table), qn(source_col_name), qn(target_col_name)), 
     346                        (self._pk_val, obj_id)
    346347                transaction.commit_unless_managed() 
    347348 
    348349        def _remove_items(self, source_col_name, target_col_name, *objs): 
  • django/db/models/fields/computed.py

    old new  
     1from django.db.models.fields import CharField, DateField, DateTimeField, DecimalField 
     2from django.db.models.fields import IntegerField, FloatField, SmallIntegerField, TimeField 
     3 
     4class ComputedField(object): 
     5    def __init__(self, **kwargs): 
     6        expression = kwargs.pop('expression') 
     7        if 'params' in kwargs: 
     8            raw_params = kwargs.pop('params') 
     9            params = [] 
     10            for rp in raw_params: 
     11                params.append('"%s"' % rp) 
     12            self.expression = expression % tuple(params) 
     13        else: 
     14            self.expression = expression     
     15    def db_type(self): 
     16        return 'ComputedField' 
     17 
     18class ComputedCharField(ComputedField, CharField): 
     19    def __init__(self, **kwargs): 
     20        ComputedField.__init__(self, **kwargs) 
     21        if 'params' in kwargs: 
     22            del kwargs['params'] 
     23        if 'expression' in kwargs: 
     24            del kwargs['expression'] 
     25        CharField.__init__(self, **kwargs) 
     26 
     27class ComputedDateField(ComputedField, DateField): 
     28    def __init__(self, **kwargs): 
     29        ComputedField.__init__(self, **kwargs) 
     30        if 'params' in kwargs: 
     31            del kwargs['params'] 
     32        if 'expression' in kwargs: 
     33            del kwargs['expression'] 
     34        DateField.__init__(self, **kwargs)   
     35 
     36class ComputedDateTimeField(ComputedField, DateTimeField): 
     37    def __init__(self, **kwargs): 
     38        ComputedField.__init__(self, **kwargs) 
     39        if 'params' in kwargs: 
     40            del kwargs['params'] 
     41        if 'expression' in kwargs: 
     42            del kwargs['expression']  
     43        DateTimeField.__init__(self, **kwargs)  
     44 
     45class ComputedDecimalField(ComputedField, DecimalField): 
     46    def __init__(self, **kwargs): 
     47        ComputedField.__init__(self, **kwargs) 
     48        if 'params' in kwargs: 
     49            del kwargs['params'] 
     50        if 'expression' in kwargs: 
     51            del kwargs['expression'] 
     52        DecimalField.__init__(self, **kwargs) 
     53         
     54class ComputedFloatField(ComputedField, FloatField): 
     55    def __init__(self, **kwargs): 
     56        ComputedField.__init__(self, **kwargs) 
     57        if 'params' in kwargs: 
     58            del kwargs['params'] 
     59        if 'expression' in kwargs: 
     60            del kwargs['expression'] 
     61        FloatField.__init__(self, **kwargs) 
     62 
     63class ComputedIntegerField(ComputedField, IntegerField): 
     64    def __init__(self, **kwargs): 
     65        ComputedField.__init__(self, **kwargs) 
     66        if 'params' in kwargs: 
     67            del kwargs['params'] 
     68        if 'expression' in kwargs: 
     69            del kwargs['expression'] 
     70        IntegerField.__init__(self, **kwargs) 
     71 
     72class ComputedSmallIntegerField(ComputedField, SmallIntegerField): 
     73    def __init__(self, **kwargs): 
     74        ComputedField.__init__(self, **kwargs) 
     75        if 'params' in kwargs: 
     76            del kwargs['params'] 
     77        if 'expression' in kwargs: 
     78            del kwargs['expression'] 
     79        SmallIntegerField.__init__(self, **kwargs) 
     80 
     81class ComputedTimeField(ComputedField, TimeField): 
     82    def __init__(self, **kwargs): 
     83        ComputedField.__init__(self, **kwargs) 
     84        if 'params' in kwargs: 
     85            del kwargs['params'] 
     86        if 'expression' in kwargs: 
     87            del kwargs['expression']  
     88        TimeField.__init__(self, **kwargs)     
     89 
  • django/db/models/__init__.py

    old new  
    77from django.db.models.manager import Manager 
    88from django.db.models.base import Model, AdminOptions 
    99from django.db.models.fields import * 
     10from django.db.models.procedures import Procedure, Trigger 
    1011from django.db.models.fields.subclassing import SubfieldBase 
    1112from django.db.models.fields.related import ForeignKey, OneToOneField, ManyToManyField, ManyToOneRel, ManyToManyRel, OneToOneRel, TABULAR, STACKED 
     13from django.db.models.fields.computed import * 
    1214from django.db.models import signals 
    1315from django.utils.functional import curry 
    1416from django.utils.text import capfirst 
  • django/db/models/procedures/__init__.py

    old new  
     1from django.db import connection, transaction 
     2 
     3 
     4class procedure_meta(type): 
     5    """ 
     6    Metaclass for native stored procedures behaving as normal Django model methods 
     7    Subclass Procedure inside your model, create procedures tuple 
     8    and use the class as ordinary method 
     9    Please treat the resulting classes as methods because they don't 
     10    behave like classes anymore 
     11    Look at tests/customtests/custom_methods/model.py for demonstration 
     12    """ 
     13    def __init__(cls, name, bases, attrs): 
     14        super(procedure_meta, cls).__init__(name, bases, attrs) 
     15        params, returns, vars, body = None, None, None, None 
     16        if '__params__' in attrs: 
     17            params = attrs['__params__'] 
     18        if '__returns__' in attrs: 
     19            returns = attrs['__returns__'] 
     20        if '__vars__' in attrs: 
     21            vars = attrs['__vars__'] 
     22        if name != 'Procedure': 
     23            assert '__body__' in attrs, "Procedure must have body"    
     24            header = ['CREATE OR ALTER PROCEDURE %s ' % name] 
     25            if params: 
     26                header.append('(') 
     27                header.append(', '.join([param + ' ' + paramtype for param, paramtype in params])) 
     28                header.append(')') 
     29            if returns: 
     30                header.append('\nRETURNS (') 
     31                header.append(', '.join([ret + ' ' + rettype for ret, rettype in returns])) 
     32                header.append(')')    
     33            cls.__sql__ = [''.join(header)] 
     34            cls.__sql__.append('AS') 
     35            if vars: 
     36                var_declare = ['DECLARE VARIABLE '] 
     37                var_declare.append(', '.join([var + ' ' + vartype for var, vartype in vars])) 
     38                var_declare.append(';') 
     39                cls.__sql__.append(''.join(var_declare)) 
     40            cls.__sql__.extend(['BEGIN', attrs['__body__'], 'END']) 
     41            cls.sql = '\n'.join(cls.__sql__) 
     42            cls.params = params 
     43            cls.returns = returns 
     44            cls.procedure_name = name 
     45     
     46    def create_procedure_sql(cls): 
     47        return cls.sql 
     48     
     49    def execute(cls, cursor, *args): 
     50        if args: 
     51            return cursor.execute_straight('SELECT %s FROM %s(%s);' %\ 
     52                    (', '.join("%s" % ret[0] for ret in cls.returns), 
     53                     cls.procedure_name, 
     54                     ', '.join("?" * len(args))), args) 
     55        else: 
     56            return cursor.execute_straight('SELECT %s FROM %s;' %\ 
     57                    (', '.join("%s" % ret[0] for ret in cls.returns), 
     58                     cls.procedure_name)) 
     59 
     60    def __call__(cls, *args): 
     61        cursor = connection.cursor() 
     62        if not cls.returns: 
     63            cursor.execute('EXECUTE PROCEDURE "%s" %s;' %\ 
     64                (cls.procedure_name, 
     65                 ' ,'.join("'%s'" % arg for arg in args))) 
     66        elif len(cls.returns) == 1: 
     67            # Procedure returns value 
     68            cursor.callproc(cls.procedure_name, args) 
     69            return cursor.fetchone()[0] 
     70         
     71        else: 
     72            cursor.execute_straight('SELECT %s FROM %s(%s);' %\ 
     73                (', '.join("%s" % ret[0] for ret in cls.returns), 
     74                 cls.procedure_name, 
     75                 ', '.join("?" * len(args))), args)        
     76            return cursor.fetchall() 
     77 
     78class trigger_meta(type): 
     79    """ 
     80    """ 
     81    def __init__(cls, name, bases, attrs): 
     82        super(trigger_meta, cls).__init__(name, bases, attrs) 
     83        vars, body, table, mode = None, None, None, None 
     84        if '__vars__' in attrs: 
     85            vars = attrs['__vars__'] 
     86        if name != 'Trigger': 
     87            assert '__body__' in attrs, "Triggers must have some SQL code" 
     88            assert '__table__' in attrs, "Triggers must have the table" 
     89            assert '__mode__' in attrs, "Triggers must have a mode, e.g. AFTER INSERT"    
     90            header = 'CREATE OR ALTER TRIGGER %s FOR %s\n%s AS' %\ 
     91             (name, connection.ops.quote_name(attrs['__table__']), attrs['__mode__']) 
     92            cls.__sql__ = [header] 
     93            if vars: 
     94                var_declare = ['DECLARE VARIABLE '] 
     95                var_declare.append(', '.join([var + ' ' + vartype for var, vartype in vars])) 
     96                var_declare.append(';') 
     97                cls.__sql__.append(''.join(var_declare)) 
     98            cls.__sql__.extend(['BEGIN', attrs['__body__'], 'END']) 
     99            cls.sql = '\n'.join(cls.__sql__) 
     100            cls.trigger_name = name 
     101    def create_trigger_sql(cls): 
     102        return cls.sql 
     103 
     104class Procedure(object): 
     105    __metaclass__ = procedure_meta 
     106 
     107class Trigger(object): 
     108    __metaclass__ = trigger_meta 
     109 
  • django/db/models/procedures/firemethod.py

    old new  
     1# Stored procedures' Django model method wrapper 
     2# 
     3# Works only with Python 2.5+ 
     4# For Python < 2.5 use class notation 
     5# 
     6#    Example: 
     7# 
     8#    @firemethod     
     9#    def was_published_today_from_id(self_id='integer'): 
     10#        """ 
     11#        SELECT "id" 
     12#        FROM "firemethod_article" 
     13#        WHERE "pub_date" = 'today' AND "id" = :self_id 
     14#        INTO :result; 
     15#        IF (result IS NULL) THEN 
     16#            result = 0; 
     17#        ELSE 
     18#            result = 1;     
     19#        EXIT; 
     20#        """ 
     21#        return ('result', 'integer') 
     22#    
     23#    @firemethod     
     24#    def was_published_today_filter(): 
     25#        """ 
     26#        FOR SELECT "id", "headline", "pub_date" 
     27#        FROM "firemethod_article" 
     28#        WHERE "pub_date" = 'today' 
     29#        INTO :other_id, :other_headline, :other_pub_date 
     30#        DO BEGIN SUSPEND; END 
     31#        """ 
     32#        # Dummy variable just to show how to declare 
     33#        declare = ('dummy', 'varchar(200)') 
     34#        return ('other_id', 'integer'), ('other_headline', 'varchar(100)'), ('other_pub_date', 'date') 
     35#    
     36#   procedures = (was_published_today_filter, was_published_today_from_id) 
     37# 
     38# 
     39# >>> article.was_published_today_from_id(article.pk) 
     40# 1 
     41# >>> bool(art.was_published_today_from_id(other_article.pk)) 
     42# False 
     43# >>>Article.objects.firefilter(self.was_published_today_filter) 
     44# [<Article: Only for today>, <Article: The life is always in a current moment>] 
     45 
     46from functools import * 
     47from django.db.models import Procedure 
     48from django.db.models.procedures import procedure_meta 
     49 
     50def firemethod(f): 
     51    @wraps(f) 
     52    def wrapper(): 
     53        params, returns, vars, ret = None, None, None, f() 
     54        attrs = {} 
     55        if f.func_code.co_varnames: 
     56            all_vars = list(f.func_code.co_varnames) 
     57            if 'declare' in all_vars: 
     58                all_tuples = set([ d for d in f.func_code.co_consts if isinstance(d, tuple) ]) 
     59                if isinstance(ret[0], basestring): 
     60                    ret = [ret] 
     61                ret_tuples = set(ret) 
     62                all_vars.remove('declare') 
     63                vars = [] 
     64                for var, var_type in all_tuples - ret_tuples: 
     65                    vars.append((var, var_type)) 
     66            if len(all_vars) > 0 and f.func_defaults: 
     67                params = [] 
     68                for key, value in zip(all_vars, f.func_defaults) : 
     69                     params.append((key, value)) 
     70        if ret: 
     71            returns = [] 
     72            if isinstance(ret[0], basestring): 
     73                    ret = [ret] 
     74            for var, var_type in ret: 
     75                returns.append((var, var_type))          
     76        if params: 
     77            attrs['__params__'] = params 
     78        if returns: 
     79            attrs['__returns__'] = returns 
     80        if vars: 
     81            attrs['__vars__'] = vars 
     82        attrs['__body__'] = f.__doc__ 
     83        return procedure_meta(f.__name__, (Procedure,), attrs) 
     84    return wrapper() 
     85         
  • django/db/models/query.py

    old new  
    612612        columns = [f.column for f in fields] 
    613613        select = ['%s.%s' % (qn(self.model._meta.db_table), qn(c)) for c in columns] 
    614614        if extra_select: 
    615             select.extend(['(%s) AS %s' % (quote_only_if_word(s[1]), qn(s[0])) for s in extra_select]) 
     615            if not settings.DATABASE_ENGINE == 'firebird': 
     616                select.extend(['(%s) AS %s' % (quote_only_if_word(s[1]), qn(s[0])) for s in extra_select]) 
     617            else: 
     618                select.extend(['(%s) AS %s' % (connection.ops.quote_id_plus_number(s[1]), qn(s[0])) for s in extra_select]) 
    616619            field_names.extend([f[0] for f in extra_select]) 
    617620 
    618621        cursor = connection.cursor() 
     
    11111114            # Last query term was a normal field. 
    11121115            column = field.column 
    11131116            db_type = field.db_type() 
    1114  
    11151117        where.append(get_where_clause(lookup_type, current_table + '.', column, value, db_type)) 
    11161118        params.extend(field.get_db_prep_lookup(lookup_type, value)) 
    11171119 
     
    11461148            if isinstance(f, generic.GenericRelation): 
    11471149                from django.contrib.contenttypes.models import ContentType 
    11481150                query_extra = 'AND %s=%%s' % f.rel.to._meta.get_field(f.content_type_field_name).column 
     1151                if settings.DATABASE_ENGINE == 'firebird': 
     1152                    query_extra = 'AND %s=%%s' % qn(f.rel.to._meta.get_field(f.content_type_field_name).column) 
    11491153                args_extra = [ContentType.objects.get_for_model(cls).id] 
    11501154            else: 
    11511155                query_extra = '' 
  • django/db/backends/__init__.py

    old new  
    4848    needs_upper_for_iops = False 
    4949    supports_constraints = True 
    5050    supports_tablespaces = False 
     51    quote_autofields = False 
    5152    uses_case_insensitive_names = False 
     53    uses_custom_field = False 
    5254    uses_custom_queryset = False 
    5355 
    5456class BaseDatabaseOperations(object): 
     
    199201        """ 
    200202        raise NotImplementedError() 
    201203 
     204    def reference_name(self, r_col, col, r_table, table): 
     205        return '%s_refs_%s_%x' % (r_col, col, abs(hash((r_table, table)))) 
     206 
    202207    def random_function_sql(self): 
    203208        """ 
    204209        Returns a SQL expression that returns a random value. 
  • django/db/backends/firebird/base.py

    old new  
     1""" 
     2Firebird database backend for Django. 
     3 
     4Requires KInterbasDB 3.2: http://kinterbasdb.sourceforge.net/ 
     5The egenix mx (mx.DateTime) is NOT required 
     6 
     7Database charset should be UNICODE_FSS or UTF8 (FireBird 2.0+) 
     8To use UTF8 encoding add FIREBIRD_CHARSET = 'UTF8' to your settings.py  
     9UNICODE_FSS works with all versions and uses less memory 
     10""" 
     11 
     12from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, BaseDatabaseOperations, util 
     13import sys 
     14try: 
     15    import decimal 
     16except ImportError: 
     17    from django.utils import _decimal as decimal    # for Python 2.3 
     18 
     19try: 
     20    import kinterbasdb as Database 
     21except ImportError, e: 
     22    from django.core.exceptions import ImproperlyConfigured 
     23    raise ImproperlyConfigured, "Error loading KInterbasDB module: %s" % e 
     24 
     25DatabaseError = Database.DatabaseError 
     26IntegrityError = Database.IntegrityError 
     27 
     28class DatabaseFeatures(BaseDatabaseFeatures): 
     29    inline_fk_references = False  
     30    needs_datetime_string_cast = False 
     31    needs_upper_for_iops = True 
     32    quote_autofields = True 
     33    uses_custom_field = True 
     34    uses_custom_queryset = True 
     35 
     36################################################################################ 
     37# Database operations (db.connection.ops)   
     38class DatabaseOperations(BaseDatabaseOperations): 
     39    """ 
     40    This class encapsulates all backend-specific differences, such as the way 
     41    a backend performs ordering or calculates the ID of a recently-inserted 
     42    row. 
     43    """ 
     44    # Utility ops: names, version, page size etc.:  
     45    _max_name_length = 31 
     46    def __init__(self): 
     47        self._firebird_version = None 
     48        self._page_size = None 
     49     
     50    def get_generator_name(self, name): 
     51        return '%s$G' % util.truncate_name(name.strip('"'), self._max_name_length-2).upper() 
     52         
     53    def get_trigger_name(self, name): 
     54        return '%s$T' % util.truncate_name(name.strip('"'), self._max_name_length-2).upper()  
     55     
     56    def _get_firebird_version(self): 
     57        if self._firebird_version is None: 
     58            from django.db import connection 
     59            self._firebird_version = [int(val) for val in connection.server_version.split()[-1].split('.')] 
     60        return self._firebird_version 
     61    firebird_version = property(_get_firebird_version) 
     62   
     63    def reference_name(self, r_col, col, r_table, table): 
     64        base_name = util.truncate_name('%s$%s' % (r_col, col), self._max_name_length-5) 
     65        return ('%s$%x' % (base_name, abs(hash((r_table, table))))).upper() 
     66     
     67    def _get_page_size(self): 
     68        if self._page_size is None: 
     69            from django.db import connection 
     70            self._page_size = connection.database_info(Database.isc_info_page_size, 'i') 
     71        return self._page_size 
     72    page_size = property(_get_page_size) 
     73     
     74    def _get_index_limit(self): 
     75        if self.firebird_version[0] < 2: 
     76            self._index_limit = 252  
     77        else: 
     78            page_size = self._get_page_size() 
     79            self._index_limit = page_size/4 
     80        return self._index_limit 
     81    index_limit = property(_get_index_limit) 
     82     
     83    def max_name_length(self): 
     84        return self._max_name_length 
     85     
     86    def quote_name(self, name): 
     87        name = '"%s"' % util.truncate_name(name.strip('"'), self._max_name_length) 
     88        return name 
     89     
     90    def quote_id_plus_number(self, name): 
     91        try: 
     92            return '"%s" + %s' % tuple(s.strip() for s in name.strip('"').split('+')) 
     93        except: 
     94            return self.quote_name(name) 
     95     
     96    def field_cast_sql(self, db_type): 
     97        return '%s' 
     98 
     99    ############################################################################ 
     100    # Basic SQL ops:     
     101    def last_insert_id(self, cursor, table_name, pk_name=None): 
     102        generator_name = self.get_generator_name(table_name) 
     103        cursor.execute('SELECT GEN_ID(%s, 0) from RDB$DATABASE' % generator_name) 
     104        return cursor.fetchone()[0] 
     105 
     106    def date_extract_sql(self, lookup_type, column_name): 
     107        # lookup_type is 'year', 'month', 'day' 
     108        return "EXTRACT(%s FROM %s)" % (lookup_type, column_name) 
     109 
     110    def date_trunc_sql(self, lookup_type, column_name): 
     111        if lookup_type == 'year': 
     112             sql = "EXTRACT(year FROM %s)||'-01-01 00:00:00'" % column_name 
     113        elif lookup_type == 'month': 
     114            sql = "EXTRACT(year FROM %s)||'-'||EXTRACT(month FROM %s)||'-01 00:00:00'" % (column_name, column_name) 
     115        elif lookup_type == 'day': 
     116            sql = "EXTRACT(year FROM %s)||'-'||EXTRACT(month FROM %s)||'-'||EXTRACT(day FROM %s)||' 00:00:00'" % (column_name, column_name, column_name) 
     117        return "CAST(%s AS TIMESTAMP)" % sql 
     118 
     119    def datetime_cast_sql(self): 
     120        return None 
     121 
     122    def drop_sequence_sql(self, table): 
     123        return "DROP GENERATOR %s;" % self.get_generator_name(table) 
     124     
     125    def drop_foreignkey_sql(self): 
     126        return "DROP CONSTRAINT" 
     127         
     128    def limit_offset_sql(self, limit, offset=None): 
     129        # limits are handled in custom FirebirdQuerySet  
     130        assert False, 'Limits are handled in a different way in Firebird' 
     131        return "" 
     132 
     133    def random_function_sql(self): 
     134        return "rand()" 
     135 
     136    def pk_default_value(self): 
     137        """ 
     138        Returns the value to use during an INSERT statement to specify that 
     139        the field should use its default value. 
     140        """ 
     141        return 'NULL' 
     142     
     143    def start_transaction_sql(self): 
     144        return "" 
     145 
     146    def fulltext_search_sql(self, field_name): 
     147        # We use varchar for TextFields so this is possible 
     148        # Look at http://www.volny.cz/iprenosil/interbase/ip_ib_strings.htm 
     149        return '%%s CONTAINING %s' % self.quote_name(field_name) 
     150 
     151    ############################################################################ 
     152    # Advanced SQL ops: 
     153    def autoinc_sql(self, style, table_name, column_name): 
     154        """ 
     155        To simulate auto-incrementing primary keys in Firebird, we have to 
     156        create a generator and a trigger. 
     157     
     158        Create the generators and triggers names based only on table name 
     159        since django only support one auto field per model 
     160        """ 
     161    
     162        generator_name = self.get_generator_name(table_name) 
     163        trigger_name = self.get_trigger_name(table_name) 
     164        column_name = self.quote_name(column_name) 
     165        table_name = self.quote_name(table_name) 
     166         
     167        generator_sql = "%s %s;" % ( style.SQL_KEYWORD('CREATE GENERATOR'),  
     168                                     generator_name)       
     169        trigger_sql = "\n".join([ 
     170            "%s %s %s %s" % ( \ 
     171            style.SQL_KEYWORD('CREATE TRIGGER'), trigger_name, style.SQL_KEYWORD('FOR'), 
     172            style.SQL_TABLE(table_name)), 
     173            "%s 0 %s" % (style.SQL_KEYWORD('ACTIVE BEFORE INSERT POSITION'), style.SQL_KEYWORD('AS')), 
     174            style.SQL_KEYWORD('BEGIN'),  
     175            "  %s ((%s.%s %s) %s (%s.%s = 0)) %s" % ( \ 
     176                style.SQL_KEYWORD('IF'), 
     177                style.SQL_KEYWORD('NEW'), style.SQL_FIELD(column_name), style.SQL_KEYWORD('IS NULL'), 
     178                style.SQL_KEYWORD('OR'), style.SQL_KEYWORD('NEW'), style.SQL_FIELD(column_name), 
     179                style.SQL_KEYWORD('THEN') 
     180            ), 
     181            "  %s" % style.SQL_KEYWORD('BEGIN'),  
     182            "    %s.%s = %s(%s, 1);" % ( \ 
     183                style.SQL_KEYWORD('NEW'), style.SQL_FIELD(column_name), 
     184                style.SQL_KEYWORD('GEN_ID'), generator_name 
     185            ), 
     186            "  %s" % style.SQL_KEYWORD('END'), 
     187            "%s;" % style.SQL_KEYWORD('END')]) 
     188        return (generator_sql, trigger_sql) 
     189     
     190    def sequence_reset_sql(self, style, model_list): 
     191        from django.db import models 
     192        output = [] 
     193        sql = ['%s %s %s' % (style.SQL_KEYWORD('CREATE OR ALTER PROCEDURE'), 
     194                             style.SQL_TABLE('"GENERATOR_RESET"'), 
     195                             style.SQL_KEYWORD('AS'))] 
     196        sql.append('%s %s' % (style.SQL_KEYWORD('DECLARE VARIABLE'), style.SQL_COLTYPE('start_val integer;'))) 
     197        sql.append('%s %s' % (style.SQL_KEYWORD('DECLARE VARIABLE'), style.SQL_COLTYPE('gen_val integer;'))) 
     198        sql.append('\t%s' % style.SQL_KEYWORD('BEGIN')) 
     199        sql.append('\t\t%s %s %s %s %s %s;' % (style.SQL_KEYWORD('SELECT MAX'), style.SQL_FIELD('(%(col)s)'), 
     200                                           style.SQL_KEYWORD('FROM'), style.SQL_TABLE('%(table)s'), 
     201                                           style.SQL_KEYWORD('INTO'), style.SQL_COLTYPE(':start_val'))) 
     202        sql.append('\t\t%s (%s %s) %s' % (style.SQL_KEYWORD('IF'), style.SQL_COLTYPE('start_val'), 
     203                                    style.SQL_KEYWORD('IS NULL'), style.SQL_KEYWORD('THEN'))) 
     204        sql.append('\t\t\t%s = %s(%s, 1 - %s(%s, 0));' %\ 
     205            (style.SQL_COLTYPE('gen_val'), style.SQL_KEYWORD('GEN_ID'), style.SQL_TABLE('%(gen)s'), 
     206             style.SQL_KEYWORD('GEN_ID'), style.SQL_TABLE('%(gen)s'))) 
     207        sql.append('\t\t%s' % style.SQL_KEYWORD('ELSE')) 
     208        sql.append('\t\t\t%s = %s(%s, %s - %s(%s, 0));' %\ 
     209            (style.SQL_COLTYPE('gen_val'), style.SQL_KEYWORD('GEN_ID'), 
     210             style.SQL_TABLE('%(gen)s'), style.SQL_COLTYPE('start_val'), style.SQL_KEYWORD('GEN_ID'), 
     211             style.SQL_TABLE('%(gen)s'))) 
     212        sql.append('\t\t%s;' % style.SQL_KEYWORD('EXIT')) 
     213        sql.append('%s;' % style.SQL_KEYWORD('END')) 
     214        sql ="\n".join(sql) 
     215        for model in model_list: 
     216            for f in model._meta.fields: 
     217                if isinstance(f, models.AutoField): 
     218                    generator_name = self.get_generator_name(model._meta.db_table) 
     219                    column_name = self.quote_name(f.db_column or f.name) 
     220                    table_name = self.quote_name(model._meta.db_table) 
     221                    output.append(sql % {'col' : column_name, 'table' : table_name, 'gen' : generator_name}) 
     222                    output.append('%s %s;' % (style.SQL_KEYWORD('EXECUTE PROCEDURE'),  
     223                                              style.SQL_TABLE('"GENERATOR_RESET"'))) 
     224                    break # Only one AutoField is allowed per model, so don't bother continuing. 
     225            for f in model._meta.many_to_many: 
     226                generator_name = self.get_generator_name(f.m2m_db_table()) 
     227                table_name = self.quote_name(f.m2m_db_table()) 
     228                column_name = '"id"' 
     229                output.append(sql % {'col' : column_name, 'table' : table_name, 'gen' : generator_name}) 
     230                output.append('%s %s;' % (style.SQL_KEYWORD('EXECUTE PROCEDURE'),  
     231                                          style.SQL_TABLE('"GENERATOR_RESET"'))) 
     232        return output 
     233     
     234    def sql_flush(self, style, tables, sequences): 
     235        if tables: 
     236            sql = ['%s %s %s;' % \ 
     237                    (style.SQL_KEYWORD('DELETE'), 
     238                     style.SQL_KEYWORD('FROM'), 
     239                     style.SQL_TABLE(self.quote_name(table)) 
     240                     ) for table in tables] 
     241            for generator_info in sequences: 
     242                table_name = generator_info['table'] 
     243                query = "%s %s %s 0;" % (style.SQL_KEYWORD('SET GENERATOR'),  
     244                    self.get_generator_name(table_name), style.SQL_KEYWORD('TO')) 
     245                sql.append(query) 
     246            return sql 
     247        else: 
     248            return [] 
     249             
     250    ############################################################################ 
     251    # Custom classes 
     252    def field_class(this, DefaultField): 
     253        from django.db import connection 
     254        from django.db.models.fields import prep_for_like_query 
     255        class FirebirdField(DefaultField): 
     256            def get_db_prep_lookup(self, lookup_type, value):        
     257                "Returns field's value prepared for database lookup." 
     258                if lookup_type in ('exact', 'regex', 'iregex', 'gt', 'gte', 'lt',  
     259                    'lte', 'month', 'day', 'search', 'icontains',  
     260                    'startswith', 'istartswith'): 
     261                    return [value] 
     262                elif lookup_type in ('range', 'in'): 
     263                    return value 
     264                elif lookup_type in ('contains',): 
     265                    return ["%%%s%%" % prep_for_like_query(value)] 
     266                elif lookup_type == 'iexact': 
     267                    return [prep_for_like_query(value)] 
     268                elif lookup_type in ('endswith', 'iendswith'): 
     269                    return ["%%%s" % prep_for_like_query(value)] 
     270                elif lookup_type == 'isnull': 
     271                    return [] 
     272                elif lookup_type == 'year': 
     273                    try: 
     274                        value = int(value) 
     275                    except ValueError: 
     276                        raise ValueError("The __year lookup type requires an integer argument") 
     277                    return ['%s-01-01 00:00:00' % value, '%s-12-31 23:59:59.999999' % value] 
     278                raise TypeError("Field has invalid lookup: %s" % lookup_type) 
     279        return FirebirdField 
     280 
     281    def query_set_class(this, DefaultQuerySet): 
     282        from django.db import connection 
     283        from django.db.models.query import EmptyResultSet, GET_ITERATOR_CHUNK_SIZE 
     284         
     285        class FirebirdQuerySet(DefaultQuerySet): 
     286            __firefilter__ = None 
     287            ___fireargs__ = () 
     288            def _get_sql_clause(self): 
     289                from django.db.models.query import SortedDict, handle_legacy_orderlist, orderfield2column, fill_table_cache 
     290                qn = this.quote_name 
     291                opts = self.model._meta 
     292 
     293                # Construct the fundamental parts of the query: SELECT X FROM Y WHERE Z. 
     294                select = ["%s.%s" % (qn(opts.db_table), qn(f.column)) for f in opts.fields] 
     295                tables = [qn(t) for t in self._tables] 
     296                joins = SortedDict() 
     297                where = self._where[:] 
     298                params = self._params[:] 
     299 
     300                # Convert self._filters into SQL. 
     301                joins2, where2, params2 = self._filters.get_sql(opts) 
     302                joins.update(joins2) 
     303                where.extend(where2) 
     304                params.extend(params2) 
     305 
     306                # Add additional tables and WHERE clauses based on select_related. 
     307                if self._select_related: 
     308                    fill_table_cache(opts, select, tables, where, 
     309                                     old_prefix=opts.db_table, 
     310                                     cache_tables_seen=[opts.db_table], 
     311                                     max_depth=self._max_related_depth) 
     312                 
     313                # Add any additional SELECTs. 
     314                if self._select: 
     315                    select.extend([('(%s AS %s') % (qn(s[1]), qn(s[0])) for s in self._select.items()]) 
     316 
     317                # Start composing the body of the SQL statement. 
     318                sql = [" FROM", qn(opts.db_table)] 
     319 
     320                # Compose the join dictionary into SQL describing the joins. 
     321                if joins: 
     322                    sql.append(" ".join(["%s %s %s ON %s" % (join_type, table, alias, condition) 
     323                                    for (alias, (table, join_type, condition)) in joins.items()])) 
     324 
     325                # Compose the tables clause into SQL. 
     326                if tables: 
     327                    sql.append(", " + ", ".join(tables)) 
     328 
     329                # Compose the where clause into SQL. 
     330                if where:  
     331  &n