Django

Code

Ticket #1261: firebird-svn6652-full-updated.diff

File firebird-svn6652-full-updated.diff, 45.8 kB (added by i_i, 10 months ago)

some more fixes

  • django/db/models/base.py

    old new  
    238238                record_exists = False 
    239239        if not pk_set or not record_exists: 
    240240            field_names = [qn(f.column) for f in self._meta.fields if not isinstance(f, AutoField)] 
     241            #if connection.features.quote_autofields: 
     242            #    field_names = [qn(f.column) for f in self._meta.fields] 
    241243            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)] 
    242244            # If the PK has been manually set, respect that. 
    243245            if pk_set: 
    244                 field_names += [f.column for f in self._meta.fields if isinstance(f, AutoField)] 
     246                if connection.features.quote_autofields: 
     247                    field_names += [qn(f.column) for f in self._meta.fields if isinstance(f, AutoField)] 
     248                else: 
     249                    field_names += [f.column for f in self._meta.fields if isinstance(f, AutoField)] 
    245250                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)] 
    246251            placeholders = ['%s'] * len(field_names) 
    247252            if self._meta.order_with_respect_to: 
  • django/db/models/fields/__init__.py

    old new  
    208208        return value 
    209209 
    210210    def get_db_prep_lookup(self, lookup_type, value): 
     211        from django.db import connection 
    211212        "Returns field's value prepared for database lookup." 
    212213        if lookup_type in ('exact', 'regex', 'iregex', 'gt', 'gte', 'lt', 'lte', 'month', 'day', 'search'): 
    213214            return [value] 
    214215        elif lookup_type in ('range', 'in'): 
    215216            return value 
    216217        elif lookup_type in ('contains', 'icontains'): 
     218            if connection.features.uses_custom_icontains and lookup_type == 'icontains': 
     219                return [value]     
    217220            return ["%%%s%%" % prep_for_like_query(value)] 
    218221        elif lookup_type == 'iexact': 
    219222            return [prep_for_like_query(value)] 
    220223        elif lookup_type in ('startswith', 'istartswith'): 
     224            if connection.features.uses_custom_startswith: 
     225                return [value] 
    221226            return ["%s%%" % prep_for_like_query(value)] 
    222227        elif lookup_type in ('endswith', 'iendswith'): 
    223228            return ["%%%s" % prep_for_like_query(value)] 
     
    480485        defaults.update(kwargs) 
    481486        return super(CharField, self).formfield(**defaults) 
    482487 
     488class ASCIICharField(CharField): 
     489    pass 
     490 
    483491# TODO: Maybe move this into contrib, because it's specialized. 
    484492class CommaSeparatedIntegerField(CharField): 
    485493    def get_manipulator_field_objs(self): 
     
    589597            # doesn't support microseconds. 
    590598            if settings.DATABASE_ENGINE == 'mysql' and hasattr(value, 'microsecond'): 
    591599                value = value.replace(microsecond=0) 
    592             value = smart_unicode(value) 
     600            #Firebird supports native datetime 
     601            if settings.DATABASE_ENGINE != 'firebird': 
     602                value = smart_unicode(value) 
    593603        return Field.get_db_prep_save(self, value) 
    594604 
    595605    def get_db_prep_lookup(self, lookup_type, value): 
     
    9971007            # doesn't support microseconds. 
    9981008            if settings.DATABASE_ENGINE == 'mysql' and hasattr(value, 'microsecond'): 
    9991009                value = value.replace(microsecond=0) 
    1000             if settings.DATABASE_ENGINE == 'oracle'
    1001                 # cx_Oracle expects a datetime.datetime to persist into TIMESTAMP field. 
     1010            if settings.DATABASE_ENGINE in ('oracle', 'firebird')
     1011                # cx_Oracle and kinterbasdb expect a datetime.datetime to persist into TIMESTAMP field. 
    10021012                if isinstance(value, datetime.time): 
    10031013                    value = datetime.datetime(1900, 1, 1, value.hour, value.minute, 
    10041014                                              value.second, value.microsecond) 
  • django/db/backends/__init__.py

    old new  
    4545    autoindexes_primary_keys = True 
    4646    inline_fk_references = True 
    4747    needs_datetime_string_cast = True 
     48    needs_default_null = False 
    4849    needs_upper_for_iops = False 
    4950    supports_constraints = True 
    5051    supports_tablespaces = False 
     52    quote_autofields = False 
    5153    uses_case_insensitive_names = False 
     54    uses_custom_icontains = False 
     55    uses_custom_startswith = False 
    5256    uses_custom_queryset = False 
    5357 
    5458class BaseDatabaseOperations(object): 
     
    6569        This SQL is executed when a table is created. 
    6670        """ 
    6771        return None 
    68  
     72     
     73    def cascade_delete_update_sql(self): 
     74        """ 
     75        Returns the SQL necessary to make a cascading deletes and updates 
     76        of foreign key references during a CREATE TABLE statement. 
     77        """ 
     78        return '' 
     79     
    6980    def date_extract_sql(self, lookup_type, field_name): 
    7081        """ 
    7182        Given a lookup_type of 'year', 'month' or 'day', returns the SQL that 
  • 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 
     13 
     14try: 
     15    import kinterbasdb as Database 
     16except ImportError, e: 
     17    from django.core.exceptions import ImproperlyConfigured 
     18    raise ImproperlyConfigured, "Error loading KInterbasDB module: %s" % e 
     19 
     20DatabaseError = Database.DatabaseError 
     21IntegrityError = Database.IntegrityError 
     22 
     23class DatabaseFeatures(BaseDatabaseFeatures): 
     24    needs_datetime_string_cast = False 
     25    needs_default_null = True 
     26    needs_upper_for_iops = True 
     27    quote_autofields = True 
     28    supports_constraints = False #some tests went strange without it 
     29    uses_custom_icontains = True #CONTAINING <value> op instead of LIKE %<value>% 
     30    uses_custom_startswith = True #STARTING WITH op. Faster than LIKE 
     31    uses_custom_queryset = True 
     32 
     33class DatabaseOperations(BaseDatabaseOperations): 
     34    _max_name_length = 31 
     35    def __init__(self): 
     36        self._firebird_version = None 
     37     
     38    def get_generator_name(self, name): 
     39        return '%s_G' % util.truncate_name(name, self._max_name_length-2).upper() 
     40         
     41    def get_trigger_name(self, name): 
     42        return '%s_T' % util.truncate_name(name, self._max_name_length-2).upper()  
     43     
     44    def _get_firebird_version(self): 
     45        if self._firebird_version is None: 
     46            from django.db import connection 
     47            self._firebird_version = [int(val) for val in connection.server_version.split()[-1].split('.')] 
     48        return self._firebird_version 
     49    firebird_version = property(_get_firebird_version) 
     50     
     51    def _autoinc_sql_with_style(self, style, table_name, column_name): 
     52        """ 
     53        To simulate auto-incrementing primary keys in Firebird, we have to 
     54        create a generator and a trigger. 
     55     
     56        Create the generators and triggers names based only on table name 
     57        since django only support one auto field per model 
     58        """ 
     59         
     60        KWD = style.SQL_KEYWORD 
     61        TBL = style.SQL_TABLE 
     62        FLD = style.SQL_FIELD 
     63     
     64        generator_name = self.get_generator_name(table_name) 
     65        trigger_name = self.get_trigger_name(table_name) 
     66        column_name = self.quote_name(column_name) 
     67        table_name = self.quote_name(table_name) 
     68         
     69        generator_sql = "%s %s;" % ( KWD('CREATE GENERATOR'),  
     70                                     TBL(generator_name))       
     71        trigger_sql = "\n".join([ 
     72            "%s %s %s %s" % ( \ 
     73            KWD('CREATE TRIGGER'), TBL(trigger_name), KWD('FOR'), 
     74            TBL(table_name)), 
     75            "%s 0 %s" % (KWD('ACTIVE BEFORE INSERT POSITION'), KWD('AS')), 
     76            KWD('BEGIN'),  
     77            "  %s ((%s.%s %s) %s (%s.%s = 0)) %s" % ( \ 
     78                KWD('IF'), 
     79                KWD('NEW'), FLD(column_name), KWD('IS NULL'), 
     80                KWD('OR'), KWD('NEW'), FLD(column_name), 
     81                KWD('THEN') 
     82            ), 
     83            "  %s" % KWD('BEGIN'),  
     84            "    %s.%s = %s(%s, 1);" % ( \ 
     85                KWD('NEW'), FLD(column_name), 
     86                KWD('GEN_ID'), TBL(generator_name) 
     87            ), 
     88            "  %s" % KWD('END'), 
     89            KWD('END') 
     90            ]) 
     91        return (generator_sql, trigger_sql) 
     92     
     93    def autoinc_sql(self, table_name, column_name): 
     94        # style argument disappeared, so we'll just import django's dummy 
     95        from django.core.management.color import no_style, color_style 
     96        return self._autoinc_sql_with_style(no_style(), table_name, column_name) 
     97 
     98    def max_name_length(self): 
     99        return self._max_name_length 
     100 
     101    def query_set_class(self, DefaultQuerySet): 
     102        from django.db import connection 
     103        from django.db.models.query import EmptyResultSet, GET_ITERATOR_CHUNK_SIZE, quote_only_if_word 
     104 
     105        class FirebirdQuerySet(DefaultQuerySet): 
     106        #TODO: Optimize for Firebird and take full advanatage of its power 
     107        # Now it's just a copy of django.db.models.query._QuerySet 
     108        # with LIMIT/OFFSET removed and FIRST/SKIP added 
     109            def _get_sql_clause(self): 
     110                from django.db.models.query import SortedDict, handle_legacy_orderlist, orderfield2column, fill_table_cache 
     111                qn = connection.ops.quote_name 
     112                opts = self.model._meta 
     113 
     114                # Construct the fundamental parts of the query: SELECT X FROM Y WHERE Z. 
     115                select = ["%s.%s" % (qn(opts.db_table), qn(f.column)) for f in opts.fields] 
     116                tables = [quote_only_if_word(t) for t in self._tables] 
     117                joins = SortedDict() 
     118                where = self._where[:] 
     119                params = self._params[:] 
     120 
     121                # Convert self._filters into SQL. 
     122                joins2, where2, params2 = self._filters.get_sql(opts) 
     123                joins.update(joins2) 
     124                where.extend(where2) 
     125                params.extend(params2) 
     126 
     127                # Add additional tables and WHERE clauses based on select_related. 
     128                if self._select_related: 
     129                    fill_table_cache(opts, select, tables, where, 
     130                                     old_prefix=opts.db_table, 
     131                                     cache_tables_seen=[opts.db_table], 
     132                                     max_depth=self._max_related_depth) 
     133 
     134                # Add any additional SELECTs. 
     135                if self._select: 
     136                    select.extend(['(%s) AS %s' % (quote_only_if_word(s[1]), qn(s[0])) for s in self._select.items()]) 
     137 
     138                # Start composing the body of the SQL statement. 
     139                sql = [" FROM", qn(opts.db_table)] 
     140 
     141                # Compose the join dictionary into SQL describing the joins. 
     142                if joins: 
     143                    sql.append(" ".join(["%s %s AS %s ON %s" % (join_type, table, alias, condition) 
     144                                    for (alias, (table, join_type, condition)) in joins.items()])) 
     145 
     146                # Compose the tables clause into SQL. 
     147                if tables: 
     148                    sql.append(", " + ", ".join(tables)) 
     149 
     150                # Compose the where clause into SQL. 
     151                if where: 
     152                    sql.append(where and "WHERE " + " AND ".join(where)) 
     153 
     154                # ORDER BY clause 
     155                order_by = [] 
     156                if self._order_by is not None: 
     157                    ordering_to_use = self._order_by 
     158                else: 
     159                    ordering_to_use = opts.ordering 
     160                for f in handle_legacy_orderlist(ordering_to_use): 
     161                    if f == '?': # Special case. 
     162                        order_by.append(connection.ops.random_function_sql()) 
     163                    else: 
     164                        if f.startswith('-'): 
     165                            col_name = f[1:] 
     166                            order = "DESC" 
     167                        else: 
     168                            col_name = f 
     169                            order = "ASC" 
     170                        if "." in col_name: 
     171                            table_prefix, col_name = col_name.split('.', 1) 
     172                            table_prefix = qn(table_prefix) + '.' 
     173                        else: 
     174                            # Use the database table as a column prefix if it wasn't given, 
     175                            # and if the requested column isn't a custom SELECT. 
     176                            if "." not in col_name and col_name not in (self._select or ()): 
     177                                table_prefix = qn(opts.db_table) + '.' 
     178                            else: 
     179                                table_prefix = '' 
     180                        order_by.append('%s%s %s' % (table_prefix, qn(orderfield2column(col_name, opts)), order)) 
     181                if order_by: 
     182                    sql.append("ORDER BY " + ", ".join(order_by)) 
     183 
     184                return select, " ".join(sql), params 
     185             
     186            def iterator(self): 
     187                "Performs the SELECT database lookup of this QuerySet." 
     188                from django.db.models.query import get_cached_row 
     189                 
     190                try: 
     191                    select, sql, params = self._get_sql_clause() 
     192                except EmptyResultSet: 
     193                    raise StopIteration  
     194                     
     195                # self._select is a dictionary, and dictionaries' key order is 
     196                # undefined, so we convert it to a list of tuples. 
     197                extra_select = self._select.items() 
     198                 
     199                cursor = connection.cursor()  
     200                limit_offset_before = ""  
     201                if self._limit is not None:  
     202                    limit_offset_before += "FIRST %s " % self._limit  
     203                    if self._offset:  
     204                        limit_offset_before += "SKIP %s " % self._offset 
     205                else: 
     206                    assert self._offset is None, "'offset' is not allowed without 'limit'" 
     207                cursor.execute("SELECT " + limit_offset_before + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params) 
     208                fill_cache = self._select_related 
     209                fields = self.model._meta.fields 
     210                index_end = len(fields) 
     211                has_resolve_columns = hasattr(self, 'resolve_columns') 
     212                while 1: 
     213                    rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE) 
     214                    if not rows: 
     215                        raise StopIteration 
     216                    for row in rows: 
     217                        if has_resolve_columns: 
     218                            row = self.resolve_columns(row, fields) 
     219                        if fill_cache: 
     220                            obj, index_end = get_cached_row(klass=self.model, row=row, 
     221                                                            index_start=0, max_depth=self._max_related_depth) 
     222                        else: 
     223                            obj = self.model(*row[:index_end]) 
     224                        for i, k in enumerate(extra_select): 
     225                            setattr(obj, k[0], row[index_end+i]) 
     226                        yield obj 
     227        return FirebirdQuerySet 
     228     
     229 
     230    def quote_name(self, name): 
     231        # Trancate and quote once. No need for uppercase since 
     232        # we quote autofields too 
     233        if not name.startswith('"') and not name.endswith('"'): 
     234            name = '"%s"' % util.truncate_name(name, self._max_name_length) 
     235        return name #.upper() 
     236     
     237    def field_cast_sql(self, db_type): 
     238        return '%s' 
     239     
     240    def last_insert_id(self, cursor, table_name, pk_name): 
     241        stmt = 'SELECT GEN_ID(%s, 0) from RDB$DATABASE' 
     242        cursor.execute(stmt % self.get_generator_name(table_name)) 
     243        return cursor.fetchone()[0] 
     244 
     245    def date_extract_sql(self, lookup_type, column_name): 
     246        # lookup_type is 'year', 'month', 'day' 
     247        return "EXTRACT(%s FROM %s)" % (lookup_type, column_name) 
     248 
     249    def date_trunc_sql(self, lookup_type, column_name): 
     250        if lookup_type == 'year': 
     251             sql = "EXTRACT(year FROM %s)||'-01-01 00:00:00'" % column_name 
     252        elif lookup_type == 'month': 
     253            sql = "EXTRACT(year FROM %s)||'-'||EXTRACT(month FROM %s)||'-01 00:00:00'" % (column_name, column_name) 
     254        elif lookup_type == 'day': 
     255            sql = "EXTRACT(year FROM %s)||'-'||EXTRACT(month FROM %s)||'-'||EXTRACT(day FROM %s)||' 00:00:00'" % (column_name, column_name, column_name) 
     256        return "CAST(%s AS TIMESTAMP)" % sql 
     257     
     258    def cascade_delete_update_sql(self): 
     259        # Solves FK problems with sql_flush 
     260        return " ON DELETE CASCADE ON UPDATE CASCADE" 
     261     
     262    def datetime_cast_sql(self): 
     263        return None 
     264 
     265    def limit_offset_sql(self, limit, offset=None): 
     266        # limits are handled in custom FirebirdQuerySet  
     267        assert False, 'Limits are handled in a different way in Firebird' 
     268        return "" 
     269 
     270    def random_function_sql(self): 
     271        return "rand()" 
     272 
     273    def pk_default_value(self): 
     274        return "NULL" 
     275     
     276    def start_transaction_sql(self): 
     277        return "" 
     278 
     279    def sequence_reset_sql(self, style, model_list): 
     280        from django.db import models 
     281        output = [] 
     282        for model in model_list: 
     283            for f in model._meta.fields: 
     284                if isinstance(f, models.AutoField): 
     285                    generator_name = self.get_generator_name(model._meta.db_table) 
     286                    output.append("SET GENERATOR %s TO 0;" % generator_name) 
     287                    break # Only one AutoField is allowed per model, so don't bother continuing. 
     288            for f in model._meta.many_to_many: 
     289                generator_name = self.get_generator_name(f.m2m_db_table()) 
     290                output.append("SET GENERATOR %s TO 0;" % generator_name) 
     291        return output 
     292     
     293    def sql_flush(self, style, tables, sequences): 
     294        if tables: 
     295            # FK constraints gave us a lot of trouble with default values 
     296            # that was a reason behind very ugly and dangerous code here 
     297            # Solved with "ON DELETE CASCADE" with all FK references         
     298            sql = ['%s %s %s;' % \ 
     299                    (style.SQL_KEYWORD('DELETE'), 
     300                     style.SQL_KEYWORD('FROM'), 
     301                     style.SQL_FIELD(self.quote_name(table)) 
     302                     ) for table in tables] 
     303            for generator_info in sequences: 
     304                table_name = generator_info['table'] 
     305                query = "SET GENERATOR %s TO 0;" % self.get_generator_name(table_name) 
     306                sql.append(query) 
     307            return sql 
     308        else: 
     309            return [] 
     310 
     311#    def fulltext_search_sql(self, field_name): 
     312#        return field_name + ' CONTAINING %s' 
     313         
     314    def drop_sequence_sql(self, table): 
     315        return "DROP GENERATOR %s;" % self.get_generator_name(table) 
     316         
     317    def last_executed_query(self, cursor, sql, params): 
     318        """ 
     319        Returns a string of the query last executed by the given cursor, with 
     320        placeholders replaced with actual values. 
     321 
     322        `sql` is the raw query containing placeholders, and `params` is the 
     323        sequence of parameters. These are used by default, but this method 
     324        exists for database backends to provide a better implementation 
     325        according to their own quoting schemes. 
     326        """ 
     327        from django.utils.encoding import smart_unicode, force_unicode 
     328 
     329        # Convert params to contain Unicode values. 
     330        to_unicode = lambda s: force_unicode(s, strings_only=True) 
     331        if isinstance(params, (list, tuple)): 
     332            u_params = tuple([to_unicode(val) for val in params]) 
     333        else: 
     334            u_params = dict([(to_unicode(k), to_unicode(v)) for k, v in params.items()]) 
     335        try: 
     336            #Extracts sql right from KInterbasDB's prepared statement 
     337            return smart_unicode(cursor.query) % u_params 
     338        except TypeError: 
     339            return smart_unicode(sql) % u_params 
     340 
     341class FirebirdCursorWrapper(object): 
     342    """ 
     343    Django uses "format" ('%s') style placeholders, but firebird uses "qmark" ('?') style. 
     344    This fixes it -- but note that if you want to use a literal "%s" in a query, 
     345    you'll need to use "%%s". 
     346     
     347    We also do all automatic type conversions here. 
     348    """ 
     349    import kinterbasdb.typeconv_datetime_stdlib as tc_dt 
     350    import kinterbasdb.typeconv_fixed_decimal as tc_fd 
     351    import kinterbasdb.typeconv_text_unicode as tc_tu 
     352    import django.utils.encoding as dj_ue 
     353    
     354    def timestamp_conv_in(self, timestamp): 
     355        if isinstance(timestamp, basestring): 
     356            #Replaces 6 digits microseconds to 4 digits allowed in Firebird 
     357            timestamp = timestamp[:24] 
     358        return self.tc_dt.timestamp_conv_in(timestamp) 
     359     
     360    def time_conv_in(self, value): 
     361        import datetime 
     362        if isinstance(value, datetime.datetime): 
     363            value = datetime.time(value.hour, value.minute, value.second, value.micosecond)        
     364        return self.tc_dt.time_conv_in(value)  
     365     
     366    def ascii_conv_in(self, text):   
     367        return self.dj_eu.smart_str(text, 'ascii') 
     368 
     369    def unicode_conv_in(self, text): 
     370        return self.tc_tu.unicode_conv_in((self.dj_ue.force_unicode(text[0]), self.FB_CHARSET_CODE)) 
     371 
     372    def blob_conv_in(self, text):  
     373        return self.tc_tu.unicode_conv_in((self.dj_ue.force_unicode(text), self.FB_CHARSET_CODE)) 
     374 
     375    def blob_conv_out(self, text): 
     376        return self.tc_tu.unicode_conv_out((text, self.FB_CHARSET_CODE)) 
     377         
     378    def __init__(self, cursor): 
     379        from django.conf import settings 
     380        self.FB_CHARSET_CODE = 3 #UNICODE_FSS 
     381        if hasattr(settings, 'FIREBIRD_CHARSET'): 
     382            if settings.FIREBIRD_CHARSET == 'UTF8': 
     383                self.FB_CHARSET_CODE = 4 # UTF-8 with Firebird 2.0+     
     384        self.cursor = cursor 
     385         
     386        # Prepared Statement  
     387        # http://kinterbasdb.sourceforge.net/dist_docs/usage.html#adv_prepared_statements 
     388        # Need to decide wether they are useful or not 
     389        # Maybe add prepare, execute_prep and executemany_pep methods here 
     390        # and rewrite QuerySet to take advantage of them? 
     391        # Could speed the things up 
     392        self._statement = None 
     393        self.cursor.set_type_trans_in({ 
     394            'DATE':             self.tc_dt.date_conv_in, 
     395            'TIME':             self.time_conv_in, 
     396            'TIMESTAMP':        self.timestamp_conv_in, 
     397            'FIXED':            self.tc_fd.fixed_conv_in_imprecise, 
     398            'TEXT':             self.ascii_conv_in, 
     399            'TEXT_UNICODE':     self.unicode_conv_in, 
     400            'BLOB':             self.blob_conv_in 
     401        }) 
     402        self.cursor.set_type_trans_out({ 
     403            'DATE':             self.tc_dt.date_conv_out, 
     404            'TIME':             self.tc_dt.time_conv_out, 
     405            'TIMESTAMP':        self.tc_dt.timestamp_conv_out, 
     406            'FIXED':            self.tc_fd.fixed_conv_out_imprecise, 
     407            'TEXT':             self.dj_ue.force_unicode, 
     408            'TEXT_UNICODE':     self.tc_tu.unicode_conv_out, 
     409            'BLOB':             self.blob_conv_out 
     410        }) 
     411     
     412    def _get_query(self): 
     413        if self._statement: 
     414            return self._statement.sql 
     415    def _get_statement(self): 
     416        if self._statement: 
     417            return self._statement 
     418    query = property(_get_query) 
     419    statement = property(_get_statement) 
     420         
     421    def execute(self, query, params=()): 
     422        query = self.convert_query(query, len(params)) 
     423        if self._get_query() != query: 
     424            try: 
     425                self._statement = self.cursor.prep(query) 
     426            except Database.ProgrammingError, e: 
     427                print query % params 
     428                raise DatabaseError, e 
     429        return self.cursor.execute(self._statement, params) 
     430 
     431    def executemany(self, query, param_list): 
     432        try: 
     433            query = self.convert_query(query, len(param_list[0])) 
     434        except IndexError: 
     435            return None 
     436        if self._get_query() != query: 
     437            self._statement = self.cursor.prep(query) 
     438        return self.cursor.executemany(self._statement, param_list) 
     439 
     440    def convert_query(self, query, num_params): 
     441        try: 
     442            return query % tuple("?" * num_params) 
     443        except TypeError, e: 
     444            print query, num_params 
     445            raise TypeError, e 
     446     
     447    def __getattr__(self, attr): 
     448        if attr in self.__dict__: 
     449            return self.__dict__[attr] 
     450        else: 
     451            return getattr(self.cursor, attr) 
     452 
     453class DatabaseWrapper(BaseDatabaseWrapper): 
     454    features = DatabaseFeatures() 
     455    ops = DatabaseOperations() 
     456    operators = { 
     457        'exact': '= %s', 
     458        'iexact': '= UPPER(%s)', 
     459        'contains': "LIKE %s ESCAPE'\\'", 
     460        'icontains': 'CONTAINING %s', #case is ignored 
     461        'gt': '> %s', 
     462        'gte': '>= %s', 
     463        'lt': '< %s', 
     464        'lte': '<= %s', 
     465        'startswith': 'STARTING WITH %s', #looks to be faster then LIKE 
     466        'endswith': "LIKE %s ESCAPE'\\'", 
     467        'istartswith': 'STARTING WITH UPPER(%s)', 
     468        'iendswith': "LIKE UPPER(%s) ESCAPE'\\'", 
     469    } 
     470    _current_cursor = None 
     471    def _connect(self, settings): 
     472        if settings.DATABASE_NAME == '': 
     473            from django.core.exceptions import ImproperlyConfigured 
     474            raise ImproperlyConfigured, "You need to specify DATABASE_NAME in your Django settings file." 
     475        charset = 'UNICODE_FSS' 
     476        if hasattr(settings, 'FIREBIRD_CHARSET'): 
     477            if settings.FIREBIRD_CHARSET == 'UTF8': 
     478                charset = 'UTF8'     
     479        kwargs = {'charset' : charset } 
     480        if settings.DATABASE_HOST: 
     481            kwargs['dsn'] = "%s:%s" % (settings.DATABASE_HOST, settings.DATABASE_NAME) 
     482        else: 
     483            kwargs['dsn'] = "localhost:%s" % settings.DATABASE_NAME 
     484        if settings.DATABASE_USER: 
     485            kwargs['user'] = settings.DATABASE_USER 
     486        if settings.DATABASE_PASSWORD: 
     487            kwargs['password'] = settings.DATABASE_PASSWORD 
     488        self.connection = Database.connect(**kwargs) 
     489        assert self.charset == charset 
     490        try: 
     491            self.connection.execute_immediate(""" 
     492                DECLARE EXTERNAL FUNCTION rand 
     493                RETURNS DOUBLE PRECISION 
     494                BY VALUE ENTRY_POINT 'IB_UDF_rand' MODULE_NAME 'ib_udf'; 
     495            """) 
     496        except Database.ProgrammingError: 
     497            pass #Already defined  
     498         
     499    def cursor(self, name=None): 
     500        #Cursors can be named 
     501        #http://kinterbasdb.sourceforge.net/dist_docs/usage.html#adv_named_cursors 
     502        #and maybe useful for scrolling updates and deletes 
     503        from django.conf import settings 
     504        cursor = self._cursor(settings, name) 
     505        if settings.DEBUG: 
     506            return self.make_debug_cursor(cursor) 
     507        return cursor 
     508     
     509    def _cursor(self, settings, name): 
     510        if self.connection is None: 
     511            self._connect(settings) 
     512        cursor = self.connection.cursor() 
     513        if name: 
     514            cursor.name = name 
     515        cursor = FirebirdCursorWrapper(cursor) 
     516        self._current_cursor = cursor 
     517        return cursor 
     518     
     519    #Returns query from prepared statement 
     520    def _get_query(self): 
     521        if self._current_cursor: 
     522            return self._current_cursor.query 
     523    query = property(_get_query) 
     524    #Returns prepared statement itself 
     525    def _get_statement(self): 
     526        if self._current_cursor: 
     527            return self._current_cursor.statement 
     528    statement = property(_get_statement) 
     529         
     530     
     531    def __getattr__(self, attr): 
     532        if attr in self.__dict__: 
     533            return self.__dict__[attr] 
     534        else: 
     535            return getattr(self.connection, attr) 
     536     
     537 
  • django/db/backends/firebird/client.py

    old new  
     1from django.conf import settings 
     2import os 
     3 
     4def runshell(): 
     5    args = [settings.DATABASE_NAME] 
     6    args += ["-u %s" % settings.DATABASE_USER] 
     7    if settings.DATABASE_PASSWORD: 
     8        args += ["-p %s" % settings.DATABASE_PASSWORD] 
     9    if 'FIREBIRD' not in os.environ: 
     10        path = '/opt/firebird/bin/' 
     11    os.system(path + 'isql ' + ' '.join(args)) 
  • django/db/backends/firebird/introspection.py

    old new  
     1from django.db import transaction 
     2from django.db.backends.firebird.base import DatabaseOperations 
     3 
     4quote_name = DatabaseOperations().quote_name 
     5 
     6def get_table_list(cursor): 
     7    "Returns a list of table names in the current database." 
     8    cursor.execute(""" 
     9        SELECT rdb$relation_name FROM rdb$relations 
     10        WHERE rdb$system_flag = 0 AND rdb$view_blr IS NULL ORDER BY rdb$relation_name""") 
     11    return [str(row[0].strip().lower()) for row in cursor.fetchall()] 
     12 
     13def get_table_description(cursor, table_name): 
     14    "Returns a description of the table, with the DB-API cursor.description interface." 
     15    #cursor.execute("SELECT FIRST 1 * FROM %s" % quote_name(table_name)) 
     16    #return cursor.description 
     17    # (name, type_code, display_size, internal_size, precision, scale, null_ok) 
     18    cursor.execute(""" 
     19        SELECT DISTINCT R.RDB$FIELD_NAME AS FNAME, 
     20                  F.RDB$FIELD_TYPE AS FTYPE, 
     21                  F.RDB$FIELD_LENGTH AS FLENGTH, 
     22                  F.RDB$FIELD_PRECISION AS FPRECISION, 
     23                  F.RDB$FIELD_SCALE AS FSCALE, 
     24                  R.RDB$NULL_FLAG AS NULL_FLAG, 
     25                  R.RDB$FIELD_POSITION 
     26        FROM RDB$RELATION_FIELDS R 
     27             JOIN RDB$FIELDS F ON R.RDB$FIELD_SOURCE=F.RDB$FIELD_NAME 
     28        WHERE F.RDB$SYSTEM_FLAG=0 and R.RDB$RELATION_NAME= %s 
     29        ORDER BY R.RDB$FIELD_POSITION 
     30    """, (table_name,)) 
     31    return [(row[0].lower().rstrip(), row[1], row[2], row[2] or 0, row[3], row[4], row[5] and True or False) for row in cursor.fetchall()] 
     32 
     33 
     34def get_relations(cursor, table_name): 
     35    """ 
     36    Returns a dictionary of {field_index: (field_index_other_table, other_table)} 
     37    representing all relationships to the given table. Indexes are 0-based. 
     38    """ 
     39    cursor.execute(""" 
     40        SELECT seg.rdb$field_name, seg_ref.rdb$field_name, idx_ref.rdb$relation_name 
     41        FROM rdb$indices idx 
     42        INNER JOIN rdb$index_segments seg 
     43            ON seg.rdb$index_name = idx.rdb$index_name 
     44        INNER JOIN rdb$indices idx_ref 
     45            ON idx_ref.rdb$index_name = idx.rdb$foreign_key 
     46        INNER JOIN rdb$index_segments seg_ref 
     47            ON seg_ref.rdb$index_name = idx_ref.rdb$index_name 
     48        WHERE idx.rdb$relation_name = %s 
     49            AND idx.rdb$foreign_key IS NOT NULL""", [table_name]) 
     50 
     51    relations = {} 
     52    for row in cursor.fetchall(): 
     53        relations[row[0].rstrip()] = (row[1].strip(), row[2].strip()) 
     54    return relations 
     55 
     56def get_indexes(cursor, table_name): 
     57    """ 
     58    Returns a dictionary of fieldname -> infodict for the given table, 
     59    where each infodict is in the format: 
     60        {'primary_key': boolean representing whether it's the primary key, 
     61         'unique': boolean representing whether it's a unique index} 
     62    """ 
     63 
     64    # This query retrieves each field name and index type on the given table. 
     65    cursor.execute(""" 
     66        SELECT seg.rdb$field_name, const.rdb$constraint_type 
     67        FROM rdb$relation_constraints const 
     68        LEFT JOIN rdb$index_segments seg 
     69            ON seg.rdb$index_name = const.rdb$index_name 
     70        WHERE const.rdb$relation_name = %s 
     71            AND (const.rdb$constraint_type = 'PRIMARY KEY' 
     72                OR const.rdb$constraint_type = 'UNIQUE')""", [table_name]) 
     73    indexes = {} 
     74    for row in cursor.fetchall(): 
     75        indexes[row[0].strip()] = { 
     76            'primary_key': ('PRIMARY KEY' == row[1].strip()), 
     77            'unique': ('UNIQUE' == row[1].strip())} 
     78    return indexes 
     79 
     80# Maps type codes to Django Field types. 
     81# !todo 
     82DATA_TYPES_REVERSE = { 
     83    7: 'BooleanField', 
     84    7: 'SmallIntegerField', 
     85    8: 'IntegerField', 
     86    261: 'TextField', 
     87    37: 'IPAddressField', 
     88    37: 'CharField', 
     89    12: 'DateField', 
     90    13: 'TimeField', 
     91    35: 'DateTimeField', 
     92    10: 'FloatField', 
     93} 
  • django/db/backends/firebird/creation.py

    old new  
     1# This dictionary maps Field objects to their associated Firebird column 
     2# types, as strings. Column-type strings can contain format strings; they'll 
     3# be interpolated against the values of Field.__dict__ before being output. 
     4# If a column type is set to None, it won't be included in the output. 
     5 
     6import sys, os.path 
     7from kinterbasdb import connect, create_database 
     8from django.core.management import call_command 
     9 
     10#TODO: Implement CHECKs 
     11DATA_TYPES = { 
     12    'ASCIICharField':                'varchar(%(max_length)s) CHARACTER SET ASCII', 
     13    'AutoField':                     'integer', 
     14    'BooleanField':                  'smallint', # CHECK ("%(column)s" IN (0,1))', 
     15    'CharField':                     'varchar(%(max_length)s)', 
     16    'CommaSeparatedIntegerField':    'varchar(%(max_length)s) CHARACTER SET ASCII', 
     17    'DateField':                     'date', 
     18    'DateTimeField':                 'timestamp', 
     19    'DecimalField':                  'numeric(%(max_digits)s, %(decimal_places)s)', 
     20    'FileField':                     'varchar(%(max_length)s)', 
     21    'FilePathField':                 'varchar(%(max_length)s)', 
     22    'FloatField':                    'double precision', 
     23    'ImageField':                    'varchar(%(max_length)s) CHARACTER SET ASCII', 
     24    'IntegerField':                  'integer', 
     25    'IPAddressField':                'varchar(15) CHARACTER SET ASCII', 
     26    'NullBooleanField':              'smallint', #CHECK (("%(column)"s IN (0,1)) OR ("%(column)s" IS NULL))', 
     27    'OneToOneField':                 'integer', 
     28    'PhoneNumberField':              'varchar(20)', # CHARACTER SET ASCII', 
     29    'PositiveIntegerField':          'integer', # CHECK ("%(column)s" >= 0)', 
     30    'PositiveSmallIntegerField':     'smallint', 
     31    'SlugField':                     'varchar(%(max_length)s)', 
     32    'SmallIntegerField':             'smallint', 
     33    'TextField':                     'blob sub_type text', 
     34    'TimeField':                     'time', 
     35    'URLField':                      'varchar(%(max_length)s) CHARACTER SET ASCII', 
     36    'USStateField':                  'varchar(2) CHARACTER SET ASCII', 
     37} 
     38 
     39TEST_DATABASE_PREFIX = 'test_' 
     40 
     41def create_test_db(settings, connection, verbosity, autoclobber): 
     42    # KInterbasDB supports dynamic database creation and deletion  
     43    # via the module-level function create_database and the method Connection.drop_database. 
     44        
     45    if settings.TEST_DATABASE_NAME: 
     46        TEST_DATABASE_NAME = settings.TEST_DATABASE_NAME 
     47    else: 
     48        dbnametuple = os.path.split(settings.DATABASE_NAME) 
     49        TEST_DATABASE_NAME = os.path.join(dbnametuple[0], TEST_DATABASE_PREFIX + dbnametuple[1]) 
     50     
     51    dsn = "localhost:%s" % TEST_DATABASE_NAME 
     52    if settings.DATABASE_HOST: 
     53        dsn = "%s:%s" % (settings.DATABASE_HOST, TEST_DATABASE_NAME) 
     54     
     55    if os.path.isfile(TEST_DATABASE_NAME): 
     56        sys.stderr.write("Database %s already exists\n" % TEST_DATABASE_NAME) 
     57        if not autoclobber: 
     58            confirm = raw_input("Type 'yes' if you would like to try deleting the test database '%s', or 'no' to cancel: " % TEST_DATABASE_NAME) 
     59        if autoclobber or confirm == 'yes': 
     60            if verbosity >= 1: 
     61                print "Destroying old test database..." 
     62            old_connection = connect(dsn=dsn, user=settings.DATABASE_USER, password=settings.DATABASE_PASSWORD) 
     63            old_connection.drop_database() 
     64        else: 
     65                print "Tests cancelled." 
     66                sys.exit(1) 
     67             
     68    if verbosity >= 1: 
     69        print "Creating test database..." 
     70    try: 
     71        charset = 'UNICODE_FSS' 
     72        if hasattr(settings, 'FIREBIRD_CHARSET'): 
     73            if settings.FIREBIRD_CHARSET == 'UTF8': 
     74                charset='UTF8'                 
     75        create_database("create database '%s' user '%s' password '%s' default character set %s" % \ 
     76            (dsn, settings.DATABASE_USER, settings.DATABASE_PASSWORD, charset)) 
     77    except Exception, e: 
     78        sys.stderr.write("Got an error creating the test database: %s\n" % e) 
     79        sys.exit(2) 
     80             
     81 
     82    connection.close()  
     83    settings.DATABASE_NAME = TEST_DATABASE_NAME 
     84 
     85    call_command('syncdb', verbosity=verbosity, interactive=False) 
     86 
     87    if settings.CACHE_BACKEND.startswith('db://'): 
     88        cache_name = settings.CACHE_BACKEND[len('db://'):] 
     89        call_command('createcachetable', cache_name) 
     90 
     91    # Get a cursor (even though we don't need one yet). This has 
     92    # the side effect of initializing the test database. 
     93    cursor = connection.cursor() 
     94 
     95    return TEST_DATABASE_NAME 
     96 
     97def destroy_test_db(settings, connection, old_database_name, verbosity): 
     98    # KInterbasDB supports dynamic database deletion via the method Connection.drop_database. 
     99    if verbosity >= 1: 
     100        print "Destroying test database..." 
     101    connection.drop_database() 
     102     
     103 
  • django/core/management/sql.py

    old new  
    263263        # Make the definition (e.g. 'foo VARCHAR(30)') for this field. 
    264264        field_output = [style.SQL_FIELD(qn(f.column)), 
    265265            style.SQL_COLTYPE(col_type)] 
    266         field_output.append(style.SQL_KEYWORD('%sNULL' % (not f.null and 'NOT ' or ''))) 
     266        nullstring = "" 
     267        if connection.features.needs_default_null: 
     268            nullstring = "DEFAULT " 
     269        field_output.append(style.SQL_KEYWORD('%sNULL' % (not f.null and 'NOT ' or nullstring))) 
    267270        if f.unique and (not f.primary_key or connection.features.allows_unique_and_pk): 
    268271            field_output.append(style.SQL_KEYWORD('UNIQUE')) 
    269272        if f.primary_key: 
     
    276279