Ticket #1261: firebird-svn6654.diff

File firebird-svn6654.diff, 54.3 KB (added by i_i, 8 years ago)

a lot of fixes and improvements for firebird backend

  • django/db/models/base.py

     
    241241            db_values = [f.get_db_prep_save(raw and getattr(self, f.attname) or f.pre_save(self, True)) for f in self._meta.fields if not isinstance(f, AutoField)]
    242242            # If the PK has been manually set, respect that.
    243243            if pk_set:
    244                 field_names += [f.column for f in self._meta.fields if isinstance(f, AutoField)]
     244                if connection.features.quote_autofields:
     245                    field_names += [qn(f.column) for f in self._meta.fields if isinstance(f, AutoField)]
     246                else:
     247                    field_names += [f.column for f in self._meta.fields if isinstance(f, AutoField)]
    245248                db_values += [f.get_db_prep_save(raw and getattr(self, f.attname) or f.pre_save(self, True)) for f in self._meta.fields if isinstance(f, AutoField)]
    246249            placeholders = ['%s'] * len(field_names)
    247250            if self._meta.order_with_respect_to:
  • django/db/models/fields/__init__.py

     
    148148        data_types = get_creation_module().DATA_TYPES
    149149        internal_type = self.get_internal_type()
    150150        if internal_type not in data_types:
    151             return None
     151            return None   
    152152        return data_types[internal_type] % self.__dict__
    153 
     153   
     154    def db_type_check(self):
     155        creation_module = get_creation_module()
     156        if hasattr(creation_module, 'DATA_TYPE_CHECKS'):
     157            internal_type = self.get_internal_type()
     158            data_checks = creation_module.DATA_TYPE_CHECKS
     159            if internal_type in data_checks:
     160                return data_checks[internal_type] % self.__dict__
     161        return None
     162   
    154163    def validate_full(self, field_data, all_data):
    155164        """
    156165        Returns a list of errors for this field. This is the main interface,
     
    208217        return value
    209218
    210219    def get_db_prep_lookup(self, lookup_type, value):
     220        from django.db import connection
    211221        "Returns field's value prepared for database lookup."
    212222        if lookup_type in ('exact', 'regex', 'iregex', 'gt', 'gte', 'lt', 'lte', 'month', 'day', 'search'):
    213223            return [value]
    214224        elif lookup_type in ('range', 'in'):
    215225            return value
    216226        elif lookup_type in ('contains', 'icontains'):
     227            if connection.features.uses_custom_icontains and lookup_type == 'icontains':
     228                return [value]   
    217229            return ["%%%s%%" % prep_for_like_query(value)]
    218230        elif lookup_type == 'iexact':
    219231            return [prep_for_like_query(value)]
    220232        elif lookup_type in ('startswith', 'istartswith'):
     233            if connection.features.uses_custom_startswith:
     234                return [value]
    221235            return ["%s%%" % prep_for_like_query(value)]
    222236        elif lookup_type in ('endswith', 'iendswith'):
    223237            return ["%%%s" % prep_for_like_query(value)]
     
    589603            # doesn't support microseconds.
    590604            if settings.DATABASE_ENGINE == 'mysql' and hasattr(value, 'microsecond'):
    591605                value = value.replace(microsecond=0)
    592             value = smart_unicode(value)
     606            #Firebird supports native datetime
     607            if settings.DATABASE_ENGINE != 'firebird':
     608                value = smart_unicode(value)
    593609        return Field.get_db_prep_save(self, value)
    594610
    595611    def get_db_prep_lookup(self, lookup_type, value):
     
    9971013            # doesn't support microseconds.
    9981014            if settings.DATABASE_ENGINE == 'mysql' and hasattr(value, 'microsecond'):
    9991015                value = value.replace(microsecond=0)
    1000             if settings.DATABASE_ENGINE == 'oracle':
    1001                 # cx_Oracle expects a datetime.datetime to persist into TIMESTAMP field.
     1016            if settings.DATABASE_ENGINE in ('oracle', 'firebird'):
     1017                # cx_Oracle and kinterbasdb expect a datetime.datetime to persist into TIMESTAMP field.
    10021018                if isinstance(value, datetime.time):
    10031019                    value = datetime.datetime(1900, 1, 1, value.hour, value.minute,
    10041020                                              value.second, value.microsecond)
  • django/db/models/fields/related.py

     
    331331                        new_ids.add(obj)
    332332                # Add the newly created or already existing objects to the join table.
    333333                # First find out which items are already added, to avoid adding them twice
     334                qn = connection.ops.quote_name
    334335                cursor = connection.cursor()
    335336                cursor.execute("SELECT %s FROM %s WHERE %s = %%s AND %s IN (%s)" % \
    336                     (target_col_name, self.join_table, source_col_name,
    337                     target_col_name, ",".join(['%s'] * len(new_ids))),
     337                    (qn(target_col_name), qn(self.join_table), qn(source_col_name),
     338                    qn(target_col_name), ",".join(['%s'] * len(new_ids))),
    338339                    [self._pk_val] + list(new_ids))
    339340                existing_ids = set([row[0] for row in cursor.fetchall()])
    340341
    341342                # Add the ones that aren't there already
    342343                for obj_id in (new_ids - existing_ids):
    343344                    cursor.execute("INSERT INTO %s (%s, %s) VALUES (%%s, %%s)" % \
    344                         (self.join_table, source_col_name, target_col_name),
     345                        (qn(self.join_table), qn(source_col_name), qn(target_col_name)),
    345346                        [self._pk_val, obj_id])
    346347                transaction.commit_unless_managed()
    347348
  • django/db/models/query.py

     
    11111111            # Last query term was a normal field.
    11121112            column = field.column
    11131113            db_type = field.db_type()
    1114 
     1114        if settings.DATABASE_ENGINE == 'firebird':
     1115            current_table = qn(current_table)
     1116            column = qn(column)   
    11151117        where.append(get_where_clause(lookup_type, current_table + '.', column, value, db_type))
    11161118        params.extend(field.get_db_prep_lookup(lookup_type, value))
    11171119
     
    11461148            if isinstance(f, generic.GenericRelation):
    11471149                from django.contrib.contenttypes.models import ContentType
    11481150                query_extra = 'AND %s=%%s' % f.rel.to._meta.get_field(f.content_type_field_name).column
     1151                if settings.DATABASE_ENGINE == 'firebird':
     1152                    query_extra = 'AND %s=%%s' % qn(f.rel.to._meta.get_field(f.content_type_field_name).column)
    11491153                args_extra = [ContentType.objects.get_for_model(cls).id]
    11501154            else:
    11511155                query_extra = ''
  • django/db/backends/__init__.py

     
    4545    autoindexes_primary_keys = True
    4646    inline_fk_references = True
    4747    needs_datetime_string_cast = True
     48    needs_default_null = False
    4849    needs_upper_for_iops = False
    4950    supports_constraints = True
    5051    supports_tablespaces = False
     52    quote_autofields = False
    5153    uses_case_insensitive_names = False
     54    uses_custom_icontains = False
     55    uses_custom_startswith = False
    5256    uses_custom_queryset = False
    5357
    5458class BaseDatabaseOperations(object):
     
    6569        This SQL is executed when a table is created.
    6670        """
    6771        return None
    68 
     72   
     73    def cascade_delete_update_sql(self):
     74        """
     75        Returns the SQL necessary to make a cascading deletes and updates
     76        of foreign key references during a CREATE TABLE statement.
     77        """
     78        return ''
     79   
    6980    def date_extract_sql(self, lookup_type, field_name):
    7081        """
    7182        Given a lookup_type of 'year', 'month' or 'day', returns the SQL that
  • django/db/backends/firebird/base.py

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

     
    4747        if timeout is None:
    4848            timeout = self.default_timeout
    4949        cursor = connection.cursor()
    50         cursor.execute("SELECT COUNT(*) FROM %s" % self._table)
     50        qn = connection.ops.quote_name
     51        cursor.execute("SELECT COUNT(*) FROM %s" % qn(self._table))
    5152        num = cursor.fetchone()[0]
    5253        now = datetime.now().replace(microsecond=0)
    5354        exp = datetime.fromtimestamp(time.time() + timeout).replace(microsecond=0)
    5455        if num > self._max_entries:
    5556            self._cull(cursor, now)
    5657        encoded = base64.encodestring(pickle.dumps(value, 2)).strip()
    57         cursor.execute("SELECT cache_key FROM %s WHERE cache_key = %%s" % self._table, [key])
     58        cursor.execute("SELECT cache_key FROM %s WHERE cache_key = %%s" % qn(self._table), [key])
    5859        try:
    5960            if mode == 'set' and cursor.fetchone():
    60                 cursor.execute("UPDATE %s SET value = %%s, expires = %%s WHERE cache_key = %%s" % self._table, [encoded, str(exp), key])
     61                cursor.execute("UPDATE %s SET value = %%s, expires = %%s WHERE cache_key = %%s" % qn(self._table), [encoded, str(exp), key])
    6162            else:
    62                 cursor.execute("INSERT INTO %s (cache_key, value, expires) VALUES (%%s, %%s, %%s)" % self._table, [key, encoded, str(exp)])
     63                cursor.execute("INSERT INTO %s (cache_key, value, expires) VALUES (%%s, %%s, %%s)" % qn(self._table), [key, encoded, str(exp)])
    6364        except DatabaseError:
    6465            # To be threadsafe, updates/inserts are allowed to fail silently
    6566            pass
  • django/core/management/sql.py

     
    11from django.core.management.base import CommandError
     2from django.conf import settings
    23import os
    34import re
    45
     
    253254    pending_references = {}
    254255    qn = connection.ops.quote_name
    255256    inline_references = connection.features.inline_fk_references
     257    is_firebird = hasattr(connection.ops, "firebird_version")
    256258    for f in opts.fields:
    257         col_type = f.db_type()
     259        col_type, col_type_check = f.db_type(), f.db_type_check()
     260        if is_firebird:
     261            fb_version = "%s.%s" % (connection.ops.firebird_version[0], connection.ops.firebird_version[1])
     262            page_size = connection.ops.page_size
     263            #look at: http://www.volny.cz/iprenosil/interbase/ip_ib_indexcalculator.htm
     264            if connection.ops.index_limit < 1000:
     265                strip2ascii = False
     266                if (f.unique or f.primary_key or f.db_index):
     267                    strip2ascii = True
     268                if len(opts.unique_together) > 0:
     269                    if f.column in opts.unique_together[0]:
     270                        strip2ascii = True     
     271                if strip2ascii:
     272                    if col_type.startswith('varchar'):
     273                        if not col_type.endswith('ASCII'):
     274                            col_type += " CHARACTER SET ASCII"
     275                            msg =  "WARNING: Character encoding of the column '%s' has changed to ASCII\n"
     276                            msg += "         due to restriction of %s bytes in Firebird %s"
     277                            if not page_size:
     278                                print  msg % (f.column, connection.ops.index_limit, fb_version)
     279                            else:
     280                                msg += " with page size %s"
     281                                print  msg % (f.column, connection.ops.index_limit, fb_version, page_size)
     282                        if opts.unique_together:
     283                            num_ufields = len(opts.unique_together[0])
     284                            limit = connection.ops.index_limit - ((num_ufields - 1)*64)
     285                            max_length = int(limit/float(num_ufields))
     286                            old_length = int(f.__dict__["max_length"])
     287                            if old_length > max_length:
     288                                col_type = "varchar(%i) CHARACTER SET ASCII" % max_length
     289                                print "         The maximum length of '%s' is now %s instead of %s"\
     290                                 % (f.column, max_length, old_length)
     291               
    258292        tablespace = f.db_tablespace or opts.db_tablespace
    259293        if col_type is None:
    260294            # Skip ManyToManyFields, because they're not represented as
     
    263297        # Make the definition (e.g. 'foo VARCHAR(30)') for this field.
    264298        field_output = [style.SQL_FIELD(qn(f.column)),
    265299            style.SQL_COLTYPE(col_type)]
    266         field_output.append(style.SQL_KEYWORD('%sNULL' % (not f.null and 'NOT ' or '')))
     300        nullstring = ""
     301        if connection.features.needs_default_null:
     302            nullstring = "DEFAULT "
     303        field_output.append(style.SQL_KEYWORD('%sNULL' % (not f.null and 'NOT ' or nullstring)))
    267304        if f.unique and (not f.primary_key or connection.features.allows_unique_and_pk):
    268305            field_output.append(style.SQL_KEYWORD('UNIQUE'))
    269306        if f.primary_key:
     
    276313            if inline_references and f.rel.to in known_models:
    277314                field_output.append(style.SQL_KEYWORD('REFERENCES') + ' ' + \
    278315                    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()
     316                    style.SQL_FIELD(qn(f.rel.to._meta.get_field(f.rel.field_name).column)) + ')' + \
     317                    connection.ops.cascade_delete_update_sql() + connection.ops.deferrable_sql()
    281318                )
    282319            else:
    283320                # We haven't yet created the table to which this field
    284321                # is related, so save it for later.
    285322                pr = pending_references.setdefault(f.rel.to, []).append((model, f))
     323        if col_type_check:
     324            field_output.extend([ style.SQL_KEYWORD('CHECK'), style.SQL_COLTYPE(col_type_check)  ])         
    286325        table_output.append(' '.join(field_output))
    287326    if opts.order_with_respect_to:
    288327        table_output.append(style.SQL_FIELD(qn('_order')) + ' ' + \
     
    335374                final_output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' % \
    336375                    (qn(r_table), truncate_name(r_name, connection.ops.max_name_length()),
    337376                    qn(r_col), qn(table), qn(col),
     377                    connection.ops.cascade_delete_update_sql(),
    338378                    connection.ops.deferrable_sql()))
    339379            del pending_references[model]
    340380    return final_output
     
    364404                tablespace_sql))
    365405            if inline_references:
    366406                deferred = []
    367                 table_output.append('    %s %s %s %s (%s)%s,' %
     407                table_output.append('    %s %s %s %s (%s)%s%s,' %
    368408                    (style.SQL_FIELD(qn(f.m2m_column_name())),
    369409                    style.SQL_COLTYPE(models.ForeignKey(model).db_type()),
    370410                    style.SQL_KEYWORD('NOT NULL REFERENCES'),
    371411                    style.SQL_TABLE(qn(opts.db_table)),
    372412                    style.SQL_FIELD(qn(opts.pk.column)),
     413                    connection.ops.cascade_delete_update_sql(),
    373414                    connection.ops.deferrable_sql()))
    374                 table_output.append('    %s %s %s %s (%s)%s,' %
     415                table_output.append('    %s %s %s %s (%s)%s%s,' %
    375416                    (style.SQL_FIELD(qn(f.m2m_reverse_name())),
    376417                    style.SQL_COLTYPE(models.ForeignKey(f.rel.to).db_type()),
    377418                    style.SQL_KEYWORD('NOT NULL REFERENCES'),
    378419                    style.SQL_TABLE(qn(f.rel.to._meta.db_table)),
    379420                    style.SQL_FIELD(qn(f.rel.to._meta.pk.column)),
     421                    connection.ops.cascade_delete_update_sql(),
    380422                    connection.ops.deferrable_sql()))
    381423            else:
    382424                table_output.append('    %s %s %s,' %
     
    412454                (qn(r_table),
    413455                truncate_name(r_name, connection.ops.max_name_length()),
    414456                qn(r_col), qn(table), qn(col),
     457                connection.ops.cascade_delete_update_sql(),
    415458                connection.ops.deferrable_sql()))
    416459
    417460            # Add any extra SQL needed to support auto-incrementing PKs
  • tests/regressiontests/initial_sql_regress/sql/simple.sql

     
    1 INSERT INTO initial_sql_regress_simple (name) VALUES ('John');
    2 INSERT INTO initial_sql_regress_simple (name) VALUES ('Paul');
    3 INSERT INTO initial_sql_regress_simple (name) VALUES ('Ringo');
    4 INSERT INTO initial_sql_regress_simple (name) VALUES ('George');
    5 INSERT INTO initial_sql_regress_simple (name) VALUES ('Miles O''Brien');
    6 INSERT INTO initial_sql_regress_simple (name) VALUES ('Semicolon;Man');
    7 INSERT INTO initial_sql_regress_simple (name) VALUES ('This line has a Windows line ending');
     1INSERT INTO "initial_sql_regress_simple" ("name") VALUES ('John');
     2INSERT INTO "initial_sql_regress_simple" ("name") VALUES ('Paul');
     3INSERT INTO "initial_sql_regress_simple" ("name") VALUES ('Ringo');
     4INSERT INTO "initial_sql_regress_simple" ("name") VALUES ('George');
     5INSERT INTO "initial_sql_regress_simple" ("name") VALUES ('Miles O''Brien');
     6INSERT INTO "initial_sql_regress_simple" ("name") VALUES ('Semicolon;Man');
     7INSERT INTO "initial_sql_regress_simple" ("name") VALUES ('This line has a Windows line ending');
    88
  • tests/regressiontests/backends/models.py

     
    2222>>> opts = Square._meta
    2323>>> f1, f2 = opts.get_field('root'), opts.get_field('square')
    2424>>> query = ('INSERT INTO %s (%s, %s) VALUES (%%s, %%s)'
    25 ...         % (t_convert(opts.db_table), qn(f1.column), qn(f2.column)))
     25...         % qn((t_convert(opts.db_table)), qn(f1.column), qn(f2.column)))
    2626>>> cursor.executemany(query, [(i, i**2) for i in range(-5, 6)]) and None or None
    2727>>> Square.objects.order_by('root')
    2828[<Square: -5 ** 2 == 25>, <Square: -4 ** 2 == 16>, <Square: -3 ** 2 == 9>, <Square: -2 ** 2 == 4>, <Square: -1 ** 2 == 1>, <Square: 0 ** 2 == 0>, <Square: 1 ** 2 == 1>, <Square: 2 ** 2 == 4>, <Square: 3 ** 2 == 9>, <Square: 4 ** 2 == 16>, <Square: 5 ** 2 == 25>]
Back to Top