Index: django/db/models/fields/__init__.py
===================================================================
--- django/db/models/fields/__init__.py	(revision 6652)
+++ django/db/models/fields/__init__.py	(working copy)
@@ -208,16 +208,21 @@
         return value
 
     def get_db_prep_lookup(self, lookup_type, value):
+        from django.db import connection
         "Returns field's value prepared for database lookup."
         if lookup_type in ('exact', 'regex', 'iregex', 'gt', 'gte', 'lt', 'lte', 'month', 'day', 'search'):
             return [value]
         elif lookup_type in ('range', 'in'):
             return value
         elif lookup_type in ('contains', 'icontains'):
+            if connection.features.uses_custom_icontains and lookup_type == 'icontains':
+                return [value]    
             return ["%%%s%%" % prep_for_like_query(value)]
         elif lookup_type == 'iexact':
             return [prep_for_like_query(value)]
         elif lookup_type in ('startswith', 'istartswith'):
+            if connection.features.uses_custom_startswith:
+                return [value]
             return ["%s%%" % prep_for_like_query(value)]
         elif lookup_type in ('endswith', 'iendswith'):
             return ["%%%s" % prep_for_like_query(value)]
@@ -480,6 +485,9 @@
         defaults.update(kwargs)
         return super(CharField, self).formfield(**defaults)
 
+class ASCIICharField(CharField):
+    pass
+
 # TODO: Maybe move this into contrib, because it's specialized.
 class CommaSeparatedIntegerField(CharField):
     def get_manipulator_field_objs(self):
@@ -589,7 +597,9 @@
             # doesn't support microseconds.
             if settings.DATABASE_ENGINE == 'mysql' and hasattr(value, 'microsecond'):
                 value = value.replace(microsecond=0)
-            value = smart_unicode(value)
+            #Firebird supports native datetime
+            if settings.DATABASE_ENGINE != 'firebird':
+                value = smart_unicode(value)
         return Field.get_db_prep_save(self, value)
 
     def get_db_prep_lookup(self, lookup_type, value):
@@ -997,8 +1007,8 @@
             # doesn't support microseconds.
             if settings.DATABASE_ENGINE == 'mysql' and hasattr(value, 'microsecond'):
                 value = value.replace(microsecond=0)
-            if settings.DATABASE_ENGINE == 'oracle':
-                # cx_Oracle expects a datetime.datetime to persist into TIMESTAMP field.
+            if settings.DATABASE_ENGINE in ('oracle', 'firebird'):
+                # cx_Oracle and kinterbasdb expect a datetime.datetime to persist into TIMESTAMP field.
                 if isinstance(value, datetime.time):
                     value = datetime.datetime(1900, 1, 1, value.hour, value.minute,
                                               value.second, value.microsecond)
Index: django/db/backends/__init__.py
===================================================================
--- django/db/backends/__init__.py	(revision 6652)
+++ django/db/backends/__init__.py	(working copy)
@@ -45,10 +45,13 @@
     autoindexes_primary_keys = True
     inline_fk_references = True
     needs_datetime_string_cast = True
+    needs_default_null = False
     needs_upper_for_iops = False
     supports_constraints = True
     supports_tablespaces = False
     uses_case_insensitive_names = False
+    uses_custom_icontains = False
+    uses_custom_startswith = False
     uses_custom_queryset = False
 
 class BaseDatabaseOperations(object):
@@ -65,7 +68,14 @@
         This SQL is executed when a table is created.
         """
         return None
-
+    
+    def cascade_delete_update_sql(self):
+        """
+        Returns the SQL necessary to make a cascading deletes and updates
+        of foreign key references during a CREATE TABLE statement.
+        """
+        return ''
+    
     def date_extract_sql(self, lookup_type, field_name):
         """
         Given a lookup_type of 'year', 'month' or 'day', returns the SQL that
Index: django/db/backends/firebird/base.py
===================================================================
--- django/db/backends/firebird/base.py	(revision 0)
+++ django/db/backends/firebird/base.py	(revision 0)
@@ -0,0 +1,530 @@
+"""
+Firebird database backend for Django.
+
+Requires KInterbasDB 3.2: http://kinterbasdb.sourceforge.net/
+The egenix mx (mx.DateTime) is NOT required
+
+Database charset should be UNICODE_FSS or UTF8 (FireBird 2.0+)
+To use UTF8 encoding add FIREBIRD_CHARSET = 'UTF8' to your settings.py 
+UNICODE_FSS works with all versions and uses less memory
+"""
+
+from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, BaseDatabaseOperations, util
+
+try:
+    import kinterbasdb as Database
+except ImportError, e:
+    from django.core.exceptions import ImproperlyConfigured
+    raise ImproperlyConfigured, "Error loading KInterbasDB module: %s" % e
+
+DatabaseError = Database.DatabaseError
+IntegrityError = Database.IntegrityError
+
+class DatabaseFeatures(BaseDatabaseFeatures):
+    needs_datetime_string_cast = False
+    needs_default_null = True
+    needs_upper_for_iops = True
+    supports_constraints = False #some tests went strange without it
+    uses_custom_icontains = True #CONTAINING <value> op instead of LIKE %<value>%
+    uses_custom_startswith = True #STARTING WITH op. Faster than LIKE
+    uses_custom_queryset = True
+
+class DatabaseOperations(BaseDatabaseOperations):
+    _max_name_length = 31
+    def __init__(self):
+        self._firebird_version = None
+    
+    def get_generator_name(self, name):
+        return '%s_G' % util.truncate_name(name, self._max_name_length-2).upper()
+        
+    def get_trigger_name(self, name):
+        return '%s_T' % util.truncate_name(name, self._max_name_length-2).upper() 
+    
+    def _get_firebird_version(self):
+        if self._firebird_version is None:
+            from django.db import connection
+            self._firebird_version = [int(val) for val in connection.server_version.split()[-1].split('.')]
+        return self._firebird_version
+    firebird_version = property(_get_firebird_version)
+    
+    def _autoinc_sql_with_style(self, style, table_name, column_name):
+        """
+        To simulate auto-incrementing primary keys in Firebird, we have to
+        create a generator and a trigger.
+    
+        Create the generators and triggers names based only on table name
+        since django only support one auto field per model
+        """
+        
+        KWD = style.SQL_KEYWORD
+        TBL = style.SQL_TABLE
+        FLD = style.SQL_FIELD
+    
+        generator_name = self.get_generator_name(table_name)
+        trigger_name = self.get_trigger_name(table_name)
+        column_name = self.quote_name(column_name)
+        table_name = self.quote_name(table_name)
+        
+        generator_sql = "%s %s;" % ( KWD('CREATE GENERATOR'), 
+                                     TBL(generator_name))      
+        trigger_sql = "\n".join([
+            "%s %s %s %s" % ( \
+            KWD('CREATE TRIGGER'), TBL(trigger_name), KWD('FOR'),
+            TBL(table_name)),
+            "%s 0 %s" % (KWD('ACTIVE BEFORE INSERT POSITION'), KWD('AS')),
+            KWD('BEGIN'), 
+            "  %s ((%s.%s %s) %s (%s.%s = 0)) %s" % ( \
+                KWD('IF'),
+                KWD('NEW'), FLD(column_name), KWD('IS NULL'),
+                KWD('OR'), KWD('NEW'), FLD(column_name),
+                KWD('THEN')
+            ),
+            "  %s" % KWD('BEGIN'), 
+            "    %s.%s = %s(%s, 1);" % ( \
+                KWD('NEW'), FLD(column_name),
+                KWD('GEN_ID'), TBL(generator_name)
+            ),
+            "  %s" % KWD('END'),
+            KWD('END')
+            ])
+        return (generator_sql, trigger_sql)
+    
+    def autoinc_sql(self, table_name, column_name):
+        # style argument disappeared, so we'll just import django's dummy
+        from django.core.management.color import no_style, color_style
+        return self._autoinc_sql_with_style(no_style(), table_name, column_name)
+
+    def max_name_length(self):
+        return self._max_name_length
+
+    def query_set_class(self, DefaultQuerySet):
+        from django.db import connection
+        from django.db.models.query import EmptyResultSet, GET_ITERATOR_CHUNK_SIZE, quote_only_if_word
+
+        class FirebirdQuerySet(DefaultQuerySet):
+        #TODO: Optimize for Firebird and take full advanatage of its power
+        # Now it's just a copy of django.db.models.query._QuerySet
+        # with LIMIT/OFFSET removed and FIRST/SKIP added
+            def _get_sql_clause(self):
+                from django.db.models.query import SortedDict, handle_legacy_orderlist, orderfield2column, fill_table_cache
+                qn = connection.ops.quote_name
+                opts = self.model._meta
+
+                # Construct the fundamental parts of the query: SELECT X FROM Y WHERE Z.
+                select = ["%s.%s" % (qn(opts.db_table), qn(f.column)) for f in opts.fields]
+                tables = [quote_only_if_word(t) for t in self._tables]
+                joins = SortedDict()
+                where = self._where[:]
+                params = self._params[:]
+
+                # Convert self._filters into SQL.
+                joins2, where2, params2 = self._filters.get_sql(opts)
+                joins.update(joins2)
+                where.extend(where2)
+                params.extend(params2)
+
+                # Add additional tables and WHERE clauses based on select_related.
+                if self._select_related:
+                    fill_table_cache(opts, select, tables, where,
+                                     old_prefix=opts.db_table,
+                                     cache_tables_seen=[opts.db_table],
+                                     max_depth=self._max_related_depth)
+
+                # Add any additional SELECTs.
+                if self._select:
+                    select.extend(['(%s) AS %s' % (quote_only_if_word(s[1]), qn(s[0])) for s in self._select.items()])
+
+                # Start composing the body of the SQL statement.
+                sql = [" FROM", qn(opts.db_table)]
+
+                # Compose the join dictionary into SQL describing the joins.
+                if joins:
+                    sql.append(" ".join(["%s %s AS %s ON %s" % (join_type, table, alias, condition)
+                                    for (alias, (table, join_type, condition)) in joins.items()]))
+
+                # Compose the tables clause into SQL.
+                if tables:
+                    sql.append(", " + ", ".join(tables))
+
+                # Compose the where clause into SQL.
+                if where:
+                    sql.append(where and "WHERE " + " AND ".join(where))
+
+                # ORDER BY clause
+                order_by = []
+                if self._order_by is not None:
+                    ordering_to_use = self._order_by
+                else:
+                    ordering_to_use = opts.ordering
+                for f in handle_legacy_orderlist(ordering_to_use):
+                    if f == '?': # Special case.
+                        order_by.append(connection.ops.random_function_sql())
+                    else:
+                        if f.startswith('-'):
+                            col_name = f[1:]
+                            order = "DESC"
+                        else:
+                            col_name = f
+                            order = "ASC"
+                        if "." in col_name:
+                            table_prefix, col_name = col_name.split('.', 1)
+                            table_prefix = qn(table_prefix) + '.'
+                        else:
+                            # Use the database table as a column prefix if it wasn't given,
+                            # and if the requested column isn't a custom SELECT.
+                            if "." not in col_name and col_name not in (self._select or ()):
+                                table_prefix = qn(opts.db_table) + '.'
+                            else:
+                                table_prefix = ''
+                        order_by.append('%s%s %s' % (table_prefix, qn(orderfield2column(col_name, opts)), order))
+                if order_by:
+                    sql.append("ORDER BY " + ", ".join(order_by))
+
+                return select, " ".join(sql), params
+            
+            def iterator(self):
+                "Performs the SELECT database lookup of this QuerySet."
+                from django.db.models.query import get_cached_row
+                
+                try:
+                    select, sql, params = self._get_sql_clause()
+                except EmptyResultSet:
+                    raise StopIteration 
+                    
+                # self._select is a dictionary, and dictionaries' key order is
+                # undefined, so we convert it to a list of tuples.
+                extra_select = self._select.items()
+                
+                cursor = connection.cursor() 
+                limit_offset_before = "" 
+                if self._limit is not None: 
+                    limit_offset_before += "FIRST %s " % self._limit 
+                    if self._offset: 
+                        limit_offset_before += "SKIP %s " % self._offset
+                else:
+                    assert self._offset is None, "'offset' is not allowed without 'limit'"
+                cursor.execute("SELECT " + limit_offset_before + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params)
+                fill_cache = self._select_related
+                fields = self.model._meta.fields
+                index_end = len(fields)
+                has_resolve_columns = hasattr(self, 'resolve_columns')
+                while 1:
+                    rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE)
+                    if not rows:
+                        raise StopIteration
+                    for row in rows:
+                        if has_resolve_columns:
+                            row = self.resolve_columns(row, fields)
+                        if fill_cache:
+                            obj, index_end = get_cached_row(klass=self.model, row=row,
+                                                            index_start=0, max_depth=self._max_related_depth)
+                        else:
+                            obj = self.model(*row[:index_end])
+                        for i, k in enumerate(extra_select):
+                            setattr(obj, k[0], row[index_end+i])
+                        yield obj
+        return FirebirdQuerySet
+    
+
+    def quote_name(self, name):
+        #Trancate and quote once. 
+        #Generally works without upper() but some tests fail? without it
+        #So let it be like in oracle backend
+        if not name.startswith('"') and not name.endswith('"'):
+            name = '"%s"' % util.truncate_name(name, self._max_name_length)
+        return name.upper()
+
+    def last_insert_id(self, cursor, table_name, pk_name):
+        stmt = 'SELECT GEN_ID(%s, 0) from RDB$DATABASE'
+        cursor.execute(stmt % self.get_generator_name(table_name))
+        return cursor.fetchone()[0]
+
+    def date_extract_sql(self, lookup_type, column_name):
+        # lookup_type is 'year', 'month', 'day'
+        return "EXTRACT(%s FROM %s)" % (lookup_type, column_name)
+
+    def date_trunc_sql(self, lookup_type, column_name):
+        if lookup_type == 'year':
+             sql = "EXTRACT(year FROM %s)||'-01-01 00:00:00'" % column_name
+        elif lookup_type == 'month':
+            sql = "EXTRACT(year FROM %s)||'-'||EXTRACT(month FROM %s)||'-01 00:00:00'" % (column_name, column_name)
+        elif lookup_type == 'day':
+            sql = "EXTRACT(year FROM %s)||'-'||EXTRACT(month FROM %s)||'-'||EXTRACT(day FROM %s)||' 00:00:00'" % (column_name, column_name, column_name)
+        return "CAST(%s AS TIMESTAMP)" % sql
+    
+    def cascade_delete_update_sql(self):
+        # Solves FK problems with sql_flush
+        return " ON DELETE CASCADE ON UPDATE CASCADE"
+    
+    def datetime_cast_sql(self):
+        return None
+
+    def limit_offset_sql(self, limit, offset=None):
+        # limits are handled in custom FirebirdQuerySet 
+        assert False, 'Limits are handled in a different way in Firebird'
+        return ""
+
+    def random_function_sql(self):
+        return "rand()"
+
+    def pk_default_value(self):
+        return "NULL"
+    
+    def start_transaction_sql(self):
+        return ""
+
+    def sequence_reset_sql(self, style, model_list):
+        from django.db import models
+        output = []
+        for model in model_list:
+            for f in model._meta.fields:
+                if isinstance(f, models.AutoField):
+                    generator_name = self.get_generator_name(model._meta.db_table)
+                    output.append("SET GENERATOR %s TO 0;" % generator_name)
+                    break # Only one AutoField is allowed per model, so don't bother continuing.
+            for f in model._meta.many_to_many:
+                generator_name = self.get_generator_name(f.m2m_db_table())
+                output.append("SET GENERATOR %s TO 0;" % generator_name)
+        return output
+    
+    def sql_flush(self, style, tables, sequences):
+        if tables:
+            # FK constraints gave us a lot of trouble with default values
+            # that was a reason behind very ugly and dangerous code here
+            # Solved with "ON DELETE CASCADE" with all FK references        
+            sql = ['%s %s %s;' % \
+                    (style.SQL_KEYWORD('DELETE'),
+                     style.SQL_KEYWORD('FROM'),
+                     style.SQL_FIELD(self.quote_name(table))
+                     ) for table in tables]
+            for generator_info in sequences:
+                table_name = generator_info['table']
+                query = "SET GENERATOR %s TO 0;" % self.get_generator_name(table_name)
+                sql.append(query)
+            return sql
+        else:
+            return []
+
+#    def fulltext_search_sql(self, field_name):
+#        return field_name + ' CONTAINING %s'
+        
+    def drop_sequence_sql(self, table):
+        return "DROP GENERATOR %s;" % self.get_generator_name(table)
+        
+    def last_executed_query(self, cursor, sql, params):
+        """
+        Returns a string of the query last executed by the given cursor, with
+        placeholders replaced with actual values.
+
+        `sql` is the raw query containing placeholders, and `params` is the
+        sequence of parameters. These are used by default, but this method
+        exists for database backends to provide a better implementation
+        according to their own quoting schemes.
+        """
+        from django.utils.encoding import smart_unicode, force_unicode
+
+        # Convert params to contain Unicode values.
+        to_unicode = lambda s: force_unicode(s, strings_only=True)
+        if isinstance(params, (list, tuple)):
+            u_params = tuple([to_unicode(val) for val in params])
+        else:
+            u_params = dict([(to_unicode(k), to_unicode(v)) for k, v in params.items()])
+        try:
+            #Extracts sql right from KInterbasDB's prepared statement
+            return smart_unicode(cursor.query) % u_params
+        except TypeError:
+            return smart_unicode(sql) % u_params
+
+class FirebirdCursorWrapper(object):
+    """
+    Django uses "format" ('%s') style placeholders, but firebird uses "qmark" ('?') style.
+    This fixes it -- but note that if you want to use a literal "%s" in a query,
+    you'll need to use "%%s".
+    
+    We also do all automatic type conversions here.
+    """
+    import kinterbasdb.typeconv_datetime_stdlib as tc_dt
+    import kinterbasdb.typeconv_fixed_decimal as tc_fd
+    import kinterbasdb.typeconv_text_unicode as tc_tu
+    import django.utils.encoding as dj_ue
+   
+    def timestamp_conv_in(self, timestamp):
+        if isinstance(timestamp, basestring):
+            #Replaces 6 digits microseconds to 4 digits allowed in Firebird
+            timestamp = timestamp[:24]
+        return self.tc_dt.timestamp_conv_in(timestamp)
+    
+    def time_conv_in(self, value):
+        import datetime
+        if isinstance(value, datetime.datetime):
+            value = datetime.time(value.hour, value.minute, value.second, value.micosecond)       
+        return self.tc_dt.time_conv_in(value) 
+    
+    def ascii_conv_in(self, text):  
+        return self.dj_eu.smart_str(text, 'ascii')
+
+    def unicode_conv_in(self, text):
+        return self.tc_tu.unicode_conv_in((self.dj_ue.force_unicode(text[0]), self.FB_CHARSET_CODE))
+
+    def blob_conv_in(self, text): 
+        return self.tc_tu.unicode_conv_in((self.dj_ue.force_unicode(text), self.FB_CHARSET_CODE))
+
+    def blob_conv_out(self, text):
+        return self.tc_tu.unicode_conv_out((text, self.FB_CHARSET_CODE))
+        
+    def __init__(self, cursor):
+        from django.conf import settings
+        self.FB_CHARSET_CODE = 3 #UNICODE_FSS
+        if hasattr(settings, 'FIREBIRD_CHARSET'):
+            if settings.FIREBIRD_CHARSET == 'UTF8':
+                self.FB_CHARSET_CODE = 4 # UTF-8 with Firebird 2.0+    
+        self.cursor = cursor
+        
+        # Prepared Statement 
+        # http://kinterbasdb.sourceforge.net/dist_docs/usage.html#adv_prepared_statements
+        # Need to decide wether they are useful or not
+        # Maybe add prepare, execute_prep and executemany_pep methods here
+        # and rewrite QuerySet to take advantage of them?
+        # Could speed the things up
+        self._statement = None
+        self.cursor.set_type_trans_in({
+            'DATE':             self.tc_dt.date_conv_in,
+            'TIME':             self.time_conv_in,
+            'TIMESTAMP':        self.timestamp_conv_in,
+            'FIXED':            self.tc_fd.fixed_conv_in_imprecise,
+            'TEXT':             self.ascii_conv_in,
+            'TEXT_UNICODE':     self.unicode_conv_in,
+            'BLOB':             self.blob_conv_in
+        })
+        self.cursor.set_type_trans_out({
+            'DATE':             self.tc_dt.date_conv_out,
+            'TIME':             self.tc_dt.time_conv_out,
+            'TIMESTAMP':        self.tc_dt.timestamp_conv_out,
+            'FIXED':            self.tc_fd.fixed_conv_out_imprecise,
+            'TEXT':             self.dj_ue.force_unicode,
+            'TEXT_UNICODE':     self.tc_tu.unicode_conv_out,
+            'BLOB':             self.blob_conv_out
+        })
+    
+    def _get_query(self):
+        if self._statement:
+            return self._statement.sql
+    def _get_statement(self):
+        if self._statement:
+            return self._statement
+    query = property(_get_query)
+    statement = property(_get_statement)
+        
+    def execute(self, query, params=()):
+        query = self.convert_query(query, len(params))
+        if self._get_query() != query:
+            try:
+                self._statement = self.cursor.prep(query)
+            except Database.ProgrammingError, e:
+                print query % params
+                raise DatabaseError, e
+        return self.cursor.execute(self._statement, params)
+
+    def executemany(self, query, param_list):
+        try:
+            query = self.convert_query(query, len(param_list[0]))
+        except IndexError:
+            param_list = []
+        if self._get_query() != query:
+            self._statement = self.cursor.prep(query)
+        return self.cursor.executemany(self._statement, param_list)
+
+    def convert_query(self, query, num_params):
+        return query % tuple("?" * num_params)
+    
+    def __getattr__(self, attr):
+        if attr in self.__dict__:
+            return self.__dict__[attr]
+        else:
+            return getattr(self.cursor, attr)
+
+class DatabaseWrapper(BaseDatabaseWrapper):
+    features = DatabaseFeatures()
+    ops = DatabaseOperations()
+    operators = {
+        'exact': '= %s',
+        'iexact': '= UPPER(%s)',
+        'contains': "LIKE %s ESCAPE'\\'",
+        'icontains': 'CONTAINING %s', #case is ignored
+        'gt': '> %s',
+        'gte': '>= %s',
+        'lt': '< %s',
+        'lte': '<= %s',
+        'startswith': 'STARTING WITH %s', #looks to be faster then LIKE
+        'endswith': "LIKE %s ESCAPE'\\'",
+        'istartswith': 'STARTING WITH UPPER(%s)',
+        'iendswith': "LIKE UPPER(%s) ESCAPE'\\'",
+    }
+    _current_cursor = None
+    def _connect(self, settings):
+        if settings.DATABASE_NAME == '':
+            from django.core.exceptions import ImproperlyConfigured
+            raise ImproperlyConfigured, "You need to specify DATABASE_NAME in your Django settings file."
+        charset = 'UNICODE_FSS'
+        if hasattr(settings, 'FIREBIRD_CHARSET'):
+            if settings.FIREBIRD_CHARSET == 'UTF8':
+                charset = 'UTF8'    
+        kwargs = {'charset' : charset }
+        if settings.DATABASE_HOST:
+            kwargs['dsn'] = "%s:%s" % (settings.DATABASE_HOST, settings.DATABASE_NAME)
+        else:
+            kwargs['dsn'] = "localhost:%s" % settings.DATABASE_NAME
+        if settings.DATABASE_USER:
+            kwargs['user'] = settings.DATABASE_USER
+        if settings.DATABASE_PASSWORD:
+            kwargs['password'] = settings.DATABASE_PASSWORD
+        self.connection = Database.connect(**kwargs)
+        assert self.charset == charset
+        try:
+            self.connection.execute_immediate("""
+                DECLARE EXTERNAL FUNCTION rand
+                RETURNS DOUBLE PRECISION
+                BY VALUE ENTRY_POINT 'IB_UDF_rand' MODULE_NAME 'ib_udf';
+            """)
+        except Database.ProgrammingError:
+            pass #Already defined 
+        
+    def cursor(self, name=None):
+        #Cursors can be named
+        #http://kinterbasdb.sourceforge.net/dist_docs/usage.html#adv_named_cursors
+        #and maybe useful for scrolling updates and deletes
+        from django.conf import settings
+        cursor = self._cursor(settings, name)
+        if settings.DEBUG:
+            return self.make_debug_cursor(cursor)
+        return cursor
+    
+    def _cursor(self, settings, name):
+        if self.connection is None:
+            self._connect(settings)
+        cursor = self.connection.cursor()
+        if name:
+            cursor.name = name
+        cursor = FirebirdCursorWrapper(cursor)
+        self._current_cursor = cursor
+        return cursor
+    
+    #Returns query from prepared statement
+    def _get_query(self):
+        if self._current_cursor:
+            return self._current_cursor.query
+    query = property(_get_query)
+    #Returns prepared statement itself
+    def _get_statement(self):
+        if self._current_cursor:
+            return self._current_cursor.statement
+    statement = property(_get_statement)
+        
+    
+    def __getattr__(self, attr):
+        if attr in self.__dict__:
+            return self.__dict__[attr]
+        else:
+            return getattr(self.connection, attr)
+    
+
Index: django/db/backends/firebird/client.py
===================================================================
--- django/db/backends/firebird/client.py	(revision 0)
+++ django/db/backends/firebird/client.py	(revision 0)
@@ -0,0 +1,11 @@
+from django.conf import settings
+import os
+
+def runshell():
+    args = [settings.DATABASE_NAME]
+    args += ["-u %s" % settings.DATABASE_USER]
+    if settings.DATABASE_PASSWORD:
+        args += ["-p %s" % settings.DATABASE_PASSWORD]
+    if 'FIREBIRD' not in os.environ:
+        path = '/opt/firebird/bin/'
+    os.system(path + 'isql ' + ' '.join(args))
Index: django/db/backends/firebird/__init__.py
===================================================================
Index: django/db/backends/firebird/introspection.py
===================================================================
--- django/db/backends/firebird/introspection.py	(revision 0)
+++ django/db/backends/firebird/introspection.py	(revision 0)
@@ -0,0 +1,93 @@
+from django.db import transaction
+from django.db.backends.firebird.base import DatabaseOperations
+
+quote_name = DatabaseOperations().quote_name
+
+def get_table_list(cursor):
+    "Returns a list of table names in the current database."
+    cursor.execute("""
+        SELECT rdb$relation_name FROM rdb$relations
+        WHERE rdb$system_flag = 0 AND rdb$view_blr IS NULL ORDER BY rdb$relation_name""")
+    return [str(row[0].strip().lower()) for row in cursor.fetchall()]
+
+def get_table_description(cursor, table_name):
+    "Returns a description of the table, with the DB-API cursor.description interface."
+    #cursor.execute("SELECT FIRST 1 * FROM %s" % quote_name(table_name))
+    #return cursor.description
+    # (name, type_code, display_size, internal_size, precision, scale, null_ok)
+    cursor.execute("""
+        SELECT DISTINCT R.RDB$FIELD_NAME AS FNAME,
+                  F.RDB$FIELD_TYPE AS FTYPE,
+                  F.RDB$FIELD_LENGTH AS FLENGTH,
+                  F.RDB$FIELD_PRECISION AS FPRECISION,
+                  F.RDB$FIELD_SCALE AS FSCALE,
+                  R.RDB$NULL_FLAG AS NULL_FLAG,
+                  R.RDB$FIELD_POSITION
+        FROM RDB$RELATION_FIELDS R
+             JOIN RDB$FIELDS F ON R.RDB$FIELD_SOURCE=F.RDB$FIELD_NAME
+        WHERE F.RDB$SYSTEM_FLAG=0 and R.RDB$RELATION_NAME= %s
+        ORDER BY R.RDB$FIELD_POSITION
+    """, (table_name,))
+    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()]
+
+
+def get_relations(cursor, table_name):
+    """
+    Returns a dictionary of {field_index: (field_index_other_table, other_table)}
+    representing all relationships to the given table. Indexes are 0-based.
+    """
+    cursor.execute("""
+        SELECT seg.rdb$field_name, seg_ref.rdb$field_name, idx_ref.rdb$relation_name
+        FROM rdb$indices idx
+        INNER JOIN rdb$index_segments seg
+            ON seg.rdb$index_name = idx.rdb$index_name
+        INNER JOIN rdb$indices idx_ref
+            ON idx_ref.rdb$index_name = idx.rdb$foreign_key
+        INNER JOIN rdb$index_segments seg_ref
+            ON seg_ref.rdb$index_name = idx_ref.rdb$index_name
+        WHERE idx.rdb$relation_name = %s
+            AND idx.rdb$foreign_key IS NOT NULL""", [table_name])
+
+    relations = {}
+    for row in cursor.fetchall():
+        relations[row[0].rstrip()] = (row[1].strip(), row[2].strip())
+    return relations
+
+def get_indexes(cursor, table_name):
+    """
+    Returns a dictionary of fieldname -> infodict for the given table,
+    where each infodict is in the format:
+        {'primary_key': boolean representing whether it's the primary key,
+         'unique': boolean representing whether it's a unique index}
+    """
+
+    # This query retrieves each field name and index type on the given table.
+    cursor.execute("""
+        SELECT seg.rdb$field_name, const.rdb$constraint_type
+        FROM rdb$relation_constraints const
+        LEFT JOIN rdb$index_segments seg
+            ON seg.rdb$index_name = const.rdb$index_name
+        WHERE const.rdb$relation_name = %s
+            AND (const.rdb$constraint_type = 'PRIMARY KEY'
+                OR const.rdb$constraint_type = 'UNIQUE')""", [table_name])
+    indexes = {}
+    for row in cursor.fetchall():
+        indexes[row[0].strip()] = {
+            'primary_key': ('PRIMARY KEY' == row[1].strip()),
+            'unique': ('UNIQUE' == row[1].strip())}
+    return indexes
+
+# Maps type codes to Django Field types.
+# !todo
+DATA_TYPES_REVERSE = {
+    7: 'BooleanField',
+    7: 'SmallIntegerField',
+    8: 'IntegerField',
+    261: 'TextField',
+    37: 'IPAddressField',
+    37: 'CharField',
+    12: 'DateField',
+    13: 'TimeField',
+    35: 'DateTimeField',
+    10: 'FloatField',
+}
Index: django/db/backends/firebird/creation.py
===================================================================
--- django/db/backends/firebird/creation.py	(revision 0)
+++ django/db/backends/firebird/creation.py	(revision 0)
@@ -0,0 +1,103 @@
+# This dictionary maps Field objects to their associated Firebird column
+# types, as strings. Column-type strings can contain format strings; they'll
+# be interpolated against the values of Field.__dict__ before being output.
+# If a column type is set to None, it won't be included in the output.
+
+import sys, os.path
+from kinterbasdb import connect, create_database
+from django.core.management import call_command
+
+#TODO: Implement CHECKs
+DATA_TYPES = {
+    'ASCIICharField':                'varchar(%(max_length)s) CHARACTER SET ASCII',
+    'AutoField':                     'integer',
+    'BooleanField':                  'smallint', # CHECK ("%(column)s" IN (0,1))',
+    'CharField':                     'varchar(%(max_length)s)',
+    'CommaSeparatedIntegerField':    'varchar(%(max_length)s) CHARACTER SET ASCII',
+    'DateField':                     'date',
+    'DateTimeField':                 'timestamp',
+    'DecimalField':                  'numeric(%(max_digits)s, %(decimal_places)s)',
+    'FileField':                     'varchar(%(max_length)s)',
+    'FilePathField':                 'varchar(%(max_length)s)',
+    'FloatField':                    'double precision',
+    'ImageField':                    'varchar(%(max_length)s) CHARACTER SET ASCII',
+    'IntegerField':                  'integer',
+    'IPAddressField':                'varchar(15) CHARACTER SET ASCII',
+    'NullBooleanField':              'smallint', #CHECK (("%(column)"s IN (0,1)) OR ("%(column)s" IS NULL))',
+    'OneToOneField':                 'integer',
+    'PhoneNumberField':              'varchar(20)', # CHARACTER SET ASCII',
+    'PositiveIntegerField':          'integer', # CHECK ("%(column)s" >= 0)',
+    'PositiveSmallIntegerField':     'smallint',
+    'SlugField':                     'varchar(%(max_length)s)',
+    'SmallIntegerField':             'smallint',
+    'TextField':                     'blob sub_type text',
+    'TimeField':                     'time',
+    'URLField':                      'varchar(%(max_length)s) CHARACTER SET ASCII',
+    'USStateField':                  'varchar(2) CHARACTER SET ASCII',
+}
+
+TEST_DATABASE_PREFIX = 'test_'
+
+def create_test_db(settings, connection, verbosity, autoclobber):
+    # KInterbasDB supports dynamic database creation and deletion 
+    # via the module-level function create_database and the method Connection.drop_database.
+       
+    if settings.TEST_DATABASE_NAME:
+        TEST_DATABASE_NAME = settings.TEST_DATABASE_NAME
+    else:
+        dbnametuple = os.path.split(settings.DATABASE_NAME)
+        TEST_DATABASE_NAME = os.path.join(dbnametuple[0], TEST_DATABASE_PREFIX + dbnametuple[1])
+    
+    dsn = "localhost:%s" % TEST_DATABASE_NAME
+    if settings.DATABASE_HOST:
+        dsn = "%s:%s" % (settings.DATABASE_HOST, TEST_DATABASE_NAME)
+    
+    if os.path.isfile(TEST_DATABASE_NAME):
+        sys.stderr.write("Database %s already exists\n" % TEST_DATABASE_NAME)
+        if not autoclobber:
+            confirm = raw_input("Type 'yes' if you would like to try deleting the test database '%s', or 'no' to cancel: " % TEST_DATABASE_NAME)
+        if autoclobber or confirm == 'yes':
+            if verbosity >= 1:
+                print "Destroying old test database..."
+            old_connection = connect(dsn=dsn, user=settings.DATABASE_USER, password=settings.DATABASE_PASSWORD)
+            old_connection.drop_database()
+        else:
+                print "Tests cancelled."
+                sys.exit(1)
+            
+    if verbosity >= 1:
+        print "Creating test database..."
+    try:
+        charset = 'UNICODE_FSS'
+        if hasattr(settings, 'FIREBIRD_CHARSET'):
+            if settings.FIREBIRD_CHARSET == 'UTF8':
+                charset='UTF8'                
+        create_database("create database '%s' user '%s' password '%s' default character set %s" % \
+            (dsn, settings.DATABASE_USER, settings.DATABASE_PASSWORD, charset))
+    except Exception, e:
+        sys.stderr.write("Got an error creating the test database: %s\n" % e)
+        sys.exit(2)
+            
+
+    connection.close() 
+    settings.DATABASE_NAME = TEST_DATABASE_NAME
+
+    call_command('syncdb', verbosity=verbosity, interactive=False)
+
+    if settings.CACHE_BACKEND.startswith('db://'):
+        cache_name = settings.CACHE_BACKEND[len('db://'):]
+        call_command('createcachetable', cache_name)
+
+    # Get a cursor (even though we don't need one yet). This has
+    # the side effect of initializing the test database.
+    cursor = connection.cursor()
+
+    return TEST_DATABASE_NAME
+
+def destroy_test_db(settings, connection, old_database_name, verbosity):
+    # KInterbasDB supports dynamic database deletion via the method Connection.drop_database.
+    if verbosity >= 1:
+        print "Destroying test database..."
+    connection.drop_database()
+    
+
Index: django/core/management/sql.py
===================================================================
--- django/core/management/sql.py	(revision 6652)
+++ django/core/management/sql.py	(working copy)
@@ -263,7 +263,10 @@
         # Make the definition (e.g. 'foo VARCHAR(30)') for this field.
         field_output = [style.SQL_FIELD(qn(f.column)),
             style.SQL_COLTYPE(col_type)]
-        field_output.append(style.SQL_KEYWORD('%sNULL' % (not f.null and 'NOT ' or '')))
+        nullstring = ""
+        if connection.features.needs_default_null:
+            nullstring = "DEFAULT "
+        field_output.append(style.SQL_KEYWORD('%sNULL' % (not f.null and 'NOT ' or nullstring)))
         if f.unique and (not f.primary_key or connection.features.allows_unique_and_pk):
             field_output.append(style.SQL_KEYWORD('UNIQUE'))
         if f.primary_key:
@@ -276,8 +279,8 @@
             if inline_references and f.rel.to in known_models:
                 field_output.append(style.SQL_KEYWORD('REFERENCES') + ' ' + \
                     style.SQL_TABLE(qn(f.rel.to._meta.db_table)) + ' (' + \
-                    style.SQL_FIELD(qn(f.rel.to._meta.get_field(f.rel.field_name).column)) + ')' +
-                    connection.ops.deferrable_sql()
+                    style.SQL_FIELD(qn(f.rel.to._meta.get_field(f.rel.field_name).column)) + ')' + \
+                    connection.ops.cascade_delete_update_sql() + connection.ops.deferrable_sql()
                 )
             else:
                 # We haven't yet created the table to which this field
@@ -335,6 +338,7 @@
                 final_output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' % \
                     (qn(r_table), truncate_name(r_name, connection.ops.max_name_length()),
                     qn(r_col), qn(table), qn(col),
+                    connection.ops.cascade_delete_update_sql(),
                     connection.ops.deferrable_sql()))
             del pending_references[model]
     return final_output
@@ -364,19 +368,21 @@
                 tablespace_sql))
             if inline_references:
                 deferred = []
-                table_output.append('    %s %s %s %s (%s)%s,' %
+                table_output.append('    %s %s %s %s (%s)%s%s,' %
                     (style.SQL_FIELD(qn(f.m2m_column_name())),
                     style.SQL_COLTYPE(models.ForeignKey(model).db_type()),
                     style.SQL_KEYWORD('NOT NULL REFERENCES'),
                     style.SQL_TABLE(qn(opts.db_table)),
                     style.SQL_FIELD(qn(opts.pk.column)),
+                    connection.ops.cascade_delete_update_sql(),
                     connection.ops.deferrable_sql()))
-                table_output.append('    %s %s %s %s (%s)%s,' %
+                table_output.append('    %s %s %s %s (%s)%s%s,' %
                     (style.SQL_FIELD(qn(f.m2m_reverse_name())),
                     style.SQL_COLTYPE(models.ForeignKey(f.rel.to).db_type()),
                     style.SQL_KEYWORD('NOT NULL REFERENCES'),
                     style.SQL_TABLE(qn(f.rel.to._meta.db_table)),
                     style.SQL_FIELD(qn(f.rel.to._meta.pk.column)),
+                    connection.ops.cascade_delete_update_sql(),
                     connection.ops.deferrable_sql()))
             else:
                 table_output.append('    %s %s %s,' %
@@ -412,6 +418,7 @@
                 (qn(r_table),
                 truncate_name(r_name, connection.ops.max_name_length()),
                 qn(r_col), qn(table), qn(col),
+                connection.ops.cascade_delete_update_sql(),
                 connection.ops.deferrable_sql()))
 
             # Add any extra SQL needed to support auto-incrementing PKs
Index: django/contrib/contenttypes/models.py
===================================================================
--- django/contrib/contenttypes/models.py	(revision 6652)
+++ django/contrib/contenttypes/models.py	(working copy)
@@ -1,6 +1,7 @@
 from django.db import models
 from django.utils.translation import ugettext_lazy as _
 from django.utils.encoding import smart_unicode
+from django.conf import settings
 
 CONTENT_TYPE_CACHE = {}
 class ContentTypeManager(models.Manager):
@@ -31,10 +32,15 @@
         global CONTENT_TYPE_CACHE
         CONTENT_TYPE_CACHE = {}
 
-class ContentType(models.Model):
+class ContentType(models.Model):   
     name = models.CharField(max_length=100)
-    app_label = models.CharField(max_length=100)
-    model = models.CharField(_('python model class name'), max_length=100)
+    # Need this because of Firebird restrictions on index key size < 252 bytes
+    if settings.DATABASE_ENGINE == 'firebird':
+        app_label = models.ASCIICharField(max_length=96)
+        model = models.ASCIICharField(_('python model class name'), max_length=96)
+    else:
+        app_label = models.CharField(max_length=100)
+        model = models.CharField(_('python model class name'), max_length=100)
     objects = ContentTypeManager()
     class Meta:
         verbose_name = _('content type')
Index: django/contrib/auth/models.py
===================================================================
--- django/contrib/auth/models.py	(revision 6652)
+++ django/contrib/auth/models.py	(working copy)
@@ -6,6 +6,7 @@
 from django.contrib.contenttypes.models import ContentType
 from django.utils.encoding import smart_str
 from django.utils.translation import ugettext_lazy as _
+from django.conf import settings
 import datetime
 import urllib
 
@@ -72,7 +73,11 @@
     """
     name = models.CharField(_('name'), max_length=50)
     content_type = models.ForeignKey(ContentType)
-    codename = models.CharField(_('codename'), max_length=100)
+    # Need this because of Firebird restrictions on index key size < 252 bytes
+    if settings.DATABASE_ENGINE == 'firebird':
+        codename = models.ASCIICharField(_('codename'), max_length=100)
+    else:
+        codename = models.CharField(_('codename'), max_length=100)
 
     class Meta:
         verbose_name = _('permission')
