Ticket #1261: firebird-svn6652-full.diff

File firebird-svn6652-full.diff, 42.5 KB (added by i_i, 7 years ago)

Firebird database backend working with svn django full patch

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