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

File firebird-svn6652-full-updated.diff, 45.8 KB (added by Ivan Illarionov, 17 years ago)

some more fixes

  • django/db/models/base.py

     
    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

     
    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

     
    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

     
     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

     
     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

     
     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

     
     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

     
    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            if inline_references and f.rel.to in known_models:
    277280                field_output.append(style.SQL_KEYWORD('REFERENCES') + ' ' + \
    278281                    style.SQL_TABLE(qn(f.rel.to._meta.db_table)) + ' (' + \
    279                     style.SQL_FIELD(qn(f.rel.to._meta.get_field(f.rel.field_name).column)) + ')' +
    280                     connection.ops.deferrable_sql()
     282                    style.SQL_FIELD(qn(f.rel.to._meta.get_field(f.rel.field_name).column)) + ')' + \
     283                    connection.ops.cascade_delete_update_sql() + connection.ops.deferrable_sql()
    281284                )
    282285            else:
    283286                # We haven't yet created the table to which this field
     
    335338                final_output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' % \
    336339                    (qn(r_table), truncate_name(r_name, connection.ops.max_name_length()),
    337340                    qn(r_col), qn(table), qn(col),
     341                    connection.ops.cascade_delete_update_sql(),
    338342                    connection.ops.deferrable_sql()))
    339343            del pending_references[model]
    340344    return final_output
     
    364368                tablespace_sql))
    365369            if inline_references:
    366370                deferred = []
    367                 table_output.append('    %s %s %s %s (%s)%s,' %
     371                table_output.append('    %s %s %s %s (%s)%s%s,' %
    368372                    (style.SQL_FIELD(qn(f.m2m_column_name())),
    369373                    style.SQL_COLTYPE(models.ForeignKey(model).db_type()),
    370374                    style.SQL_KEYWORD('NOT NULL REFERENCES'),
    371375                    style.SQL_TABLE(qn(opts.db_table)),
    372376                    style.SQL_FIELD(qn(opts.pk.column)),
     377                    connection.ops.cascade_delete_update_sql(),
    373378                    connection.ops.deferrable_sql()))
    374                 table_output.append('    %s %s %s %s (%s)%s,' %
     379                table_output.append('    %s %s %s %s (%s)%s%s,' %
    375380                    (style.SQL_FIELD(qn(f.m2m_reverse_name())),
    376381                    style.SQL_COLTYPE(models.ForeignKey(f.rel.to).db_type()),
    377382                    style.SQL_KEYWORD('NOT NULL REFERENCES'),
    378383                    style.SQL_TABLE(qn(f.rel.to._meta.db_table)),
    379384                    style.SQL_FIELD(qn(f.rel.to._meta.pk.column)),
     385                    connection.ops.cascade_delete_update_sql(),
    380386                    connection.ops.deferrable_sql()))
    381387            else:
    382388                table_output.append('    %s %s %s,' %
     
    412418                (qn(r_table),
    413419                truncate_name(r_name, connection.ops.max_name_length()),
    414420                qn(r_col), qn(table), qn(col),
     421                connection.ops.cascade_delete_update_sql(),
    415422                connection.ops.deferrable_sql()))
    416423
    417424            # Add any extra SQL needed to support auto-incrementing PKs
  • django/contrib/contenttypes/models.py

     
    11from django.db import models
    22from django.utils.translation import ugettext_lazy as _
    33from django.utils.encoding import smart_unicode
     4from django.conf import settings
    45
    56CONTENT_TYPE_CACHE = {}
    67class ContentTypeManager(models.Manager):
     
    3132        global CONTENT_TYPE_CACHE
    3233        CONTENT_TYPE_CACHE = {}
    3334
    34 class ContentType(models.Model):
     35class ContentType(models.Model):   
    3536    name = models.CharField(max_length=100)
    36     app_label = models.CharField(max_length=100)
    37     model = models.CharField(_('python model class name'), max_length=100)
     37    # Need this because of Firebird restrictions on index key size < 252 bytes
     38    if settings.DATABASE_ENGINE == 'firebird':
     39        app_label = models.ASCIICharField(max_length=96)
     40        model = models.ASCIICharField(_('python model class name'), max_length=96)
     41    else:
     42        app_label = models.CharField(max_length=100)
     43        model = models.CharField(_('python model class name'), max_length=100)
    3844    objects = ContentTypeManager()
    3945    class Meta:
    4046        verbose_name = _('content type')
  • django/contrib/auth/models.py

     
    66from django.contrib.contenttypes.models import ContentType
    77from django.utils.encoding import smart_str
    88from django.utils.translation import ugettext_lazy as _
     9from django.conf import settings
    910import datetime
    1011import urllib
    1112
     
    7273    """
    7374    name = models.CharField(_('name'), max_length=50)
    7475    content_type = models.ForeignKey(ContentType)
    75     codename = models.CharField(_('codename'), max_length=100)
     76    # Need this because of Firebird restrictions on index key size < 252 bytes
     77    if settings.DATABASE_ENGINE == 'firebird':
     78        codename = models.ASCIICharField(_('codename'), max_length=100)
     79    else:
     80        codename = models.CharField(_('codename'), max_length=100)
    7681
    7782    class Meta:
    7883        verbose_name = _('permission')
  • tests/regressiontests/serializers_regress/tests.py

     
    266266                         data[2]._meta.get_field('data').empty_strings_allowed and
    267267                         data[3] is None)]
    268268
     269#JUST SKIP some for now....
     270if settings.DATABASE_ENGINE == 'firebird':
     271    test_data = test_data[:5]
     272
    269273# Dynamically create serializer tests to ensure that all
    270274# registered serializers are automatically tested.
    271275class SerializerTests(unittest.TestCase):
  • tests/regressiontests/serializers_regress/models.py

     
    102102    """This is a model that can be used as
    103103    something for other models to point at"""
    104104   
    105     data = models.CharField(max_length=30)
     105    data = models.ASCIICharField(max_length=30)
    106106
    107107class UniqueAnchor(models.Model):
    108108    """This is a model that can be used as
    109109    something for other models to point at"""
    110110
    111     data = models.CharField(unique=True, max_length=30)
     111    data = models.ASCIICharField(unique=True, max_length=30)
    112112   
    113113class FKData(models.Model):
    114114    data = models.ForeignKey(Anchor, null=True)
     
    144144    data = models.BooleanField(primary_key=True)
    145145   
    146146class CharPKData(models.Model):
    147     data = models.CharField(max_length=30, primary_key=True)
     147    data = models.ASCIICharField(max_length=30, primary_key=True)
    148148
    149149# class DatePKData(models.Model):
    150150#    data = models.DateField(primary_key=True)
Back to Top