Ticket #1261: firebird-6669.diff

File firebird-6669.diff, 86.3 KB (added by i_i, 8 years ago)

code clean up and support for stored procedures and computed columns

  • django/db/models/base.py

     
    44from django.core.exceptions import ObjectDoesNotExist
    55from django.db.models.fields import AutoField, ImageField, FieldDoesNotExist
    66from django.db.models.fields.related import OneToOneRel, ManyToOneRel
     7from django.db.models.fields.computed import ComputedField
    78from django.db.models.query import delete_objects
    89from django.db.models.options import Options, AdminOptions
    910from django.db import connection, transaction
     
    227228                self._meta.pk.get_db_prep_lookup('exact', pk_val))
    228229            # If it does already exist, do an UPDATE.
    229230            if cursor.fetchone():
    230                 db_values = [f.get_db_prep_save(raw and getattr(self, f.attname) or f.pre_save(self, False)) for f in non_pks]
     231                db_values = [f.get_db_prep_save(raw and getattr(self, f.attname) or f.pre_save(self, False)) for f in non_pks if not isinstance(f, ComputedField)]
    231232                if db_values:
    232233                    cursor.execute("UPDATE %s SET %s WHERE %s=%%s" % \
    233234                        (qn(self._meta.db_table),
    234                         ','.join(['%s=%%s' % qn(f.column) for f in non_pks]),
     235                        ','.join(['%s=%%s' % qn(f.column) for f in non_pks if not isinstance(f, ComputedField)]),
    235236                        qn(self._meta.pk.column)),
    236237                        db_values + self._meta.pk.get_db_prep_lookup('exact', pk_val))
    237238            else:
    238239                record_exists = False
    239240        if not pk_set or not record_exists:
    240             field_names = [qn(f.column) for f in self._meta.fields if not isinstance(f, AutoField)]
    241             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)]
     241            field_names = [qn(f.column) for f in self._meta.fields if not isinstance(f, (AutoField, ComputedField))]
     242            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, ComputedField))]
    242243            # If the PK has been manually set, respect that.
    243244            if pk_set:
    244                 field_names += [f.column for f in self._meta.fields if isinstance(f, AutoField)]
     245                if connection.features.quote_autofields:
     246                    field_names += [qn(f.column) for f in self._meta.fields if isinstance(f, AutoField)]
     247                else:
     248                    field_names += [f.column for f in self._meta.fields if isinstance(f, AutoField)]
    245249                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)]
    246250            placeholders = ['%s'] * len(field_names)
    247251            if self._meta.order_with_respect_to:
  • django/db/models/fields/__init__.py

     
    66except ImportError:
    77    from django.utils import _decimal as decimal    # for Python 2.3
    88
    9 from django.db import get_creation_module
     9from django.db import connection, get_creation_module
    1010from django.db.models import signals
    1111from django.dispatch import dispatcher
    1212from django.conf import settings
     
    6666#
    6767#     getattr(obj, opts.pk.attname)
    6868
    69 class Field(object):
     69class _Field(object):
    7070    # Provide backwards compatibility for the maxlength attribute and
    7171    # argument for this class and all subclasses.
    7272    __metaclass__ = LegacyMaxlength
     
    8383        core=False, rel=None, default=NOT_PROVIDED, editable=True, serialize=True,
    8484        prepopulate_from=None, unique_for_date=None, unique_for_month=None,
    8585        unique_for_year=None, validator_list=None, choices=None, radio_admin=None,
    86         help_text='', db_column=None, db_tablespace=None):
     86        help_text='', db_column=None, db_tablespace=None, encoding=None,
     87        on_update=None, on_delete=None):
    8788        self.name = name
    8889        self.verbose_name = verbose_name
    8990        self.primary_key = primary_key
    9091        self.max_length, self.unique = max_length, unique
     92        self.encoding = encoding
    9193        self.blank, self.null = blank, null
    9294        # Oracle treats the empty string ('') as null, so coerce the null
    9395        # option whenever '' is a possible value.
     
    105107        self.help_text = help_text
    106108        self.db_column = db_column
    107109        self.db_tablespace = db_tablespace
     110        self.on_update = on_update
     111        self.on_delete = on_delete
    108112
    109113        # Set db_index to True if the field has a relationship and doesn't explicitly set db_index.
    110114        self.db_index = db_index
     
    148152        data_types = get_creation_module().DATA_TYPES
    149153        internal_type = self.get_internal_type()
    150154        if internal_type not in data_types:
    151             return None
     155            return None   
    152156        return data_types[internal_type] % self.__dict__
    153157
    154158    def validate_full(self, field_data, all_data):
     
    402406        "Returns the value of this field in the given model instance."
    403407        return getattr(obj, self.attname)
    404408
     409# Use the backend's Field class if it defines one. Otherwise, use _Field.
     410if connection.features.uses_custom_field:
     411    Field = connection.ops.field_class(_Field)
     412else:
     413    Field = _Field
     414
    405415class AutoField(Field):
    406416    empty_strings_allowed = False
    407417    def __init__(self, *args, **kwargs):
     
    688698        defaults.update(kwargs)
    689699        return super(DecimalField, self).formfield(**defaults)
    690700
     701class DefaultCharField(CharField):
     702    def __init__(self, *args, **kwargs):
     703        DEFAULT_MAX_LENGTH = 100
     704        if hasattr(settings, 'DEFAULT_MAX_LENGTH'):
     705           DEFAULT_MAX_LENGTH = settings.DEFAULT_MAX_LENGT
     706        kwargs['max_length'] = kwargs.get('max_length', DEFAULT_MAX_LENGTH)
     707        CharField.__init__(self, *args, **kwargs)
     708
    691709class EmailField(CharField):
    692710    def __init__(self, *args, **kwargs):
    693711        kwargs['max_length'] = kwargs.get('max_length', 75)
     
    890908        defaults.update(kwargs)
    891909        return super(IPAddressField, self).formfield(**defaults)
    892910
     911class LargeTextField(Field):
     912    def get_manipulator_field_objs(self):
     913        return [oldforms.LargeTextField]
     914
     915    def formfield(self, **kwargs):
     916        defaults = {'widget': forms.Textarea}
     917        defaults.update(kwargs)
     918        return super(LargeTextField, self).formfield(**defaults)
     919
    893920class NullBooleanField(Field):
    894921    empty_strings_allowed = False
    895922    def __init__(self, *args, **kwargs):
     
    9971024            # doesn't support microseconds.
    9981025            if settings.DATABASE_ENGINE == 'mysql' and hasattr(value, 'microsecond'):
    9991026                value = value.replace(microsecond=0)
    1000             if settings.DATABASE_ENGINE == 'oracle':
    1001                 # cx_Oracle expects a datetime.datetime to persist into TIMESTAMP field.
     1027            if settings.DATABASE_ENGINE in ('oracle', 'firebird'):
     1028                # cx_Oracle and kinterbasdb expect a datetime.datetime to persist into TIMESTAMP field.
    10021029                if isinstance(value, datetime.time):
    10031030                    value = datetime.datetime(1900, 1, 1, value.hour, value.minute,
    10041031                                              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):
    343                     cursor.execute("INSERT INTO %s (%s, %s) VALUES (%%s, %%s)" % \
    344                         (self.join_table, source_col_name, target_col_name),
    345                         [self._pk_val, obj_id])
     344                    cursor.execute('INSERT INTO %s (%s, %s) VALUES (%%s, %%s)' % \
     345                        (qn(self.join_table), qn(source_col_name), qn(target_col_name)),
     346                        (self._pk_val, obj_id))
    346347                transaction.commit_unless_managed()
    347348
    348349        def _remove_items(self, source_col_name, target_col_name, *objs):
  • django/db/models/fields/computed.py

     
     1from django.db.models.fields import IntegerField
     2
     3class ComputedField(IntegerField):
     4    def __init__(self, **kwargs):
     5        expression = kwargs.pop('expression')
     6        raw_params = kwargs.pop('params')
     7        params = []
     8        for rp in raw_params:
     9            params.append('"%s"' % rp)
     10        self.expression = expression % tuple(params)
     11        IntegerField.__init__(self, **kwargs)
     12   
     13    def db_type(self):
     14        return 'ComputedField'
     15
     16#TODO:  Add support for other field types
     17#       Add support for fields which values are depended on INSERT, UPDATE or DLETE
     18#       triggers of other fields/models
     19
     20   
     21       
     22   
     23
     24
     25
  • django/db/models/__init__.py

     
    77from django.db.models.manager import Manager
    88from django.db.models.base import Model, AdminOptions
    99from django.db.models.fields import *
     10from django.db.models.procedures import Procedure
    1011from django.db.models.fields.subclassing import SubfieldBase
    1112from django.db.models.fields.related import ForeignKey, OneToOneField, ManyToManyField, ManyToOneRel, ManyToManyRel, OneToOneRel, TABULAR, STACKED
     13from django.db.models.fields.computed import ComputedField
    1214from django.db.models import signals
    1315from django.utils.functional import curry
    1416from django.utils.text import capfirst
  • django/db/models/procedures/__init__.py

     
     1from django.db import connection, transaction
     2
     3class procedure_meta(type):
     4    """
     5    Metaclass for native stored procedures behaving as normal Django model methods
     6    Subclass Procedure inside your model, create procedures tuple
     7    and use the class as ordinary method
     8    Please treat the resulting classes as methods because they don't
     9    behave like classes anymore
     10    Look at tests/customtests/custom_methods/model.py for demonstration
     11    """
     12    def __init__(cls, name, bases, attrs):
     13        super(procedure_meta, cls).__init__(name, bases, attrs)
     14        params, returns, body = None, None, None
     15        if '__params__' in attrs:
     16            params = attrs['__params__']
     17        if '__returns__' in attrs:
     18            returns = attrs['__returns__']
     19        if name != 'Procedure':
     20            assert '__body__' in attrs, "Procedure must have body"   
     21            header = ['CREATE PROCEDURE %s ' % name]
     22            if params:
     23                header.append('(')
     24                header.append(', '.join([param + ' ' + f for param, f in params]))
     25                header.append(')')
     26            if returns:
     27                header.append('\nRETURNS (')
     28                header.append(', '.join([ret + ' ' + f for ret, f in returns]))
     29                header.append(')')   
     30            cls.__sql__ = [''.join(header)]
     31            cls.__sql__.extend(['AS', 'BEGIN', attrs['__body__'], 'END'])
     32            cls.sql = '\n'.join(cls.__sql__)
     33            cls.params = params
     34            cls.returns = returns
     35            cls.procedure_name = name
     36   
     37    def create_procedure_sql(cls):
     38        return cls.sql
     39   
     40    def __call__(cls, *args):
     41        cursor = connection.cursor()
     42        if not cls.returns:
     43            cursor.execute('EXECUTE PROCEDURE "%s" %s;' %\
     44                (cls.procedure_name,
     45                 ' ,'.join("'%s'" % arg for arg in args)))
     46        else:
     47            cursor.execute_straight('SELECT %s FROM %s(%s);' %\
     48                (', '.join("%s" % ret[0] for ret in cls.returns),
     49                 cls.procedure_name,
     50                 ', '.join("?" * len(args))), args)       
     51            return cursor.fetchall()
     52
     53class Procedure(object):
     54    __metaclass__ = procedure_meta
     55
  • django/db/models/query.py

     
    612612        columns = [f.column for f in fields]
    613613        select = ['%s.%s' % (qn(self.model._meta.db_table), qn(c)) for c in columns]
    614614        if extra_select:
    615             select.extend(['(%s) AS %s' % (quote_only_if_word(s[1]), qn(s[0])) for s in extra_select])
     615            if not settings.DATABASE_ENGINE == 'firebird':
     616                select.extend(['(%s) AS %s' % (quote_only_if_word(s[1]), qn(s[0])) for s in extra_select])
     617            else:
     618                select.extend(['(%s) AS %s' % (connection.ops.quote_id_plus_number(s[1]), qn(s[0])) for s in extra_select])
    616619            field_names.extend([f[0] for f in extra_select])
    617620
    618621        cursor = connection.cursor()
     
    11111114            # Last query term was a normal field.
    11121115            column = field.column
    11131116            db_type = field.db_type()
    1114 
    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

     
    4848    needs_upper_for_iops = False
    4949    supports_constraints = True
    5050    supports_tablespaces = False
     51    quote_autofields = False
    5152    uses_case_insensitive_names = False
     53    uses_custom_field = False
    5254    uses_custom_queryset = False
    5355
    5456class BaseDatabaseOperations(object):
     
    199201        """
    200202        raise NotImplementedError()
    201203
     204    def reference_name(self, r_col, col, r_table, table):
     205        return '%s_refs_%s_%x' % (r_col, col, abs(hash((r_table, table))))
     206
    202207    def random_function_sql(self):
    203208        """
    204209        Returns a SQL expression that returns a random value.
  • 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
     13import sys
     14try:
     15    import decimal
     16except ImportError:
     17    from django.utils import _decimal as decimal    # for Python 2.3
     18
     19try:
     20    import kinterbasdb as Database
     21except ImportError, e:
     22    from django.core.exceptions import ImproperlyConfigured
     23    raise ImproperlyConfigured, "Error loading KInterbasDB module: %s" % e
     24
     25DatabaseError = Database.DatabaseError
     26IntegrityError = Database.IntegrityError
     27
     28class DatabaseFeatures(BaseDatabaseFeatures):
     29    inline_fk_references = False
     30    needs_datetime_string_cast = False
     31    needs_upper_for_iops = True
     32    quote_autofields = True
     33    uses_custom_field = True
     34    uses_custom_queryset = True
     35
     36################################################################################
     37# Database operations (db.connection.ops) 
     38class DatabaseOperations(BaseDatabaseOperations):
     39    """
     40    This class encapsulates all backend-specific differences, such as the way
     41    a backend performs ordering or calculates the ID of a recently-inserted
     42    row.
     43    """
     44    # Utility ops: names, version, page size etc.:
     45    _max_name_length = 31
     46    def __init__(self):
     47        self._firebird_version = None
     48        self._page_size = None
     49   
     50    def get_generator_name(self, name):
     51        return '%s$G' % util.truncate_name(name.strip('"'), self._max_name_length-2).upper()
     52       
     53    def get_trigger_name(self, name):
     54        return '%s$T' % util.truncate_name(name.strip('"'), self._max_name_length-2).upper()
     55   
     56    def _get_firebird_version(self):
     57        if self._firebird_version is None:
     58            from django.db import connection
     59            self._firebird_version = [int(val) for val in connection.server_version.split()[-1].split('.')]
     60        return self._firebird_version
     61    firebird_version = property(_get_firebird_version)
     62 
     63    def reference_name(self, r_col, col, r_table, table):
     64        base_name = util.truncate_name('%s$%s' % (r_col, col), self._max_name_length-5)
     65        return ('%s$%x' % (base_name, abs(hash((r_table, table))))).upper()
     66   
     67    def _get_page_size(self):
     68        if self._page_size is None:
     69            from django.db import connection
     70            self._page_size = connection.database_info(Database.isc_info_page_size, 'i')
     71        return self._page_size
     72    page_size = property(_get_page_size)
     73   
     74    def _get_index_limit(self):
     75        if self.firebird_version[0] < 2:
     76            self._index_limit = 252
     77        else:
     78            page_size = self._get_page_size()
     79            self._index_limit = page_size/4
     80        return self._index_limit
     81    index_limit = property(_get_index_limit)
     82   
     83    def max_name_length(self):
     84        return self._max_name_length
     85   
     86    def quote_name(self, name):
     87        name = '"%s"' % util.truncate_name(name.strip('"'), self._max_name_length)
     88        return name
     89   
     90    def quote_id_plus_number(self, name):
     91        try:
     92            return '"%s" + %s' % tuple(s.strip() for s in name.strip('"').split('+'))
     93        except:
     94            return self.quote_name(name)
     95   
     96    def field_cast_sql(self, db_type):
     97        return '%s'
     98
     99    ############################################################################
     100    # Basic SQL ops:   
     101    def last_insert_id(self, cursor, table_name, pk_name=None):
     102        generator_name = self.get_generator_name(table_name)
     103        cursor.execute('SELECT GEN_ID(%s, 0) from RDB$DATABASE' % generator_name)
     104        return cursor.fetchone()[0]
     105
     106    def date_extract_sql(self, lookup_type, column_name):
     107        # lookup_type is 'year', 'month', 'day'
     108        return "EXTRACT(%s FROM %s)" % (lookup_type, column_name)
     109
     110    def date_trunc_sql(self, lookup_type, column_name):
     111        if lookup_type == 'year':
     112             sql = "EXTRACT(year FROM %s)||'-01-01 00:00:00'" % column_name
     113        elif lookup_type == 'month':
     114            sql = "EXTRACT(year FROM %s)||'-'||EXTRACT(month FROM %s)||'-01 00:00:00'" % (column_name, column_name)
     115        elif lookup_type == 'day':
     116            sql = "EXTRACT(year FROM %s)||'-'||EXTRACT(month FROM %s)||'-'||EXTRACT(day FROM %s)||' 00:00:00'" % (column_name, column_name, column_name)
     117        return "CAST(%s AS TIMESTAMP)" % sql
     118
     119    def datetime_cast_sql(self):
     120        return None
     121
     122    def drop_sequence_sql(self, table):
     123        return "DROP GENERATOR %s;" % self.get_generator_name(table)
     124   
     125    def drop_foreignkey_sql(self):
     126        return "DROP CONSTRAINT"
     127       
     128    def limit_offset_sql(self, limit, offset=None):
     129        # limits are handled in custom FirebirdQuerySet
     130        assert False, 'Limits are handled in a different way in Firebird'
     131        return ""
     132
     133    def random_function_sql(self):
     134        return "rand()"
     135
     136    def pk_default_value(self):
     137        """
     138        Returns the value to use during an INSERT statement to specify that
     139        the field should use its default value.
     140        """
     141        return 'NULL'
     142   
     143    def start_transaction_sql(self):
     144        return ""
     145
     146    def fulltext_search_sql(self, field_name):
     147        # We use varchar for TextFields so this is possible
     148        # Look at http://www.volny.cz/iprenosil/interbase/ip_ib_strings.htm
     149        return '%%s CONTAINING %s' % self.quote_name(field_name)
     150
     151    ############################################################################
     152    # Advanced SQL ops:
     153    def autoinc_sql(self, style, table_name, column_name):
     154        """
     155        To simulate auto-incrementing primary keys in Firebird, we have to
     156        create a generator and a trigger.
     157   
     158        Create the generators and triggers names based only on table name
     159        since django only support one auto field per model
     160        """
     161   
     162        generator_name = self.get_generator_name(table_name)
     163        trigger_name = self.get_trigger_name(table_name)
     164        column_name = self.quote_name(column_name)
     165        table_name = self.quote_name(table_name)
     166       
     167        generator_sql = "%s %s;" % ( style.SQL_KEYWORD('CREATE GENERATOR'),
     168                                     generator_name)     
     169        trigger_sql = "\n".join([
     170            "%s %s %s %s" % ( \
     171            style.SQL_KEYWORD('CREATE TRIGGER'), trigger_name, style.SQL_KEYWORD('FOR'),
     172            style.SQL_TABLE(table_name)),
     173            "%s 0 %s" % (style.SQL_KEYWORD('ACTIVE BEFORE INSERT POSITION'), style.SQL_KEYWORD('AS')),
     174            style.SQL_KEYWORD('BEGIN'),
     175            "  %s ((%s.%s %s) %s (%s.%s = 0)) %s" % ( \
     176                style.SQL_KEYWORD('IF'),
     177                style.SQL_KEYWORD('NEW'), style.SQL_FIELD(column_name), style.SQL_KEYWORD('IS NULL'),
     178                style.SQL_KEYWORD('OR'), style.SQL_KEYWORD('NEW'), style.SQL_FIELD(column_name),
     179                style.SQL_KEYWORD('THEN')
     180            ),
     181            "  %s" % style.SQL_KEYWORD('BEGIN'),
     182            "    %s.%s = %s(%s, 1);" % ( \
     183                style.SQL_KEYWORD('NEW'), style.SQL_FIELD(column_name),
     184                style.SQL_KEYWORD('GEN_ID'), generator_name
     185            ),
     186            "  %s" % style.SQL_KEYWORD('END'),
     187            "%s;" % style.SQL_KEYWORD('END')])
     188        return (generator_sql, trigger_sql)
     189   
     190    def sequence_reset_sql(self, style, model_list):
     191        from django.db import models
     192        output = []
     193        sql = ['%s %s %s' % (style.SQL_KEYWORD('CREATE OR ALTER PROCEDURE'),
     194                             style.SQL_TABLE('"GENERATOR_RESET"'),
     195                             style.SQL_KEYWORD('AS'))]
     196        sql.append('%s %s' % (style.SQL_KEYWORD('DECLARE VARIABLE'), style.SQL_COLTYPE('start_val integer;')))
     197        sql.append('%s %s' % (style.SQL_KEYWORD('DECLARE VARIABLE'), style.SQL_COLTYPE('gen_val integer;')))
     198        sql.append('\t%s' % style.SQL_KEYWORD('BEGIN'))
     199        sql.append('\t\t%s %s %s %s %s %s;' % (style.SQL_KEYWORD('SELECT MAX'), style.SQL_FIELD('(%(col)s)'),
     200                                           style.SQL_KEYWORD('FROM'), style.SQL_TABLE('%(table)s'),
     201                                           style.SQL_KEYWORD('INTO'), style.SQL_COLTYPE(':start_val')))
     202        sql.append('\t\t%s (%s %s) %s' % (style.SQL_KEYWORD('IF'), style.SQL_COLTYPE('start_val'),
     203                                    style.SQL_KEYWORD('IS NULL'), style.SQL_KEYWORD('THEN')))
     204        sql.append('\t\t\t%s = %s(%s, 1 - %s(%s, 0));' %\
     205            (style.SQL_COLTYPE('gen_val'), style.SQL_KEYWORD('GEN_ID'), style.SQL_TABLE('%(gen)s'),
     206             style.SQL_KEYWORD('GEN_ID'), style.SQL_TABLE('%(gen)s')))
     207        sql.append('\t\t%s' % style.SQL_KEYWORD('ELSE'))
     208        sql.append('\t\t\t%s = %s(%s, %s - %s(%s, 0));' %\
     209            (style.SQL_COLTYPE('gen_val'), style.SQL_KEYWORD('GEN_ID'),
     210             style.SQL_TABLE('%(gen)s'), style.SQL_COLTYPE('start_val'), style.SQL_KEYWORD('GEN_ID'),
     211             style.SQL_TABLE('%(gen)s')))
     212        sql.append('\t\t%s;' % style.SQL_KEYWORD('EXIT'))
     213        sql.append('%s;' % style.SQL_KEYWORD('END'))
     214        sql ="\n".join(sql)
     215        for model in model_list:
     216            for f in model._meta.fields:
     217                if isinstance(f, models.AutoField):
     218                    generator_name = self.get_generator_name(model._meta.db_table)
     219                    column_name = self.quote_name(f.db_column or f.name)
     220                    table_name = self.quote_name(model._meta.db_table)
     221                    output.append(sql % {'col' : column_name, 'table' : table_name, 'gen' : generator_name})
     222                    output.append('%s %s;' % (style.SQL_KEYWORD('EXECUTE PROCEDURE'),
     223                                              style.SQL_TABLE('"GENERATOR_RESET"')))
     224                    break # Only one AutoField is allowed per model, so don't bother continuing.
     225            for f in model._meta.many_to_many:
     226                generator_name = self.get_generator_name(f.m2m_db_table())
     227                table_name = self.quote_name(f.m2m_db_table())
     228                column_name = '"id"'
     229                output.append(sql % {'col' : column_name, 'table' : table_name, 'gen' : generator_name})
     230                output.append('%s %s;' % (style.SQL_KEYWORD('EXECUTE PROCEDURE'),
     231                                          style.SQL_TABLE('"GENERATOR_RESET"')))
     232        return output
     233   
     234    def sql_flush(self, style, tables, sequences):
     235        if tables:
     236            sql = ['%s %s %s;' % \
     237                    (style.SQL_KEYWORD('DELETE'),
     238                     style.SQL_KEYWORD('FROM'),
     239                     style.SQL_TABLE(self.quote_name(table))
     240                     ) for table in tables]
     241            for generator_info in sequences:
     242                table_name = generator_info['table']
     243                query = "%s %s %s 0;" % (style.SQL_KEYWORD('SET GENERATOR'),
     244                    self.get_generator_name(table_name), style.SQL_KEYWORD('TO'))
     245                sql.append(query)
     246            return sql
     247        else:
     248            return []
     249           
     250    ############################################################################
     251    # Custom classes
     252    def field_class(this, DefaultField):
     253        from django.db import connection
     254        from django.db.models.fields import prep_for_like_query
     255        class FirebirdField(DefaultField):
     256            def get_db_prep_lookup(self, lookup_type, value):       
     257                "Returns field's value prepared for database lookup."
     258                if lookup_type in ('exact', 'regex', 'iregex', 'gt', 'gte', 'lt',
     259                    'lte', 'month', 'day', 'search', 'icontains',
     260                    'startswith', 'istartswith'):
     261                    return [value]
     262                elif lookup_type in ('range', 'in'):
     263                    return value
     264                elif lookup_type in ('contains',):
     265                    return ["%%%s%%" % prep_for_like_query(value)]
     266                elif lookup_type == 'iexact':
     267                    return [prep_for_like_query(value)]
     268                elif lookup_type in ('endswith', 'iendswith'):
     269                    return ["%%%s" % prep_for_like_query(value)]
     270                elif lookup_type == 'isnull':
     271                    return []
     272                elif lookup_type == 'year':
     273                    try:
     274                        value = int(value)
     275                    except ValueError:
     276                        raise ValueError("The __year lookup type requires an integer argument")
     277                    return ['%s-01-01 00:00:00' % value, '%s-12-31 23:59:59.999999' % value]
     278                raise TypeError("Field has invalid lookup: %s" % lookup_type)
     279        return FirebirdField
     280
     281    def query_set_class(this, DefaultQuerySet):
     282        from django.db import connection
     283        from django.db.models.query import EmptyResultSet, GET_ITERATOR_CHUNK_SIZE
     284        class FirebirdQuerySet(DefaultQuerySet):
     285            def _get_sql_clause(self):
     286                from django.db.models.query import SortedDict, handle_legacy_orderlist, orderfield2column, fill_table_cache
     287                qn = this.quote_name
     288                opts = self.model._meta
     289
     290                # Construct the fundamental parts of the query: SELECT X FROM Y WHERE Z.
     291                select = ["%s.%s" % (qn(opts.db_table), qn(f.column)) for f in opts.fields]
     292                tables = [qn(t) for t in self._tables]
     293                joins = SortedDict()
     294                where = self._where[:]
     295                params = self._params[:]
     296
     297                # Convert self._filters into SQL.
     298                joins2, where2, params2 = self._filters.get_sql(opts)
     299                joins.update(joins2)
     300                where.extend(where2)
     301                params.extend(params2)
     302
     303                # Add additional tables and WHERE clauses based on select_related.
     304                if self._select_related:
     305                    fill_table_cache(opts, select, tables, where,
     306                                     old_prefix=opts.db_table,
     307                                     cache_tables_seen=[opts.db_table],
     308                                     max_depth=self._max_related_depth)
     309               
     310                # Add any additional SELECTs.
     311                if self._select:
     312                    select.extend([('(%s AS %s') % (qn(s[1]), qn(s[0])) for s in self._select.items()])
     313
     314                # Start composing the body of the SQL statement.
     315                sql = [" FROM", qn(opts.db_table)]
     316
     317                # Compose the join dictionary into SQL describing the joins.
     318                if joins:
     319                    sql.append(" ".join(["%s %s %s ON %s" % (join_type, table, alias, condition)
     320                                    for (alias, (table, join_type, condition)) in joins.items()]))
     321
     322                # Compose the tables clause into SQL.
     323                if tables:
     324                    sql.append(", " + ", ".join(tables))
     325
     326                # Compose the where clause into SQL.
     327                if where:
     328                    sql.append(where and "WHERE " + " AND ".join(where))
     329
     330                # ORDER BY clause
     331                order_by = []
     332                if self._order_by is not None:
     333                    ordering_to_use = self._order_by
     334                else:
     335                    ordering_to_use = opts.ordering
     336                for f in handle_legacy_orderlist(ordering_to_use):
     337                    if f == '?': # Special case.
     338                        order_by.append(connection.ops.random_function_sql())
     339                    else:
     340                        if f.startswith('-'):
     341                            col_name = f[1:]
     342                            order = "DESC"
     343                        else:
     344                            col_name = f
     345                            order = "ASC"
     346                        if "." in col_name:
     347                            table_prefix, col_name = col_name.split('.', 1)
     348                            table_prefix = qn(table_prefix) + '.'
     349                        else:
     350                            # Use the database table as a column prefix if it wasn't given,
     351                            # and if the requested column isn't a custom SELECT.
     352                            if "." not in col_name and col_name not in (self._select or ()):
     353                                table_prefix = qn(opts.db_table) + '.'
     354                            else:
     355                                table_prefix = ''
     356                        order_by.append('%s%s %s' % \
     357                            (table_prefix, qn(orderfield2column(col_name, opts)), order))
     358                if order_by:
     359                    sql.append("ORDER BY " + ", ".join(order_by))
     360
     361                return select, " ".join(sql), params
     362           
     363            def iterator(self):
     364                "Performs the SELECT database lookup of this QuerySet."
     365                from django.db.models.query import get_cached_row
     366                try:
     367                    select, sql, params = self._get_sql_clause()
     368                except EmptyResultSet:
     369                    raise StopIteration
     370                   
     371                # self._select is a dictionary, and dictionaries' key order is
     372                # undefined, so we convert it to a list of tuples.
     373                extra_select = self._select.items()
     374               
     375                cursor = connection.cursor()
     376                limit_offset_before = ""
     377                if self._limit is not None:
     378                    limit_offset_before += "FIRST %s " % self._limit
     379                    if self._offset:
     380                        limit_offset_before += "SKIP %s " % self._offset
     381                else:
     382                    assert self._offset is None, "'offset' is not allowed without 'limit'"
     383                cursor.execute("SELECT " + limit_offset_before + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params)
     384                fill_cache = self._select_related
     385                fields = self.model._meta.fields
     386                index_end = len(fields)
     387                while 1:
     388                    rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE)
     389                    if not rows:
     390                        raise StopIteration
     391                    for row in rows:
     392                        row = self.resolve_columns(row, fields)
     393                        if fill_cache:
     394                            obj, index_end = get_cached_row(klass=self.model, row=row,
     395                                                            index_start=0, max_depth=self._max_related_depth)
     396                        else:
     397                            obj = self.model(*row[:index_end])
     398                        for i, k in enumerate(extra_select):
     399                            setattr(obj, k[0], row[index_end+i])
     400                        yield obj
     401           
     402            def resolve_columns(self, row, fields=()):
     403                from django.db.models.fields import DateField, DateTimeField, \
     404                    TimeField, BooleanField, NullBooleanField, DecimalField, Field
     405                values = []
     406                for value, field in map(None, row, fields):
     407                    # Convert 1 or 0 to True or False
     408                    if value in (1, 0) and isinstance(field, (BooleanField, NullBooleanField)):
     409                        value = bool(value)
     410
     411                    values.append(value)
     412                return values
     413               
     414            def extra(self, select=None, where=None, params=None, tables=None):
     415                assert self._limit is None and self._offset is None, \
     416                        "Cannot change a query once a slice has been taken"
     417                clone = self._clone()
     418                qn = this.quote_name
     419                if select: clone._select.update(select)
     420                if where:
     421                    qn_where = []
     422                    for where_item in where:
     423                        try:
     424                            table, col_exact = where_item.split(".")
     425                            col, value = col_exact.split("=")
     426                            where_item = "%s.%s = %s" % (qn(table.strip()),
     427                                qn(col.strip()), value.strip())
     428                        except:
     429                            try:
     430                                table, value = where_item.split("=")
     431                                where_item = "%s = %s" % (qn(table.strip()), qn(value.strip()))
     432                            except:
     433                                raise TypeError, "Can't understand extra WHERE clause: %s" % where
     434                        qn_where.append(where_item)
     435                    clone._where.extend(qn_where)
     436                if params: clone._params.extend(params)
     437                if tables: clone._tables.extend(tables)
     438                return clone
     439               
     440        return FirebirdQuerySet
     441
     442################################################################################
     443# Cursor wrapper       
     444class FirebirdCursorWrapper(object):
     445    """
     446    Django uses "format" ('%s') style placeholders, but firebird uses "qmark" ('?') style.
     447    This fixes it -- but note that if you want to use a literal "%s" in a query,
     448    you'll need to use "%%s".
     449   
     450    We also do all automatic type conversions here.
     451    """
     452    import kinterbasdb.typeconv_datetime_stdlib as tc_dt
     453    import kinterbasdb.typeconv_fixed_decimal as tc_fd
     454    import kinterbasdb.typeconv_text_unicode as tc_tu
     455    import django.utils.encoding as dj_ue
     456
     457    def ascii_conv_in(self, text):
     458        if text is not None: 
     459            return self.dj_ue.smart_str(text, 'ascii')
     460   
     461    def ascii_conv_out(self, text):
     462        if text is not None:
     463            return self.dj_ue.smart_unicode(text)   
     464
     465    def blob_conv_in(self, text):
     466        return self.tc_tu.unicode_conv_in((self.dj_ue.smart_unicode(text), self.FB_CHARSET_CODE))
     467
     468    def blob_conv_out(self, text):
     469        return self.tc_tu.unicode_conv_out((text, self.FB_CHARSET_CODE))   
     470
     471    def fixed_conv_in(self, (val, scale)):
     472        if val is not None:
     473            if isinstance(val, basestring):
     474                val = decimal.Decimal(val)
     475            return self.tc_fd.fixed_conv_in_precise((val, scale))
     476
     477    def timestamp_conv_in(self, timestamp):
     478        if isinstance(timestamp, basestring):
     479            #Replaces 6 digits microseconds to 4 digits allowed in Firebird
     480            timestamp = timestamp[:24]
     481        return self.tc_dt.timestamp_conv_in(timestamp)
     482
     483    def time_conv_in(self, value):
     484        import datetime
     485        if isinstance(value, datetime.datetime):
     486            value = datetime.time(value.hour, value.minute, value.second, value.microsecond)       
     487
     488    def unicode_conv_in(self, text):
     489        if text[0] is not None:
     490            return self.tc_tu.unicode_conv_in((self.dj_ue.smart_unicode(text[0]), self.FB_CHARSET_CODE))
     491
     492    def __init__(self, cursor, connection):   
     493        self.cursor = cursor
     494        self._connection = connection
     495        self._statement = None #prepared statement
     496        self.FB_CHARSET_CODE = 3 #UNICODE_FSS
     497        if connection.charset == 'UTF8':
     498            self.FB_CHARSET_CODE = 4 # UTF-8 with Firebird 2.0+
     499        self.cursor.set_type_trans_in({
     500            'DATE':             self.tc_dt.date_conv_in,
     501            'TIME':             self.time_conv_in,
     502            'TIMESTAMP':        self.timestamp_conv_in,
     503            'FIXED':            self.fixed_conv_in,
     504            'TEXT':             self.ascii_conv_in,
     505            'TEXT_UNICODE':     self.unicode_conv_in,
     506            'BLOB':             self.blob_conv_in
     507        })
     508        self.cursor.set_type_trans_out({
     509            'DATE':             self.tc_dt.date_conv_out,
     510            'TIME':             self.tc_dt.time_conv_out,
     511            'TIMESTAMP':        self.tc_dt.timestamp_conv_out,
     512            'FIXED':            self.tc_fd.fixed_conv_out_precise,
     513            'TEXT':             self.ascii_conv_out,
     514            'TEXT_UNICODE':     self.tc_tu.unicode_conv_out,
     515            'BLOB':             self.blob_conv_out
     516        })
     517   
     518    def execute_immediate(self, query, params=()):
     519        query = query % tuple(params)
     520        self._connection.execute_immediate(query)
     521   
     522    def prepare(self, query):
     523        """
     524        Returns prepared statement for use with execute_prepared
     525        http://kinterbasdb.sourceforge.net/dist_docs/usage.html#adv_prepared_statements
     526        """
     527        query.replace("%s", "?")
     528        return self.cursor.prep(query)
     529   
     530    def execute_prepared(self, statement, params):
     531        return self.cursor.execute(statement, params)
     532   
     533    def execute_straight(self, query, params=()):
     534        """
     535        Kinterbasdb-style execute with '?' instead of '%s'
     536        """
     537        return self.cursor.execute(query, params)
     538   
     539    def execute(self, query, params=()):
     540        cquery = self.convert_query(query, len(params))
     541        if self._get_query() != cquery:
     542            try:
     543                self._statement = self.cursor.prep(cquery)
     544            except Database.ProgrammingError, e:
     545                output = ["Prepare query error."]
     546                output.extend(str(e).split("'")[1].split('\\n'))
     547                output.append("Query:")
     548                output.append(cquery)
     549                raise Database.ProgrammingError, "\n".join(output)
     550        try:
     551            return self.cursor.execute(self._statement, params)
     552        except Database.ProgrammingError, e:
     553            err_no = int(str(e).split()[0].strip(',()'))
     554            output = ["Execute query error. FB error No. %i" % err_no]
     555            output.extend(str(e).split("'")[1].split('\\n'))
     556            output.append("Query:")
     557            output.append(cquery)
     558            output.append("Parameters:")
     559            output.append(str(params))
     560            raise Database.ProgrammingError, "\n".join(output)
     561   
     562    def executemany(self, query, param_list):
     563        try:
     564            cquery = self.convert_query(query, len(param_list[0]))
     565        except IndexError:
     566            return None
     567        if self._get_query() != cquery:
     568            self._statement = self.cursor.prep(cquery)
     569        return self.cursor.executemany(self._statement, param_list)
     570
     571    def convert_query(self, query, num_params):
     572        try:
     573            return query % tuple("?" * num_params)
     574        except TypeError, e:
     575            print query, num_params
     576            raise TypeError, e
     577   
     578    def _get_query(self):
     579        if self._statement:
     580            return self._statement.sql
     581   
     582    def __getattr__(self, attr):
     583        if attr in self.__dict__:
     584            return self.__dict__[attr]
     585        else:
     586            return getattr(self.cursor, attr)
     587
     588################################################################################
     589# DatabaseWrapper(db.connection) 
     590class DatabaseWrapper(BaseDatabaseWrapper):
     591    features = DatabaseFeatures()
     592    ops = DatabaseOperations()
     593    operators = {
     594        'exact': '= %s',
     595        'iexact': '= UPPER(%s)',
     596        'contains': "LIKE %s ESCAPE'\\'",
     597        'icontains': 'CONTAINING %s', #case is ignored
     598        'gt': '> %s',
     599        'gte': '>= %s',
     600        'lt': '< %s',
     601        'lte': '<= %s',
     602        'startswith': 'STARTING WITH %s', #looks to be faster then LIKE
     603        'endswith': "LIKE %s ESCAPE'\\'",
     604        'istartswith': 'STARTING WITH UPPER(%s)',
     605        'iendswith': "LIKE UPPER(%s) ESCAPE'\\'"
     606    }
     607   
     608    def __init__(self, **kwargs):
     609        from django.conf import settings
     610        super(DatabaseWrapper, self).__init__(**kwargs)
     611        self. _current_cursor = None
     612        self._raw_cursor = None
     613        self.charset = 'UNICODE_FSS'
     614        self.FB_MAX_VARCHAR = 10921 #32765 MAX /3
     615        self.BYTES_PER_DEFAULT_CHAR = 3
     616        if hasattr(settings, 'FIREBIRD_CHARSET'):
     617            if settings.FIREBIRD_CHARSET == 'UTF8':
     618                self.charset = 'UTF8'
     619                self.FB_MAX_VARCHAR = 8191 #32765 MAX /4
     620                self.BYTES_PER_DEFAULT_CHAR = 4
     621       
     622    def _connect(self, settings):
     623        if settings.DATABASE_NAME == '':
     624            from django.core.exceptions import ImproperlyConfigured
     625            raise ImproperlyConfigured, "You need to specify DATABASE_NAME in your Django settings file."
     626        kwargs = {'charset' : self.charset }
     627        if settings.DATABASE_HOST:
     628            kwargs['dsn'] = "%s:%s" % (settings.DATABASE_HOST, settings.DATABASE_NAME)
     629        else:
     630            kwargs['dsn'] = "localhost:%s" % settings.DATABASE_NAME
     631        if settings.DATABASE_USER:
     632            kwargs['user'] = settings.DATABASE_USER
     633        if settings.DATABASE_PASSWORD:
     634            kwargs['password'] = settings.DATABASE_PASSWORD
     635        self.connection = Database.connect(**kwargs)
     636        assert self.connection.charset == self.charset
     637       
     638    def cursor(self):
     639        from django.conf import settings
     640        cursor = self._cursor(settings)
     641        if settings.DEBUG:
     642            self._debug_cursor = self.make_debug_cursor(cursor)
     643            return self._debug_cursor
     644        return cursor
     645   
     646    def _cursor(self, settings):
     647        if self.connection is None:
     648            self._connect(settings)
     649        cursor = self.connection.cursor()
     650        self._raw_cursor = cursor
     651        cursor = FirebirdCursorWrapper(cursor, self)
     652        self._current_cursor = cursor
     653        return cursor
     654
     655    def __getattr__(self, attr):
     656        if attr in self.__dict__:
     657            return self.__dict__[attr]
     658        else:
     659            return getattr(self.connection, attr)
     660
  • 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
     4qn = quote_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()) 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_straight("""
     19        SELECT DISTINCT R.RDB$FIELD_NAME AS FNAME,
     20                  F.RDB$FIELD_TYPE AS FTYPE,
     21                  F.RDB$CHARACTER_LENGTH AS FCHARLENGTH,
     22                  F.RDB$FIELD_LENGTH AS FLENGTH,
     23                  F.RDB$FIELD_PRECISION AS FPRECISION,
     24                  F.RDB$FIELD_SCALE AS FSCALE,
     25                  R.RDB$NULL_FLAG AS NULL_FLAG,
     26                  R.RDB$FIELD_POSITION
     27        FROM RDB$RELATION_FIELDS R
     28             JOIN RDB$FIELDS F ON R.RDB$FIELD_SOURCE = F.RDB$FIELD_NAME
     29        WHERE F.RDB$SYSTEM_FLAG=0 and R.RDB$RELATION_NAME STARTING WITH ?
     30        ORDER BY R.RDB$FIELD_POSITION
     31    """, (qn(table_name).strip('"'),))
     32    return [(row[0].rstrip(), row[1], row[2] or 0, row[3], row[4], row[5], row[6] and True or False) for row in cursor.fetchall()]
     33
     34
     35def get_relations(cursor, table_name):
     36    """
     37    Returns a dictionary of {field_index: (field_index_other_table, other_table)}
     38    representing all relationships to the given table. Indexes are 0-based.
     39    """
     40    cursor.execute_straight("""
     41        SELECT seg.rdb$field_name, seg_ref.rdb$field_name, idx_ref.rdb$relation_name
     42        FROM rdb$indices idx
     43        INNER JOIN rdb$index_segments seg
     44            ON seg.rdb$index_name = idx.rdb$index_name
     45        INNER JOIN rdb$indices idx_ref
     46            ON idx_ref.rdb$index_name = idx.rdb$foreign_key
     47        INNER JOIN rdb$index_segments seg_ref
     48            ON seg_ref.rdb$index_name = idx_ref.rdb$index_name
     49        WHERE idx.rdb$relation_name STARTING WITH ?
     50            AND idx.rdb$foreign_key IS NOT NULL""", [qn(table_name).strip('"')])
     51
     52    relations = {}
     53    for row in cursor.fetchall():
     54        relations[row[0].rstrip()] = (row[1].strip(), row[2].strip())
     55    return relations
     56
     57
     58def get_indexes(cursor, table_name):
     59    """
     60    Returns a dictionary of fieldname -> infodict for the given table,
     61    where each infodict is in the format:
     62        {'primary_key': boolean representing whether it's the primary key,
     63         'unique': boolean representing whether it's a unique index}
     64    """
     65
     66    # This query retrieves each field name and index type on the given table.
     67    cursor.execute("""
     68        SELECT seg.RDB$FIELD_NAME, const.RDB$CONSTRAINT_TYPE
     69        FROM RDB$RELATION_CONSTRAINTS const
     70        LEFT JOIN RDB$INDEX_SEGMENTS seg
     71            ON seg.RDB$INDEX_NAME = const.RDB$INDEX_NAME
     72        WHERE const.RDB$RELATION_NAME STARTING WITH ?
     73        ORDER BY seg.RDB$FIELD_POSITION)""",
     74        [qn(table_name).strip('"')])
     75    indexes = {}
     76    for row in cursor.fetchall():
     77        indexes[row[0]] = {'primary_key': row[1].startswith('PRIMARY'),
     78                           'unique': row[1].startswith('UNIQUE')}   
     79    return indexes
     80
     81# Maps type codes to Django Field types.
     82DATA_TYPES_REVERSE = {
     83   261: 'LargeTextField',
     84    14:  'CharField',
     85    40:  'CharField',
     86    11:  'FloatField',
     87    27:  'FloatField',
     88    10:  'FloatField',
     89    16:  'IntegerField',
     90     8:   'IntegerField',
     91     9:   'IntegerField',
     92     7:   'SmallIntegerField',
     93    12:  'DateField',
     94    13:  'TimeField',
     95    35:  'DateTimeField',
     96    37: 'CharField'
     97}
  • 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
     6
     7from kinterbasdb import connect, create_database
     8from django.core.management import call_command
     9from django.conf import settings
     10from django.db import connection
     11import sys, os, os.path, codecs
     12try:
     13    set
     14except NameError:
     15    from sets import Set as set   # Python 2.3 fallback
     16
     17# Setting TEST_MODE to 1 enables cascading deletes (for table flush) which are dangerous
     18# Setting TEST_MODE to 2 disables strict FK constraints (for forward/post references)
     19# Setting TEST_MODE to 0 is the most secure option (it even fails some official Django tests  because of it)
     20TEST_MODE = 0
     21if 'FB_DJANGO_TEST_MODE' in os.environ:
     22    TEST_MODE = int(os.environ['FB_DJANGO_TEST_MODE'])
     23
     24DATA_TYPES = {
     25    'AutoField':                     '"AutoField"',
     26    'BooleanField':                  '"BooleanField"',
     27    'CharField':                     'varchar(%(max_length)s)',
     28    'CommaSeparatedIntegerField':    'varchar(%(max_length)s) CHARACTER SET ASCII',
     29    'DateField':                     '"DateField"',
     30    'DateTimeField':                 '"DateTimeField"',
     31    'DecimalField':                  'numeric(%(max_digits)s, %(decimal_places)s)',
     32    'DefaultCharField':              '"CharField"',
     33    'FileField':                     'varchar(%(max_length)s)',
     34    'FilePathField':                 'varchar(%(max_length)s)',
     35    'FloatField':                    '"FloatField"',
     36    'ImageField':                    '"varchar(%(max_length)s)"',
     37    'IntegerField':                  '"IntegerField"',
     38    'IPAddressField':                'varchar(15) CHARACTER SET ASCII',
     39    'NullBooleanField':              '"NullBooleanField"',
     40    'OneToOneField':                 '"OneToOneField"',
     41    'PhoneNumberField':              '"PhoneNumberField"',
     42    'PositiveIntegerField':          '"PositiveIntegerField"',
     43    'PositiveSmallIntegerField':     '"PositiveSmallIntegerField"',
     44    'SlugField':                     'varchar(%(max_length)s)',
     45    'SmallIntegerField':             '"SmallIntegerField"',
     46    'LargeTextField':                '"LargeTextField"',
     47    'TextField':                     '"TextField"',
     48    'TimeField':                     '"TimeField"',
     49    'URLField':                      'varchar(%(max_length)s) CHARACTER SET ASCII',
     50    'USStateField':                  '"USStateField"'
     51}
     52     
     53PYTHON_TO_FB_ENCODING_MAP = {
     54    'ascii':        'ASCII',
     55    'utf_8':        connection.charset,
     56    'shift_jis':    'SJIS_0208',
     57    'euc_jp':       'EUCJ_0208',
     58    'cp737':        'DOS737',
     59    'cp437':        'DOS437',
     60    'cp850':        'DOS850',
     61    'cp865':        'DOS865',
     62    'cp860':        'DOS860',
     63    'cp863':        'DOS863',
     64    'cp775':        'DOS775',
     65    'cp862':        'DOS862',
     66    'cp864':        'DOS864',
     67    'iso8859_1':    'ISO8859_1',
     68    'iso8859_2':    'ISO8859_2',
     69    'iso8859_3':    'ISO8859_3',
     70    'iso8859_4':    'ISO8859_4',
     71    'iso8859_5':    'ISO8859_5',
     72    'iso8859_6':    'ISO8859_6',
     73    'iso8859_7':    'ISO8859_7',
     74    'iso8859_8':    'ISO8859_8',
     75    'iso8859_9':    'ISO8859_9',
     76    'iso8859_13':   'ISO8859_13',
     77    'euc_kr':       'KSC_5601',
     78    'cp852':        'DOS852',
     79    'cp857':        'DOS857',
     80    'cp861':        'DOS861',
     81    'cp866':        'DOS866',
     82    'cp869':        'DOS869',
     83    'cp1250':       'WIN1250',
     84    'cp1251':       'WIN1251',
     85    'cp1252':       'WIN1252',
     86    'cp1253':       'WIN1253',
     87    'cp1254':       'WIN1254',
     88    'big5':         'BIG_5',
     89    'gb2312':       'GB_2312',
     90    'cp1255':       'WIN1255',
     91    'cp1256':       'WIN1256',
     92    'cp1257':       'WIN1257',
     93    'koi8_r':       'KOI8-R',
     94    'koi8_u':       'KOI8-U',
     95    'cp1258':       'WIN1258'
     96  }
     97
     98def get_data_size(data_type, max_length = 100):
     99    char_bytes = connection.BYTES_PER_DEFAULT_CHAR
     100    size_map = {
     101        'AutoField':                     8,
     102        'BooleanField':                  4,
     103        'CharField':                     char_bytes*max_length,
     104        'CommaSeparatedIntegerField':    max_length,
     105        'DateField':                     16,
     106        'DateTimeField':                 16,
     107        'DecimalField':                  16,
     108        'FileField':                     char_bytes*max_length,
     109        'FilePathField':                 'varchar(%(max_length)s)',
     110        'FloatField':                    16,
     111        'ImageField':                    char_bytes*max_length,
     112        'IntegerField':                  8,
     113        'IPAddressField':                15,
     114        'NullBooleanField':              4,
     115        'OneToOneField':                 8,
     116        'PhoneNumberField':              20,
     117        'PositiveIntegerField':          8,
     118        'PositiveSmallIntegerField':     4,
     119        'SlugField':                     char_bytes*max_length,
     120        'SmallIntegerField':             4,
     121        'TextBlob':                      8,
     122        'TextField':                     32767,
     123        'TimeField':                     16,
     124        'URLField':                      max_length,
     125        'USStateField':                  char_bytes*2
     126    }
     127    return size_map[data_type]
     128
     129DEFAULT_MAX_LENGTH = 100
     130def sql_model_create(model, style, known_models=set()):
     131    """
     132    Returns the SQL required to create a single model, as a tuple of:
     133        (list_of_sql, pending_references_dict)
     134    """
     135    from django.db import connection, models
     136
     137    opts = model._meta
     138    final_output = []
     139    table_output = []
     140    pending_references = {}
     141    qn = connection.ops.quote_name
     142   
     143    # Create domains
     144    domains = [ ('AutoField', 'integer'),
     145                ('BooleanField', 'smallint CHECK (VALUE IN (0,1))'),
     146                ('DateField', 'date'),
     147                ('CharField', 'varchar(%i)' % DEFAULT_MAX_LENGTH),
     148                ('DateTimeField', 'timestamp'),
     149                ('FloatField', 'double precision'),
     150                ('IntegerField', 'integer'),
     151                ('IPAddressField', 'varchar(15) CHARACTER SET ASCII'),
     152                ('NullBooleanField', 'smallint CHECK ((VALUE IN (0,1)) OR (VALUE IS NULL))'),
     153                ('OneToOneField', 'integer'),
     154                ('PhoneNumberField', 'varchar(20) CHARACTER SET ASCII'),
     155                ('PositiveIntegerField', 'integer CHECK ((VALUE >= 0) OR (VALUE IS NULL))'),
     156                ('PositiveSmallIntegerField', 'smallint CHECK ((VALUE >= 0) OR (VALUE IS NULL))'),
     157                ('SmallIntegerField', 'smallint'),
     158                ('TextField', 'varchar(%s)' % connection.FB_MAX_VARCHAR),
     159                ('LargeTextField', 'blob sub_type text'),
     160                ('TimeField', 'time'),
     161                ('USStateField', 'varchar(2) CHARACTER SET ASCII') ]
     162   
     163    connected = True
     164    try:
     165        cursor = connection.cursor()
     166    except:
     167        connected = False
     168    if connected:
     169        cursor.execute("SELECT RDB$FIELD_NAME FROM RDB$FIELDS")
     170        existing_domains = set([row[0].strip() for row in cursor.fetchall() if not row[0].startswith('RDB$')])
     171        domains = map(lambda domain: '%s "%s" AS %s;' % ('CREATE DOMAIN', domain[0], domain[1]),
     172            filter(lambda x: x[0] not in existing_domains, domains))
     173        final_output.extend(domains)
     174   
     175    # Check that row size is less than 64k and adjust TextFields if needed
     176    row_size = 0
     177    columns = [(f.db_type().strip('"'), f.get_internal_type(), f) for f in opts.fields]
     178    columns_simple = [col[0] for col in columns]
     179    text_field_type = '"TextField"'
     180    max_alowed_bytes = 32765
     181    if 'TextField' in columns_simple:
     182        max_length = 100
     183        num_text_fields = 0
     184        for column in columns:
     185            num_text_fields += (column[0] == 'TextField')
     186            if column[0].startswith('varchar'):
     187                max_length = int(column[0].split('(')[1].split(')')[0])
     188            if column[1] in DATA_TYPES:
     189                row_size += get_data_size(column[1], max_length)
     190        if row_size > 65536:
     191            max_alowed_bytes = int( (max_alowed_bytes/num_text_fields) - (row_size-65536) )
     192            n = max_alowed_bytes / connection.BYTES_PER_DEFAULT_CHAR
     193            if n > 512:
     194                text_field_type = 'varchar(%s)' % n
     195                FB_TEXTFIELD_ALTERED = True   
     196                print
     197                print "WARNING: Maximum number of characters in TextFields has changed to %s." % n
     198                print "         TextField columns with custom charsets will have %s chars available" % max_alowed_bytes
     199                print "         The change affects %s table only." % opts.db_table
     200                print "         TextFields in other tables will have %s characters maximum" % connection.FB_MAX_VARCHAR
     201                print "         or 32765 characters with custom (non-UTF) encoding."
     202                print "         If you need more space in those fields try LargeTextFields instead."
     203                print
     204            else:
     205                # Swich to blobs if size is too small (<1024)   
     206                text_field_type = '"LargeTextField"'   
     207   
     208    # Create tables
     209    for f in opts.fields:
     210        col_type = f.db_type()
     211        if col_type.strip('"') == 'TextField':
     212            col_type = text_field_type
     213        fb_version = "%s.%s" % (connection.ops.firebird_version[0], connection.ops.firebird_version[1])
     214        page_size = connection.ops.page_size
     215        #look at: http://www.volny.cz/iprenosil/interbase/ip_ib_indexcalculator.htm
     216        if connection.ops.index_limit < 1000:
     217            strip2ascii = False
     218            custom_charset = False
     219            if col_type.startswith('varchar'):
     220                if (f.unique or f.primary_key or f.db_index):
     221                    length = f.max_length
     222                    if f.encoding:
     223                        if not f.encoding.upper().startswith('UTF'):
     224                            custom_charset = True
     225                    if not custom_charset:
     226                        try:
     227                            length = f.max_length * connection.BYTES_PER_DEFAULT_CHAR
     228                        except TypeError:
     229                            length = 100*connection.BYTES_PER_DEFAULT_CHAR #Default for CharField
     230                    if length >= connection.ops.index_limit:   
     231                        strip2ascii = True
     232                if len(opts.unique_together) > 0:
     233                    if f.column in opts.unique_together[0]:
     234                        num_unique_char_fields = len([ fld for fld in opts.unique_together[0] if opts.get_field(fld).db_type().startswith('varchar') ])
     235                        num_unique_fileds = len(opts.unique_together[0])
     236                        num_unique_nonchar_fileds = num_unique_fileds - num_unique_char_fields
     237                        limit = connection.ops.index_limit
     238                        limit -= ((num_unique_fileds - 1)*64)
     239                        limit -= 8*num_unique_nonchar_fileds
     240                        max_length = limit/num_unique_char_fields
     241                        ascii_length = int(f.max_length)
     242                        old_length = ascii_length*connection.BYTES_PER_DEFAULT_CHAR
     243                         
     244                        if (old_length >= max_length) and (ascii_length < max_length):
     245                            strip2ascii = True
     246                        elif old_length > max_length:
     247                            strip2ascii = False #We change it here
     248                            col_type = "varchar(%i) CHARACTER SET ASCII" % max_length
     249                            msg =  "WARNING: Character set of the '%s' field\n"
     250                            msg += "         (table %s)\n"
     251                            msg += "         has changed to ASCII"
     252                            msg += " to fit %s-byte limit in FB %s"
     253                            if not page_size:
     254                                print  msg % (f.column, opts.db_table, connection.ops.index_limit, fb_version)
     255                            else:
     256                                msg += " with page size %s"
     257                                print  msg % (f.column, opts.db_table, connection.ops.index_limit, fb_version, page_size)
     258                            print "         The maximum length of '%s' is now %s instead of %s"\
     259                             % (f.column, max_length, old_length)     
     260            if strip2ascii:
     261                col_type = "%s %s %s" % (col_type, "CHARACTER SET", "ASCII")
     262                msg =  "WARNING: Character set of the '%s' field\n"
     263                msg += "         (table %s)\n"
     264                msg += "         has changed to ASCII"
     265                msg += " to fit %s-byte limit in FB %s"
     266                if not page_size:
     267                    print  msg % (f.column, opts.db_table, connection.ops.index_limit, fb_version)
     268                else:
     269                    msg += " with page size %s"
     270                    print  msg % (f.column, opts.db_table, connection.ops.index_limit, fb_version, page_size)
     271                   
     272        if (col_type.startswith('varchar') or col_type.strip('"') == 'TextField') and f.encoding:
     273            charset = PYTHON_TO_FB_ENCODING_MAP[codecs.lookup(f.encoding).name]
     274            if col_type.strip('"') == 'TextField':
     275                col_type = 'varchar(%i)' % max_alowed_bytes
     276            col_type = "%s %s %s" % (col_type, "CHARACTER SET", charset)
     277       
     278        if col_type is None:
     279            # Skip ManyToManyFields, because they're not represented as
     280            # database columns in this table.
     281            continue
     282        if col_type == 'ComputedField':
     283            # Make the definition (e.g. 'foo COMPUTED BY (oldfoo*2)') for this field.
     284            field_output = [ style.SQL_FIELD(qn(f.column)), style.SQL_KEYWORD('COMPUTED BY'),
     285                             '(%s)' % f.expression ]
     286               
     287        else:   
     288            # Make the definition (e.g. 'foo VARCHAR(30)') for this field.
     289            field_output = [style.SQL_FIELD(qn(f.column)),
     290                style.SQL_COLTYPE(col_type)]
     291            field_output.append(style.SQL_KEYWORD('%s' % (not f.null and 'NOT NULL' or '')))
     292        if f.unique:
     293            field_output.append(style.SQL_KEYWORD('UNIQUE'))
     294        if f.primary_key:
     295            field_output.append(style.SQL_KEYWORD('PRIMARY KEY'))
     296        if f.rel:
     297            # We haven't yet created the table to which this field
     298            # is related, so save it for later.
     299            pr = pending_references.setdefault(f.rel.to, []).append((model, f))
     300        table_output.append(' '.join(field_output))
     301    for field_constraints in opts.unique_together:
     302        table_output.append(style.SQL_KEYWORD('UNIQUE') + ' (%s)' % \
     303            ", ".join([qn(style.SQL_FIELD(opts.get_field(f).column)) for f in field_constraints]))
     304
     305    full_statement = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + style.SQL_TABLE(qn(opts.db_table)) + ' (']
     306    for i, line in enumerate(table_output): # Combine and add commas.
     307        full_statement.append('    %s%s' % (line, i < len(table_output)-1 and ',' or ''))
     308    full_statement.append(');')
     309    final_output.append('\n'.join(full_statement))
     310
     311    if opts.has_auto_field:
     312        # Add any extra SQL needed to support auto-incrementing primary keys.
     313        auto_column = opts.auto_field.db_column or opts.auto_field.name
     314        autoinc_sql = connection.ops.autoinc_sql(style, opts.db_table, auto_column)
     315        if autoinc_sql:
     316            for stmt in autoinc_sql:
     317                final_output.append(stmt)
     318
     319    # Declare exteral functions
     320    if connected:
     321        cursor.execute("SELECT RDB$FUNCTION_NAME FROM RDB$FUNCTIONS")
     322        existing_functions = set([row[0].strip().upper() for row in cursor.fetchall()])
     323        if 'RAND' not in existing_functions:
     324            final_output.append('%s %s\n\t%s %s\n\t%s %s\n\t%s;' % (style.SQL_KEYWORD('DECLARE EXTERNAL FUNCTION'),
     325                style.SQL_TABLE('RAND'), style.SQL_KEYWORD('RETURNS'), style.SQL_COLTYPE('DOUBLE PRECISION'),
     326                style.SQL_KEYWORD('BY VALUE ENTRY_POINT'), style.SQL_FIELD("'IB_UDF_rand'"),
     327                style.SQL_TABLE("MODULE_NAME 'ib_udf'")))
     328   
     329    # Create stored procedures
     330    if hasattr(model, 'procedures'):
     331        for proc in model.procedures:
     332            final_output.append(proc.create_procedure_sql())   
     333   
     334    return final_output, pending_references
     335
     336def many_to_many_sql_for_model(model, style):
     337    from django.db import connection, models
     338    from django.contrib.contenttypes import generic
     339    from django.db.backends.util import truncate_name
     340
     341    opts = model._meta
     342    final_output = []
     343    qn = connection.ops.quote_name
     344    for f in opts.many_to_many:
     345        if not isinstance(f.rel, generic.GenericRel):
     346            table_output = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + \
     347                style.SQL_TABLE(qn(f.m2m_db_table())) + ' (']
     348            table_output.append('    %s %s %s,' %
     349                (style.SQL_FIELD(qn('id')),
     350                style.SQL_COLTYPE(models.AutoField(primary_key=True).db_type()),
     351                style.SQL_KEYWORD('NOT NULL PRIMARY KEY')))
     352
     353            table_output.append('    %s %s %s,' %
     354                (style.SQL_FIELD(qn(f.m2m_column_name())),
     355                style.SQL_COLTYPE(models.ForeignKey(model).db_type()),
     356                style.SQL_KEYWORD('NOT NULL')))
     357            table_output.append('    %s %s %s,' %
     358                (style.SQL_FIELD(qn(f.m2m_reverse_name())),
     359                style.SQL_COLTYPE(models.ForeignKey(f.rel.to).db_type()),
     360                style.SQL_KEYWORD('NOT NULL')))
     361            deferred = [
     362                (f.m2m_db_table(), f.m2m_column_name(), opts.db_table,
     363                    opts.pk.column),
     364                ( f.m2m_db_table(), f.m2m_reverse_name(),
     365                    f.rel.to._meta.db_table, f.rel.to._meta.pk.column)
     366                ]
     367
     368            table_output.append('    %s (%s, %s)' %
     369                (style.SQL_KEYWORD('UNIQUE'),
     370                style.SQL_FIELD(qn(f.m2m_column_name())),
     371                style.SQL_FIELD(qn(f.m2m_reverse_name()))))
     372            table_output.append(');')
     373            final_output.append('\n'.join(table_output))
     374           
     375            autoinc_sql = connection.ops.autoinc_sql(style, f.m2m_db_table(), 'id')
     376            if autoinc_sql:
     377                for stmt in autoinc_sql:
     378                    final_output.append(stmt)
     379           
     380            if TEST_MODE < 2:
     381                for r_table, r_col, table, col in deferred:
     382                    r_name = '%s_refs_%s_%x' % (r_col, col,
     383                            abs(hash((r_table, table))))
     384                    final_output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' %
     385                    (qn(r_table),
     386                    truncate_name(r_name, connection.ops.max_name_length()),
     387                    qn(r_col), qn(table), qn(col),
     388                    'ON DELETE CASCADE ON UPDATE CASCADE'))
     389
     390    return final_output
     391
     392def sql_for_pending_references(model, style, pending_references):
     393    """
     394    Returns any ALTER TABLE statements to add constraints after the fact.
     395    """
     396    from django.db import connection
     397   
     398    qn = connection.ops.quote_name
     399    final_output = []
     400    if TEST_MODE < 2:
     401        opts = model._meta
     402        if model in pending_references:
     403            for rel_class, f in pending_references[model]:
     404                rel_opts = rel_class._meta
     405                r_table = rel_opts.db_table
     406                r_col = f.column
     407                table = opts.db_table
     408                col = opts.get_field(f.rel.field_name).column
     409                r_name = connection.ops.reference_name(r_col, col, r_table, table)
     410                if not f.on_update:
     411                    f.on_update = 'CASCADE'
     412                if not f.on_delete:
     413                    if TEST_MODE > 0:
     414                        f.on_delete = 'CASCADE'
     415                    else:
     416                        f.on_delete = 'SET NULL'
     417                final_output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s) ON UPDATE %s ON DELETE %s;' % \
     418                    (qn(r_table), r_name, qn(r_col), qn(table), qn(col),
     419                    f.on_update, f.on_delete))
     420
     421            del pending_references[model]
     422    return final_output
     423
     424
     425TEST_DATABASE_PREFIX = 'test_'
     426def create_test_db(settings, connection, verbosity, autoclobber):
     427    # KInterbasDB supports dynamic database creation and deletion
     428    # via the module-level function create_database and the method Connection.drop_database.
     429       
     430    if settings.TEST_DATABASE_NAME:
     431        TEST_DATABASE_NAME = settings.TEST_DATABASE_NAME
     432    else:
     433        dbnametuple = os.path.split(settings.DATABASE_NAME)
     434        TEST_DATABASE_NAME = os.path.join(dbnametuple[0], TEST_DATABASE_PREFIX + dbnametuple[1])
     435   
     436    dsn = "localhost:%s" % TEST_DATABASE_NAME
     437    if settings.DATABASE_HOST:
     438        dsn = "%s:%s" % (settings.DATABASE_HOST, TEST_DATABASE_NAME)
     439   
     440    if os.path.isfile(TEST_DATABASE_NAME):
     441        sys.stderr.write("Database %s already exists\n" % TEST_DATABASE_NAME)
     442        if not autoclobber:
     443            confirm = raw_input("Type 'yes' if you would like to try deleting the test database '%s', or 'no' to cancel: " % TEST_DATABASE_NAME)
     444        if autoclobber or confirm == 'yes':
     445            if verbosity >= 1:
     446                print "Destroying old test database..."
     447            old_connection = connect(dsn=dsn, user=settings.DATABASE_USER, password=settings.DATABASE_PASSWORD)
     448            old_connection.drop_database()
     449        else:
     450                print "Tests cancelled."
     451                sys.exit(1)
     452           
     453    if verbosity >= 1:
     454        print "Creating test database..."
     455    try:
     456        charset = 'UNICODE_FSS'
     457        if hasattr(settings, 'FIREBIRD_CHARSET'):
     458            if settings.FIREBIRD_CHARSET == 'UTF8':
     459                charset='UTF8'               
     460        create_database("create database '%s' user '%s' password '%s' default character set %s" % \
     461            (dsn, settings.DATABASE_USER, settings.DATABASE_PASSWORD, charset))
     462    except Exception, e:
     463        sys.stderr.write("Got an error creating the test database: %s\n" % e)
     464        sys.exit(2)
     465
     466    connection.close()
     467    settings.DATABASE_NAME = TEST_DATABASE_NAME
     468
     469    call_command('syncdb', verbosity=verbosity, interactive=False)
     470
     471    if settings.CACHE_BACKEND.startswith('db://'):
     472        cache_name = settings.CACHE_BACKEND[len('db://'):]
     473        call_command('createcachetable', cache_name)
     474
     475    # Get a cursor (even though we don't need one yet). This has
     476    # the side effect of initializing the test database.
     477    cursor = connection.cursor()
     478
     479    return TEST_DATABASE_NAME
     480
     481def destroy_test_db(settings, connection, old_database_name, verbosity):
     482    # KInterbasDB supports dynamic database deletion via the method Connection.drop_database.
     483    if verbosity >= 1:
     484        print "Destroying test database..."
     485    connection.drop_database()
     486   
     487
  • 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
     
    6667
    6768def sql_create(app, style):
    6869    "Returns a list of the CREATE TABLE SQL statements for the given app."
    69     from django.db import models
     70    from django.db import models, get_creation_module
    7071    from django.conf import settings
    7172
    7273    if settings.DATABASE_ENGINE == 'dummy':
     
    9798    # Create the many-to-many join tables.
    9899    for model in app_models:
    99100        final_output.extend(many_to_many_sql_for_model(model, style))
    100 
     101   
    101102    # Handle references to tables that are from other apps
    102103    # but don't exist physically.
    103104    not_installed_models = set(pending_references.keys())
     
    163164                    col = f.column
    164165                    r_table = model._meta.db_table
    165166                    r_col = model._meta.get_field(f.rel.field_name).column
    166                     r_name = '%s_refs_%s_%x' % (col, r_col, abs(hash((table, r_table))))
     167                    r_name = connection.ops.reference_name(r_col, col, r_table, table)
    167168                    output.append('%s %s %s %s;' % \
    168169                        (style.SQL_KEYWORD('ALTER TABLE'),
    169170                        style.SQL_TABLE(qn(table)),
     
    245246    Returns the SQL required to create a single model, as a tuple of:
    246247        (list_of_sql, pending_references_dict)
    247248    """
    248     from django.db import connection, models
    249 
     249    from django.db import connection, models, get_creation_module
     250    creation_module = get_creation_module()
     251    # If the database backend wants to create model itself, let it
     252    if hasattr(creation_module, "sql_model_create"):
     253        return creation_module.sql_model_create(model, style, known_models)
    250254    opts = model._meta
    251255    final_output = []
    252256    table_output = []
     
    315319    """
    316320    Returns any ALTER TABLE statements to add constraints after the fact.
    317321    """
    318     from django.db import connection
     322    from django.db import connection, get_creation_module
    319323    from django.db.backends.util import truncate_name
    320 
     324   
     325    creation_module = get_creation_module()
     326    # If the database backend wants to create many_to_many sql itself, let it
     327    if hasattr(creation_module, "sql_for_pending_references"):
     328        return creation_module.sql_for_pending_references(model, style, pending_references)
     329   
    321330    qn = connection.ops.quote_name
    322331    final_output = []
    323332    if connection.features.supports_constraints:
     
    331340                col = opts.get_field(f.rel.field_name).column
    332341                # For MySQL, r_name must be unique in the first 64 characters.
    333342                # So we are careful with character usage here.
    334                 r_name = '%s_refs_%s_%x' % (r_col, col, abs(hash((r_table, table))))
     343                r_name = connection.ops.reference_name(r_col, col, r_table, table)
    335344                final_output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' % \
    336345                    (qn(r_table), truncate_name(r_name, connection.ops.max_name_length()),
    337346                    qn(r_col), qn(table), qn(col),
     
    340349    return final_output
    341350
    342351def many_to_many_sql_for_model(model, style):
    343     from django.db import connection, models
     352    from django.db import connection, models, get_creation_module
    344353    from django.contrib.contenttypes import generic
    345354    from django.db.backends.util import truncate_name
    346 
     355   
     356    creation_module = get_creation_module()
     357    # If the database backend wants to create many_to_many sql itself, let it
     358    if hasattr(creation_module, "many_to_many_sql_for_model"):
     359        return creation_module.many_to_many_sql_for_model(model, style)
     360       
    347361    opts = model._meta
    348362    final_output = []
    349363    qn = connection.ops.quote_name
     
    406420            final_output.append('\n'.join(table_output))
    407421
    408422            for r_table, r_col, table, col in deferred:
    409                 r_name = '%s_refs_%s_%x' % (r_col, col,
    410                         abs(hash((r_table, table))))
     423                r_name = connection.ops.reference_name(r_col, col, r_table, table)
    411424                final_output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' %
    412425                (qn(r_table),
    413426                truncate_name(r_name, connection.ops.max_name_length()),
  • tests/modeltests/custom_methods/models.py

     
    2727        """
    2828        from django.db import connection
    2929        cursor = connection.cursor()
     30        # Some backends really really need quotes!
    3031        cursor.execute("""
    31             SELECT id, headline, pub_date
    32             FROM custom_methods_article
    33             WHERE pub_date = %s
    34                 AND id != %s""", [str(self.pub_date), self.id])
     32            SELECT "id", "headline", "pub_date"
     33            FROM "custom_methods_article"
     34            WHERE "pub_date" = %s
     35                AND "id" != %s""", [str(self.pub_date), self.id])
    3536        # The asterisk in "(*row)" tells Python to expand the list into
    3637        # positional arguments to Article().
    3738        return [self.__class__(*row) for row in cursor.fetchall()]
     39   
     40    class articles_from_same_day_3(models.Procedure):
     41        """
     42        Stored procedure version of get_articles_from_same_day_1, which executes
     43        a custom stored procedure for the sake of demonstration.
     44        """
     45        __params__ = [ ('self_pub_date', 'date'),
     46                       ('self_id', 'integer') ]
     47        __returns__ = [ ('other_id', 'integer'),
     48                       ('other_headline', 'varchar(100)'),
     49                       ('other_pub_date', 'date') ]
     50        __body__ = \
     51        """FOR SELECT "id", "headline", "pub_date"
     52           FROM "custom_methods_article"
     53           WHERE "pub_date" = :self_pub_date
     54           AND "id" != :self_id
     55           INTO :other_id, :other_headline, :other_pub_date
     56           DO BEGIN SUSPEND; END"""
     57   
     58    def articles_from_same_day_4(self):
     59        # The asterisk in "(*row)" tells Python to expand the list into
     60        # positional arguments to Article().
     61        return [self.__class__(*row) for row in self.articles_from_same_day_3(self.pub_date, self.pk) ]
     62     
     63    # Tell the creation module to install the procedure   
     64    procedures = (articles_from_same_day_3,)
    3865
     66   
    3967__test__ = {'API_TESTS':"""
    4068# Create a couple of Articles.
    4169>>> from datetime import date
     
    5179[<Article: Beatles reunite>]
    5280>>> a.articles_from_same_day_2()
    5381[<Article: Beatles reunite>]
     82>>> [Article(*row) for row in a.articles_from_same_day_3(a.pub_date, a.pk)]
     83[<Article: Beatles reunite>]
     84>>> a.articles_from_same_day_4()
     85[<Article: Beatles reunite>]
    5486>>> b.articles_from_same_day_1()
    5587[<Article: Area man programs in Python>]
    5688>>> b.articles_from_same_day_2()
  • tests/modeltests/lookup/models.py

     
    274274>>> a4.save()
    275275>>> a5 = Article(pub_date=now, headline='hey-Foo')
    276276>>> a5.save()
     277"""}
    277278
     279# Firebird support 'magic values'
     280if settings.DATABASE_ENGINE in ('firebird',):
     281    __test__['API_TESTS'] += r"""
     282# and yet more:
     283>>> a10 = Article(pub_date='today', headline='foobar')
     284>>> a10.save()
     285>>> a11 = Article(pub_date='tomorrow', headline='foobaz')
     286>>> a11.save()
     287>>> a12 = Article(pub_date='yesterday', headline='ooF')
     288>>> a12.save()
     289>>> a13 = Article(pub_date='now', headline='foobarbaz')
     290>>> a13.save()
     291>>> a14 = Article(pub_date='today', headline='zoocarfaz')
     292>>> a14.save()
     293>>> a15 = Article(pub_date='today', headline='barfoobaz')
     294>>> a15.save()
     295>>> a16 = Article(pub_date='today', headline='bazbaRFOO')
     296>>> a16.save()
     297>>> Article.objects.filter(pub_date__exact='yesterday')
     298[<Article: ooF>]
     299"""
     300
     301
     302# Firebird doesn't support regular expression lookups
     303if settings.DATABASE_ENGINE not in ('firebird',):
     304    __test__['API_TESTS'] += r"""
    278305# zero-or-more
    279306>>> Article.objects.filter(headline__regex=r'fo*')
    280307[<Article: f>, <Article: fo>, <Article: foo>, <Article: fooo>]
     
    348375[<Article: barfoobaz>, <Article: baz>, <Article: bazbaRFOO>, <Article: foobarbaz>, <Article: foobaz>]
    349376>>> Article.objects.filter(headline__iregex=r'b.*ar')
    350377[<Article: bar>, <Article: barfoobaz>, <Article: bazbaRFOO>, <Article: foobar>, <Article: foobarbaz>]
    351 """}
     378"""
    352379
    353380
    354 if settings.DATABASE_ENGINE not in ('mysql', 'mysql_old'):
     381if settings.DATABASE_ENGINE not in ('mysql', 'mysql_old', 'firebird'):
    355382    __test__['API_TESTS'] += r"""
    356383# grouping and backreferences
    357384>>> Article.objects.filter(headline__regex=r'b(.).*b\1')
  • tests/regressiontests/serializers_regress/tests.py

     
    2222    import decimal
    2323except ImportError:
    2424    from django.utils import _decimal as decimal
     25try:
     26    set
     27except NameError:
     28    from sets import Set as set   # Python 2.3 fallback
    2529
    2630# A set of functions that can be used to recreate
    2731# test data objects of various kinds.
     
    8387    testcase.assertEqual(data, instance.data_id)
    8488
    8589def m2m_compare(testcase, pk, klass, data):
     90    # Use sets to ignore order of data
    8691    instance = klass.objects.get(id=pk)
    87     testcase.assertEqual(data, [obj.id for obj in instance.data.all()])
     92    testcase.assertEqual(set(data), set([obj.id for obj in instance.data.all()]))
    8893
    8994def o2o_compare(testcase, pk, klass, data):
    9095    instance = klass.objects.get(data=data)
  • 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