Index: django/db/models/query.py
===================================================================
--- django/db/models/query.py	(revision 6369)
+++ django/db/models/query.py	(working copy)
@@ -184,6 +184,16 @@
         # 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()
+        pre_columns = ""
+        # this is perhaps solvable by custom query set
+        if settings.DATABASE_ENGINE == 'firebird' and self._limit is not None:
+            pre_columns += "FIRST %s " % self._limit
+            if self._offset:
+                pre_columns += "SKIP %s " % self._offset
+        if self._distinct:
+            pre_columns += "DISTINCT "
+        cursor = connection.cursor()
+        cursor.execute("SELECT " + pre_columns + ",".join(select) + sql, params)
 
         cursor = connection.cursor()
         cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params)
@@ -515,7 +525,7 @@
 
         # 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)
+            sql.append(" ".join(["%s %s %s ON %s" % (join_type, table, alias, condition)
                             for (alias, (table, join_type, condition)) in joins.items()]))
 
         # Compose the tables clause into SQL.
Index: django/db/backends/oracle/base.py
===================================================================
--- django/db/backends/oracle/base.py	(revision 6369)
+++ django/db/backends/oracle/base.py	(working copy)
@@ -83,10 +83,10 @@
         cursor.execute('SELECT %s_sq.currval FROM dual' % sq_name)
         return cursor.fetchone()[0]
 
-    def limit_offset_sql(self, limit, offset=None):
-        # Limits and offset are too complicated to be handled here.
-        # Instead, they are handled in django/db/backends/oracle/query.py.
-        return ""
+#    def limit_offset_sql(self, limit, offset=None):
+#        # Limits and offset are too complicated to be handled here.
+#        # Instead, they are handled in django/db/backends/oracle/query.py.
+#        return ""
 
     def max_name_length(self):
         return 30
Index: django/db/backends/oracle/creation.py
===================================================================
--- django/db/backends/oracle/creation.py	(revision 6369)
+++ django/db/backends/oracle/creation.py	(working copy)
@@ -36,7 +36,7 @@
 PASSWORD = 'Im_a_lumberjack'
 REMEMBER = {}
 
-def create_test_db(settings, connection, verbosity=1, autoclobber=False):
+def create_test_db(settings, connection, backend, verbosity=1, autoclobber=False):
     TEST_DATABASE_NAME = _test_database_name(settings)
     TEST_DATABASE_USER = _test_database_user(settings)
     TEST_DATABASE_PASSWD = _test_database_passwd(settings)
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,344 @@
+"""
+Firebird database backend for Django.
+
+Requires kinterbasdb: http://kinterbasdb.sourceforge.net/
+"""
+
+from django.conf import settings
+from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, BaseDatabaseOperations, util
+
+import re
+try:
+    import kinterbasdb as Database
+    import kinterbasdb.typeconv_datetime_stdlib as typeconv_datetime
+except ImportError, e:
+    from django.core.exceptions import ImproperlyConfigured
+    raise ImproperlyConfigured, "Error loading kinterbasdb module: %s" % e
+
+DatabaseError = Database.DatabaseError
+IntegrityError = Database.IntegrityError
+Database.init(type_conv=200)
+Database.typeconv_text_unicode.DB_TO_PYTHON_ENCODING_MAP[127] = 'utf_8'
+
+try:
+    # Only exists in Python 2.4+
+    from threading import local
+except ImportError:
+    # Import copy of _thread_local.py from Python 2.4
+    from django.utils._threading_local import local
+
+server_version = None
+
+def unicode_conv_in(unicodeString):
+    if isinstance(unicodeString, unicode):
+        return unicodeString.encode(settings.DEFAULT_CHARSET)
+    return unicodeString
+
+#def unicode_conv_out(text):
+#    if isinstance(text, str):
+#        return text.decode(settings.DEFAULT_CHARSET)
+#    return text
+
+def timestamp_conv_in(datetime):
+    #Type converter for datetime casted strings.
+    #Replaces 6 digits microseconds to 4 digits allowed in Firebird
+    if isinstance(datetime, basestring) and datetime.find('.') > 0 and len(datetime) == 26:
+        datetime = datetime[0:-2]
+    return typeconv_datetime.timestamp_conv_in(datetime)
+
+class DatabaseFeatures(BaseDatabaseFeatures):
+    needs_upper_for_iops = True
+    autoindexes_primary_keys = False
+    
+class DatabaseOperations(BaseDatabaseOperations):
+
+    def autoinc_sql(self, table_name, column_name):
+        """To simulate auto-incrementing primary keys in Firebird, we have to
+        create a generator (sequence in Firebird 2) and a trigger.
+    
+        Create the sequences and triggers names based only on table name
+        since django only support one auto field per model"""
+        
+        # style argument disappeared, so we'll just import django's dummy
+        from django.core.management.color import no_style
+        style = no_style()
+        
+        KEYWORD = style.SQL_KEYWORD
+        TABLE = style.SQL_TABLE
+        FIELD = style.SQL_FIELD
+    
+        sequence_name = _quote_sequence_name(table_name)
+        column_name = self.quote_name(column_name)
+        
+        sequence_sql = "%s %s;" % ( \
+                KEYWORD(server_version < '2' and 'CREATE GENERATOR' or 'CREATE SEQUENCE'),
+                TABLE(sequence_name))
+        trigger_sql = "\n".join(["%s %s %s %s" % ( \
+                KEYWORD('CREATE TRIGGER'),
+                TABLE(_quote_trigger_name(table_name)),
+                KEYWORD('FOR'),
+                TABLE(self.quote_name(table_name))),
+            "%s 0 %s" % ( \
+                KEYWORD('ACTIVE BEFORE INSERT POSITION'),
+                KEYWORD('AS')),
+            KEYWORD('BEGIN'),
+            "  %s ((%s.%s %s) %s (%s.%s = 0)) %s" % ( \
+                KEYWORD('IF'),
+                KEYWORD('NEW'),
+                FIELD(column_name),
+                KEYWORD('IS NULL'),
+                KEYWORD('OR'),
+                KEYWORD('NEW'),
+                FIELD(column_name),
+                KEYWORD('THEN')),
+            "  %s" % KEYWORD('BEGIN'),
+            "    %s.%s = %s(%s, 1);" % ( \
+                KEYWORD('NEW'),
+                FIELD(column_name),
+                KEYWORD('GEN_ID'),
+                TABLE(sequence_name)),
+            "  %s" % KEYWORD('END'),
+            KEYWORD('END')])
+        
+        return (sequence_sql, trigger_sql)
+
+    def max_name_length(self):
+        return 30
+
+    def quote_name(self, name):
+        # the standard for firebird is not to quote names but in django
+        # it will quote all names uppercased so we can write sql without quotes
+        # because all names without quotes will defualt to uppercased,
+        # like oracle truncate names bigger than 30 chars
+        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 = server_version < '2' and 'SELECT GEN_ID(%s, 0) FROM RDB$DATABASE' or 'NEXT VALUE FOR %s'
+        stmt = 'SELECT GEN_ID(%s, 0) FROM RDB$DATABASE'
+        cursor.execute(stmt % _quote_sequence_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.upper(), 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 datetime_cast_sql(self):
+        return None
+
+    def limit_offset_sql(self, limit, offset=None):
+        # limits are handled in models/query.py
+        return ""
+
+    def random_function_sql(self):
+        return "RAND()"
+
+    def deferrable_sql(self):
+        # Not supported, maybe in Firebird 3.0
+        return ""
+
+    def fulltext_search_sql(field_name):
+        # maybe in Firebird 3.0
+        raise NotImplementedError
+
+    def drop_foreignkey_sql(self):
+        return "DROP CONSTRAINT"
+
+    def pk_default_value(self):
+        return "NULL"
+    
+#    def start_transaction_sql(self):
+#        raise NotImplementedError
+#        #return "BEGIN TRANSACTION;"
+
+    def sql_flush(self, style, tables, sequences):
+        """Return a list of SQL statements required to remove all data from
+        all tables in the database (without actually removing the tables
+        themselves) and put the database in an empty 'initial' state
+    
+        For Firebird we gonna take a dangerous workaround:
+            - first (re)create a temporary table 'django_flush$constraints' that
+            will contain all Foreignkey definitions for this database.
+            - next import this definitions into the new table and delete them
+            from system table 'rdb$relation_constraints'.
+            - after the flush is done import the definitions back to the system table
+            and delete the temporary table.
+            - on some systems, there is a problem with dropping django_flush$constraints
+            because of object locking. Thus, we're leaving it in database (as it's used
+            only for tests)
+        """
+        KEYWORD = style.SQL_KEYWORD
+        TABLE = style.SQL_TABLE
+        FIELD = style.SQL_FIELD
+    
+        if tables:
+            temp_table_name = self.quote_name('django_flush$constraints')
+            orig_table_name = 'RDB$RELATION_CONSTRAINTS'
+            sql = ["\n".join([
+                "%s %s (" % (KEYWORD('RECREATE TABLE'), TABLE(temp_table_name)),
+                "  %s %s(31) %s," % (FIELD(self.quote_name('rdb$constraint_name')), KEYWORD('CHAR'), KEYWORD('CHARACTER SET UNICODE_FSS')),
+                "  %s %s(11)," % (FIELD(self.quote_name('rdb$constraint_type')), KEYWORD('CHAR')),
+                "  %s %s(31) %s," % (FIELD(self.quote_name('rdb$relation_name')), KEYWORD('CHAR'), KEYWORD('CHARACTER SET UNICODE_FSS')),
+                "  %s %s(3)," % (FIELD(self.quote_name('rdb$deferrable')), KEYWORD('CHAR')),
+                "  %s %s(3)," % (FIELD(self.quote_name('rdb$initially_deferred')), KEYWORD('CHAR')),
+                "  %s %s(31) %s" % (FIELD(self.quote_name('rdb$index_name')), KEYWORD('CHAR'), KEYWORD('CHARACTER SET UNICODE_FSS')),
+                ");"]),
+                "%s;" % KEYWORD('COMMIT'),
+                "%s %s %s %s %s %s %s %s = 'FOREIGN KEY';" % ( \
+                    KEYWORD('INSERT INTO'), TABLE(temp_table_name),
+                    KEYWORD('SELECT'), FIELD('*'), KEYWORD('FROM'), TABLE(orig_table_name),
+                    KEYWORD('WHERE'), FIELD('rdb$constraint_type')),
+                "%s %s %s %s = 'FOREIGN KEY';" % ( \
+                    KEYWORD('DELETE FROM'), TABLE(orig_table_name),
+                    KEYWORD('WHERE'), FIELD('rdb$constraint_type')),
+                "%s;" % KEYWORD('COMMIT')]
+    
+            sql += ['%s %s;' % \
+                    (KEYWORD('DELETE FROM'),
+                     TABLE(self.quote_name(table))
+                     ) for table in tables]
+    
+            sql += ["%s %s %s 0;" % \
+                    (KEYWORD(server_version < '2' and 'SET GENERATOR' or 'ALTER SEQUENCE'),
+                     TABLE(_quote_sequence_name(sequence['table'])),
+                     KEYWORD(server_version < '2' and 'TO' or 'RESTART WITH')
+                     ) for sequence in sequences]
+            return sql + [
+                "%s %s %s %s %s %s;" % ( \
+                    KEYWORD('INSERT INTO'), TABLE(orig_table_name),
+                    KEYWORD('SELECT'), FIELD('*'), KEYWORD('FROM'), TABLE(temp_table_name)),
+#                "%s %s;" % (KEYWORD('DROP TABLE'), TABLE(temp_table_name)),
+                "%s;" % KEYWORD('COMMIT')]
+        return []
+   
+
+class DatabaseWrapper(local):
+    features = DatabaseFeatures()
+    ops = DatabaseOperations()
+    operators = {
+        'exact': '= %s',
+        'iexact': "= UPPER(%s)",
+        'contains': "LIKE %s ESCAPE '\\'",
+        'icontains': "LIKE UPPER(%s) ESCAPE '\\'",
+        'gt': '> %s',
+        'gte': '>= %s',
+        'lt': '< %s',
+        'lte': '<= %s',
+        'startswith': "LIKE %s ESCAPE '\\'",
+        'endswith': "LIKE %s ESCAPE '\\'",
+        'istartswith': "LIKE UPPER(%s) ESCAPE '\\'",
+        'iendswith': "LIKE UPPER(%s) ESCAPE '\\'",
+    
+        'text_icontains': "CONTAINING %s",
+    }
+
+    def __init__(self, **kwargs):
+        self.connection = None
+        self.queries = []
+        self.options = kwargs
+    
+    def drop_database(self):
+        if self.connection is None:
+            self.connect()
+        self.connection.drop_database()
+        self.connection = None
+    
+    def connect(self):
+        """ Connect to database and store newly created connection in self.connection """
+        if settings.DATABASE_NAME == '':
+            from django.core.exceptions import ImproperlyConfigured
+            raise ImproperlyConfigured, "You need to specify DATABASE_NAME in your Django settings file."
+        kwargs = {'database': settings.DATABASE_NAME}
+        if settings.DATABASE_HOST:
+            kwargs['host'] = settings.DATABASE_HOST
+        if settings.DATABASE_USER:
+            kwargs['user'] = settings.DATABASE_USER
+        if settings.DATABASE_PASSWORD:
+            kwargs['password'] = settings.DATABASE_PASSWORD
+        self.connection = Database.connect(**kwargs)
+        self.connection.set_type_trans_in({
+            'TEXT': unicode_conv_in,
+            'BLOB': unicode_conv_in,
+            'TIMESTAMP': timestamp_conv_in
+        })
+#            self.connection.set_type_trans_out({
+#                'TEXT': unicode_conv_out,
+#            })
+        global server_version
+        if not server_version:
+            import re
+            version_re = re.compile('.*Firebird\s([\d\.]+)')
+            m = version_re.match(self.connection.server_version)
+            if not m:
+                raise Exception('Unable to determine Firebird version from version string %r' % self.connection.server_version)
+            server_version = m.groups()[0]
+        assert self.connection != None
+    
+    def get_connection(self):
+        if not self.connection:
+            self.connect()
+        return self.connection
+    
+    def cursor(self):
+        cursor = FirebirdCursorWrapper(self.get_connection())
+        if settings.DEBUG:
+            return util.CursorDebugWrapper(cursor, self)
+        return cursor
+
+    def _commit(self):
+        if self.connection is not None:
+            return self.connection.commit()
+
+    def _rollback(self):
+        if self.connection is not None:
+            return self.connection.rollback()
+
+    def close(self):
+        if self.connection is not None:
+            self.connection.close()
+            self.connection = None
+
+class FirebirdCursorWrapper(Database.Cursor):
+    """
+    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".
+    """
+
+    def execute(self, query, params=()):
+        query = self._convert_query(query, len(params))
+#        import logging
+#        logging.fatal("%s %s" % (query, params))
+        return Database.Cursor.execute(self, query, params)
+
+    def executemany(self, query, params):
+        query = self._convert_query(query, len(params[0]))
+        return Database.Cursor.executemany(self, query, params)
+
+    def _convert_query(self, query, num_params):
+        return query % tuple("?" * num_params)
+
+def dictfetchone(cursor):
+    "Returns a row from the cursor as a dict"
+    return cursor.fetchonemap()
+
+def dictfetchmany(cursor, number):
+    "Returns a certain number of rows from a cursor as a dict"
+    return cursor.fetchmanymap(number)
+
+def dictfetchall(cursor):
+    "Returns all rows from a cursor as a dict"
+    return cursor.fetchallmap()
+
+_quote_sequence_name = lambda n: '"%s_SQ"' % util.truncate_name(n.upper(), DatabaseOperations().max_name_length()-3)
+_quote_trigger_name = lambda n: '"%s_TR"' % util.truncate_name(n.upper(), DatabaseOperations().max_name_length()-3)
Index: django/db/backends/firebird/base.pyc
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: django/db/backends/firebird/base.pyc
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

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,123 @@
+from django.core import management
+import sys, os, time
+
+from base import server_version
+
+# 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.
+
+DATA_TYPES = {
+    'AutoField':                     'INTEGER',
+    'BooleanField':                  'SMALLINT',
+    'CharField':                     'VARCHAR(%(max_length)s)',
+    'CommaSeparatedIntegerField':    'VARCHAR(%(max_length)s)',
+    'DateField':                     'DATE',
+    'DateTimeField':                 'TIMESTAMP',
+    'DecimalField':                  'DECIMAL(%(max_digits)s, %(decimal_places)s)',
+    'FileField':                     'VARCHAR(100)',
+    'FilePathField':                 'VARCHAR(100)',
+    'FloatField':                    'FLOAT',
+    'ImageField':                    'VARCHAR(100)',
+    'IntegerField':                  'INTEGER',
+    'IPAddressField':                'VARCHAR(15)',
+    'ManyToManyField':               None,
+    'NullBooleanField':              'SMALLINT',
+    'OneToOneField':                 'INTEGER',
+    'PhoneNumberField':              'VARCHAR(20)',
+    'PositiveIntegerField':          'INTEGER',
+    'PositiveSmallIntegerField':     'SMALLINT',
+    'SlugField':                     'VARCHAR(%(max_length)s)',
+    'SmallIntegerField':             'SMALLINT',
+    'TextField':                     'BLOB SUB_TYPE TEXT',
+    'TimeField':                     'TIME',
+    'URLField':                      'VARCHAR(200)',
+    'USStateField':                  'VARCHAR(2)',
+}
+
+TEST_DATABASE_PREFIX = 'test_'
+
+def create_test_db(settings, database, verbosity=1, autoclobber=False):
+
+    if verbosity >= 1:
+        print "Creating test database..."
+
+    if settings.TEST_DATABASE_NAME:
+        if os.path.isfile(settings.DATABASE_NAME):
+            TEST_DATABASE_NAME = os.path.join(
+                os.path.dirname(settings.DATABASE_NAME),
+                settings.TEST_DATABASE_NAME)
+        else:
+            import tempfile
+            tempfile.gettempdir()
+            TEST_DATABASE_NAME = os.path.join(
+                tempfile.gettempdir(),
+                settings.TEST_DATABASE_NAME)
+    else:
+        if os.path.isfile(settings.DATABASE_NAME):
+            TEST_DATABASE_NAME = os.path.join(
+                os.path.dirname(settings.DATABASE_NAME),
+                TEST_DATABASE_PREFIX + os.path.basename(settings.DATABASE_NAME))
+        else:
+            import tempfile
+            tempfile.gettempdir()
+            TEST_DATABASE_NAME = os.path.join(
+                tempfile.gettempdir(),
+                TEST_DATABASE_PREFIX + settings.DATABASE_NAME)
+
+    settings.DATABASE_NAME = TEST_DATABASE_NAME
+
+    try:
+        _create_test_db(database, settings)
+    except Exception, e:
+        sys.stderr.write("Got an error creating the test database: %s\n" % e)
+        if not autoclobber:
+            confirm = raw_input("It appears the test database, %s, already exists. Type 'yes' to delete it, or 'no' to cancel: " % TEST_DATABASE_NAME)
+        if autoclobber or confirm == 'yes':
+            try:
+                if verbosity >= 1:
+                    print "Destroying old test database..."
+                _destroy_test_db(database)
+                if verbosity >= 1:
+                    print "Creating test database..."
+                _create_test_db(database, settings)
+            except Exception, e:
+                sys.stderr.write("Got an error recreating the test database: %s\n" % e)
+                sys.exit(2)
+        else:
+            print "Tests cancelled."
+            sys.exit(1)
+
+    management.call_command('syncdb', verbosity=verbosity, interactive=False)
+
+    # Get a cursor (even though we don't need one yet). This has
+    # the side effect of initializing the test database.
+    cursor = database.cursor()
+
+
+def destroy_test_db(settings, connection, old_database_name, verbosity):
+    if verbosity >= 1:
+        print "Destroying test database..."
+
+    # To avoid "database is being accessed by other users" errors.
+    time.sleep(1)
+    _destroy_test_db(connection)
+
+def _create_test_db(database, settings):
+    import kinterbasdb
+    default_charset = server_version < '2' and 'UNICODE_FSS' or 'UTF8'
+    connection = kinterbasdb.create_database("""
+        CREATE DATABASE '%s' user '%s' password '%s' DEFAULT CHARACTER SET %s""" % \
+        (settings.DATABASE_NAME, settings.DATABASE_USER, settings.DATABASE_PASSWORD, default_charset))
+    cursor = connection.cursor()
+    cursor.execute("""
+    DECLARE EXTERNAL FUNCTION rand
+        RETURNS DOUBLE PRECISION
+        BY VALUE ENTRY_POINT 'IB_UDF_rand' MODULE_NAME 'ib_udf';
+    """)
+    connection.commit()
+    connection.close()
+
+def _destroy_test_db(database):
+    database.drop_database()
\ No newline at end of file
Index: django/core/management/sql.py
===================================================================
--- django/core/management/sql.py	(revision 6369)
+++ django/core/management/sql.py	(working copy)
@@ -1,4 +1,5 @@
 from django.core.management.base import CommandError
+from django.conf import settings 
 import os
 import re
 
@@ -262,7 +263,13 @@
         # 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 '')))
+        #field_output.append(style.SQL_KEYWORD('%sNULL' % (not f.null and 'NOT ' or '')))
+        column_null_sql = '%sNULL' % (not f.null and 'NOT ' or '') 
+        if settings.DATABASE_ENGINE == 'firebird' and f.null: 
+            column_null_sql = (not f.null or f.primary_key) and 'NOT NULL' or '' 
+        if column_null_sql: 
+            field_output.append(style.SQL_KEYWORD(column_null_sql))
+            
         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:
Index: tests/modeltests/serializers/models.py
===================================================================
--- tests/modeltests/serializers/models.py	(revision 6369)
+++ tests/modeltests/serializers/models.py	(working copy)
@@ -125,7 +125,7 @@
 
 # You can easily create new objects by deserializing data with an empty PK
 # (It's easier to demo this with JSON...)
->>> new_author_json = '[{"pk": null, "model": "serializers.author", "fields": {"name": "Bill"}}]'
+>>> new_author_json = '[{"pk" : null, "model": "serializers.author", "fields": {"name": "Bill"}}]'
 >>> for obj in serializers.deserialize("json", new_author_json):
 ...     obj.save()
 >>> Author.objects.all()
Index: tests/regressiontests/string_lookup/models.py
===================================================================
--- tests/regressiontests/string_lookup/models.py	(revision 6369)
+++ tests/regressiontests/string_lookup/models.py	(working copy)
@@ -91,6 +91,7 @@
 <Foo: Foo Bjorn>
 
 # Regression tests for #5087: make sure we can perform queries on TextFields.
+# This is unsupported for Firebird < 2.1
 >>> a = Article(name='Test', text='The quick brown fox jumps over the lazy dog.')
 >>> a.save()
 >>> Article.objects.get(text__exact='The quick brown fox jumps over the lazy dog.')
Index: tests/regressiontests/fixtures_regress/models.py
===================================================================
--- tests/regressiontests/fixtures_regress/models.py	(revision 6369)
+++ tests/regressiontests/fixtures_regress/models.py	(working copy)
@@ -36,6 +36,7 @@
         
 # Create a new animal. Without a sequence reset, this new object
 # will take a PK of 1 (on Postgres), and the save will fail.
+# Same goes for Firebird
 # This is a regression test for ticket #3790.
 >>> animal = Animal(name='Platypus', latin_name='Ornithorhynchus anatinus')
 >>> animal.save()
