Ticket #1261: firebird-6669-update.diff
File firebird-6669-update.diff, 98.2 KB (added by , 17 years ago) |
---|
-
django/db/models/base.py
4 4 from django.core.exceptions import ObjectDoesNotExist 5 5 from django.db.models.fields import AutoField, ImageField, FieldDoesNotExist 6 6 from django.db.models.fields.related import OneToOneRel, ManyToOneRel 7 from django.db.models.fields.computed import ComputedField 7 8 from django.db.models.query import delete_objects 8 9 from django.db.models.options import Options, AdminOptions 9 10 from django.db import connection, transaction … … 227 228 self._meta.pk.get_db_prep_lookup('exact', pk_val)) 228 229 # If it does already exist, do an UPDATE. 229 230 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)] 231 232 if db_values: 232 233 cursor.execute("UPDATE %s SET %s WHERE %s=%%s" % \ 233 234 (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)]), 235 236 qn(self._meta.pk.column)), 236 237 db_values + self._meta.pk.get_db_prep_lookup('exact', pk_val)) 237 238 else: 238 239 record_exists = False 239 240 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))] 242 243 # If the PK has been manually set, respect that. 243 244 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)] 245 249 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)] 246 250 placeholders = ['%s'] * len(field_names) 247 251 if self._meta.order_with_respect_to: -
django/db/models/manager.py
77 77 def filter(self, *args, **kwargs): 78 78 return self.get_query_set().filter(*args, **kwargs) 79 79 80 def firefilter(self, *args, **kwargs): 81 return self.get_query_set().firefilter(*args, **kwargs) 82 80 83 def complex_filter(self, *args, **kwargs): 81 84 return self.get_query_set().complex_filter(*args, **kwargs) 82 85 -
django/db/models/fields/__init__.py
6 6 except ImportError: 7 7 from django.utils import _decimal as decimal # for Python 2.3 8 8 9 from django.db import get_creation_module9 from django.db import connection, get_creation_module 10 10 from django.db.models import signals 11 11 from django.dispatch import dispatcher 12 12 from django.conf import settings … … 66 66 # 67 67 # getattr(obj, opts.pk.attname) 68 68 69 class Field(object):69 class _Field(object): 70 70 # Provide backwards compatibility for the maxlength attribute and 71 71 # argument for this class and all subclasses. 72 72 __metaclass__ = LegacyMaxlength … … 83 83 core=False, rel=None, default=NOT_PROVIDED, editable=True, serialize=True, 84 84 prepopulate_from=None, unique_for_date=None, unique_for_month=None, 85 85 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): 87 88 self.name = name 88 89 self.verbose_name = verbose_name 89 90 self.primary_key = primary_key 90 91 self.max_length, self.unique = max_length, unique 92 self.encoding = encoding 91 93 self.blank, self.null = blank, null 92 94 # Oracle treats the empty string ('') as null, so coerce the null 93 95 # option whenever '' is a possible value. … … 105 107 self.help_text = help_text 106 108 self.db_column = db_column 107 109 self.db_tablespace = db_tablespace 110 self.on_update = on_update 111 self.on_delete = on_delete 108 112 109 113 # Set db_index to True if the field has a relationship and doesn't explicitly set db_index. 110 114 self.db_index = db_index … … 148 152 data_types = get_creation_module().DATA_TYPES 149 153 internal_type = self.get_internal_type() 150 154 if internal_type not in data_types: 151 return None 155 return None 152 156 return data_types[internal_type] % self.__dict__ 153 157 154 158 def validate_full(self, field_data, all_data): … … 402 406 "Returns the value of this field in the given model instance." 403 407 return getattr(obj, self.attname) 404 408 409 # Use the backend's Field class if it defines one. Otherwise, use _Field. 410 if connection.features.uses_custom_field: 411 Field = connection.ops.field_class(_Field) 412 else: 413 Field = _Field 414 405 415 class AutoField(Field): 406 416 empty_strings_allowed = False 407 417 def __init__(self, *args, **kwargs): … … 688 698 defaults.update(kwargs) 689 699 return super(DecimalField, self).formfield(**defaults) 690 700 701 class 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 691 709 class EmailField(CharField): 692 710 def __init__(self, *args, **kwargs): 693 711 kwargs['max_length'] = kwargs.get('max_length', 75) … … 890 908 defaults.update(kwargs) 891 909 return super(IPAddressField, self).formfield(**defaults) 892 910 911 class 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 893 920 class NullBooleanField(Field): 894 921 empty_strings_allowed = False 895 922 def __init__(self, *args, **kwargs): … … 997 1024 # doesn't support microseconds. 998 1025 if settings.DATABASE_ENGINE == 'mysql' and hasattr(value, 'microsecond'): 999 1026 value = value.replace(microsecond=0) 1000 if settings.DATABASE_ENGINE == 'oracle':1001 # cx_Oracle expectsa 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. 1002 1029 if isinstance(value, datetime.time): 1003 1030 value = datetime.datetime(1900, 1, 1, value.hour, value.minute, 1004 1031 value.second, value.microsecond) -
django/db/models/fields/related.py
331 331 new_ids.add(obj) 332 332 # Add the newly created or already existing objects to the join table. 333 333 # First find out which items are already added, to avoid adding them twice 334 qn = connection.ops.quote_name 334 335 cursor = connection.cursor() 335 336 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))), 338 339 [self._pk_val] + list(new_ids)) 339 340 existing_ids = set([row[0] for row in cursor.fetchall()]) 340 341 341 342 # Add the ones that aren't there already 342 343 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)) 346 347 transaction.commit_unless_managed() 347 348 348 349 def _remove_items(self, source_col_name, target_col_name, *objs): -
django/db/models/fields/computed.py
1 from django.db.models.fields import CharField, DateField, DateTimeField, DecimalField 2 from django.db.models.fields import IntegerField, FloatField, SmallIntegerField, TimeField 3 4 class ComputedField(object): 5 def __init__(self, **kwargs): 6 expression = kwargs.pop('expression') 7 if 'params' in kwargs: 8 raw_params = kwargs.pop('params') 9 params = [] 10 for rp in raw_params: 11 params.append('"%s"' % rp) 12 self.expression = expression % tuple(params) 13 else: 14 self.expression = expression 15 def db_type(self): 16 return 'ComputedField' 17 18 class ComputedCharField(ComputedField, CharField): 19 def __init__(self, **kwargs): 20 ComputedField.__init__(self, **kwargs) 21 if 'params' in kwargs: 22 del kwargs['params'] 23 if 'expression' in kwargs: 24 del kwargs['expression'] 25 CharField.__init__(self, **kwargs) 26 27 class ComputedDateField(ComputedField, DateField): 28 def __init__(self, **kwargs): 29 ComputedField.__init__(self, **kwargs) 30 if 'params' in kwargs: 31 del kwargs['params'] 32 if 'expression' in kwargs: 33 del kwargs['expression'] 34 DateField.__init__(self, **kwargs) 35 36 class ComputedDateTimeField(ComputedField, DateTimeField): 37 def __init__(self, **kwargs): 38 ComputedField.__init__(self, **kwargs) 39 if 'params' in kwargs: 40 del kwargs['params'] 41 if 'expression' in kwargs: 42 del kwargs['expression'] 43 DateTimeField.__init__(self, **kwargs) 44 45 class ComputedDecimalField(ComputedField, DecimalField): 46 def __init__(self, **kwargs): 47 ComputedField.__init__(self, **kwargs) 48 if 'params' in kwargs: 49 del kwargs['params'] 50 if 'expression' in kwargs: 51 del kwargs['expression'] 52 DecimalField.__init__(self, **kwargs) 53 54 class ComputedFloatField(ComputedField, FloatField): 55 def __init__(self, **kwargs): 56 ComputedField.__init__(self, **kwargs) 57 if 'params' in kwargs: 58 del kwargs['params'] 59 if 'expression' in kwargs: 60 del kwargs['expression'] 61 FloatField.__init__(self, **kwargs) 62 63 class ComputedIntegerField(ComputedField, IntegerField): 64 def __init__(self, **kwargs): 65 ComputedField.__init__(self, **kwargs) 66 if 'params' in kwargs: 67 del kwargs['params'] 68 if 'expression' in kwargs: 69 del kwargs['expression'] 70 IntegerField.__init__(self, **kwargs) 71 72 class ComputedSmallIntegerField(ComputedField, SmallIntegerField): 73 def __init__(self, **kwargs): 74 ComputedField.__init__(self, **kwargs) 75 if 'params' in kwargs: 76 del kwargs['params'] 77 if 'expression' in kwargs: 78 del kwargs['expression'] 79 SmallIntegerField.__init__(self, **kwargs) 80 81 class ComputedTimeField(ComputedField, TimeField): 82 def __init__(self, **kwargs): 83 ComputedField.__init__(self, **kwargs) 84 if 'params' in kwargs: 85 del kwargs['params'] 86 if 'expression' in kwargs: 87 del kwargs['expression'] 88 TimeField.__init__(self, **kwargs) 89 -
django/db/models/__init__.py
7 7 from django.db.models.manager import Manager 8 8 from django.db.models.base import Model, AdminOptions 9 9 from django.db.models.fields import * 10 from django.db.models.procedures import Procedure, Trigger 10 11 from django.db.models.fields.subclassing import SubfieldBase 11 12 from django.db.models.fields.related import ForeignKey, OneToOneField, ManyToManyField, ManyToOneRel, ManyToManyRel, OneToOneRel, TABULAR, STACKED 13 from django.db.models.fields.computed import * 12 14 from django.db.models import signals 13 15 from django.utils.functional import curry 14 16 from django.utils.text import capfirst -
django/db/models/procedures/__init__.py
1 from django.db import connection, transaction 2 3 4 class procedure_meta(type): 5 """ 6 Metaclass for native stored procedures behaving as normal Django model methods 7 Subclass Procedure inside your model, create procedures tuple 8 and use the class as ordinary method 9 Please treat the resulting classes as methods because they don't 10 behave like classes anymore 11 Look at tests/customtests/custom_methods/model.py for demonstration 12 """ 13 def __init__(cls, name, bases, attrs): 14 super(procedure_meta, cls).__init__(name, bases, attrs) 15 params, returns, vars, body = None, None, None, None 16 if '__params__' in attrs: 17 params = attrs['__params__'] 18 if '__returns__' in attrs: 19 returns = attrs['__returns__'] 20 if '__vars__' in attrs: 21 vars = attrs['__vars__'] 22 if name != 'Procedure': 23 assert '__body__' in attrs, "Procedure must have body" 24 header = ['CREATE OR ALTER PROCEDURE %s ' % name] 25 if params: 26 header.append('(') 27 header.append(', '.join([param + ' ' + paramtype for param, paramtype in params])) 28 header.append(')') 29 if returns: 30 header.append('\nRETURNS (') 31 header.append(', '.join([ret + ' ' + rettype for ret, rettype in returns])) 32 header.append(')') 33 cls.__sql__ = [''.join(header)] 34 cls.__sql__.append('AS') 35 if vars: 36 var_declare = ['DECLARE VARIABLE '] 37 var_declare.append(', '.join([var + ' ' + vartype for var, vartype in vars])) 38 var_declare.append(';') 39 cls.__sql__.append(''.join(var_declare)) 40 cls.__sql__.extend(['BEGIN', attrs['__body__'], 'END']) 41 cls.sql = '\n'.join(cls.__sql__) 42 cls.params = params 43 cls.returns = returns 44 cls.procedure_name = name 45 46 def create_procedure_sql(cls): 47 return cls.sql 48 49 def execute(cls, cursor, *args): 50 if args: 51 return cursor.execute_straight('SELECT %s FROM %s(%s);' %\ 52 (', '.join("%s" % ret[0] for ret in cls.returns), 53 cls.procedure_name, 54 ', '.join("?" * len(args))), args) 55 else: 56 return cursor.execute_straight('SELECT %s FROM %s;' %\ 57 (', '.join("%s" % ret[0] for ret in cls.returns), 58 cls.procedure_name)) 59 60 def __call__(cls, *args): 61 cursor = connection.cursor() 62 if not cls.returns: 63 cursor.execute('EXECUTE PROCEDURE "%s" %s;' %\ 64 (cls.procedure_name, 65 ' ,'.join("'%s'" % arg for arg in args))) 66 elif len(cls.returns) == 1: 67 # Procedure returns value 68 cursor.callproc(cls.procedure_name, args) 69 return cursor.fetchone()[0] 70 71 else: 72 cursor.execute_straight('SELECT %s FROM %s(%s);' %\ 73 (', '.join("%s" % ret[0] for ret in cls.returns), 74 cls.procedure_name, 75 ', '.join("?" * len(args))), args) 76 return cursor.fetchall() 77 78 class trigger_meta(type): 79 """ 80 """ 81 def __init__(cls, name, bases, attrs): 82 super(trigger_meta, cls).__init__(name, bases, attrs) 83 vars, body, table, mode = None, None, None, None 84 if '__vars__' in attrs: 85 vars = attrs['__vars__'] 86 if name != 'Trigger': 87 assert '__body__' in attrs, "Triggers must have some SQL code" 88 assert '__table__' in attrs, "Triggers must have the table" 89 assert '__mode__' in attrs, "Triggers must have a mode, e.g. AFTER INSERT" 90 header = 'CREATE OR ALTER TRIGGER %s FOR %s\n%s AS' %\ 91 (name, connection.ops.quote_name(attrs['__table__']), attrs['__mode__']) 92 cls.__sql__ = [header] 93 if vars: 94 var_declare = ['DECLARE VARIABLE '] 95 var_declare.append(', '.join([var + ' ' + vartype for var, vartype in vars])) 96 var_declare.append(';') 97 cls.__sql__.append(''.join(var_declare)) 98 cls.__sql__.extend(['BEGIN', attrs['__body__'], 'END']) 99 cls.sql = '\n'.join(cls.__sql__) 100 cls.trigger_name = name 101 def create_trigger_sql(cls): 102 return cls.sql 103 104 class Procedure(object): 105 __metaclass__ = procedure_meta 106 107 class Trigger(object): 108 __metaclass__ = trigger_meta 109 -
django/db/models/procedures/firemethod.py
1 # Stored procedures' Django model method wrapper 2 # 3 # Works only with Python 2.5+ 4 # For Python < 2.5 use class notation 5 # 6 # Example: 7 # 8 # @firemethod 9 # def was_published_today_from_id(self_id='integer'): 10 # """ 11 # SELECT "id" 12 # FROM "firemethod_article" 13 # WHERE "pub_date" = 'today' AND "id" = :self_id 14 # INTO :result; 15 # IF (result IS NULL) THEN 16 # result = 0; 17 # ELSE 18 # result = 1; 19 # EXIT; 20 # """ 21 # return ('result', 'integer') 22 # 23 # @firemethod 24 # def was_published_today_filter(): 25 # """ 26 # FOR SELECT "id", "headline", "pub_date" 27 # FROM "firemethod_article" 28 # WHERE "pub_date" = 'today' 29 # INTO :other_id, :other_headline, :other_pub_date 30 # DO BEGIN SUSPEND; END 31 # """ 32 # # Dummy variable just to show how to declare 33 # declare = ('dummy', 'varchar(200)') 34 # return ('other_id', 'integer'), ('other_headline', 'varchar(100)'), ('other_pub_date', 'date') 35 # 36 # procedures = (was_published_today_filter, was_published_today_from_id) 37 # 38 # 39 # >>> article.was_published_today_from_id(article.pk) 40 # 1 41 # >>> bool(art.was_published_today_from_id(other_article.pk)) 42 # False 43 # >>>Article.objects.firefilter(self.was_published_today_filter) 44 # [<Article: Only for today>, <Article: The life is always in a current moment>] 45 46 from functools import * 47 from django.db.models import Procedure 48 from django.db.models.procedures import procedure_meta 49 50 def firemethod(f): 51 @wraps(f) 52 def wrapper(): 53 params, returns, vars, ret = None, None, None, f() 54 attrs = {} 55 if f.func_code.co_varnames: 56 all_vars = list(f.func_code.co_varnames) 57 if 'declare' in all_vars: 58 all_tuples = set([ d for d in f.func_code.co_consts if isinstance(d, tuple) ]) 59 if isinstance(ret[0], basestring): 60 ret = [ret] 61 ret_tuples = set(ret) 62 all_vars.remove('declare') 63 vars = [] 64 for var, var_type in all_tuples - ret_tuples: 65 vars.append((var, var_type)) 66 if len(all_vars) > 0 and f.func_defaults: 67 params = [] 68 for key, value in zip(all_vars, f.func_defaults) : 69 params.append((key, value)) 70 if ret: 71 returns = [] 72 if isinstance(ret[0], basestring): 73 ret = [ret] 74 for var, var_type in ret: 75 returns.append((var, var_type)) 76 if params: 77 attrs['__params__'] = params 78 if returns: 79 attrs['__returns__'] = returns 80 if vars: 81 attrs['__vars__'] = vars 82 attrs['__body__'] = f.__doc__ 83 return procedure_meta(f.__name__, (Procedure,), attrs) 84 return wrapper() 85 -
django/db/models/query.py
612 612 columns = [f.column for f in fields] 613 613 select = ['%s.%s' % (qn(self.model._meta.db_table), qn(c)) for c in columns] 614 614 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]) 616 619 field_names.extend([f[0] for f in extra_select]) 617 620 618 621 cursor = connection.cursor() … … 1111 1114 # Last query term was a normal field. 1112 1115 column = field.column 1113 1116 db_type = field.db_type() 1114 1115 1117 where.append(get_where_clause(lookup_type, current_table + '.', column, value, db_type)) 1116 1118 params.extend(field.get_db_prep_lookup(lookup_type, value)) 1117 1119 … … 1146 1148 if isinstance(f, generic.GenericRelation): 1147 1149 from django.contrib.contenttypes.models import ContentType 1148 1150 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) 1149 1153 args_extra = [ContentType.objects.get_for_model(cls).id] 1150 1154 else: 1151 1155 query_extra = '' -
django/db/backends/__init__.py
48 48 needs_upper_for_iops = False 49 49 supports_constraints = True 50 50 supports_tablespaces = False 51 quote_autofields = False 51 52 uses_case_insensitive_names = False 53 uses_custom_field = False 52 54 uses_custom_queryset = False 53 55 54 56 class BaseDatabaseOperations(object): … … 199 201 """ 200 202 raise NotImplementedError() 201 203 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 202 207 def random_function_sql(self): 203 208 """ 204 209 Returns a SQL expression that returns a random value. -
django/db/backends/firebird/base.py
1 """ 2 Firebird database backend for Django. 3 4 Requires KInterbasDB 3.2: http://kinterbasdb.sourceforge.net/ 5 The egenix mx (mx.DateTime) is NOT required 6 7 Database charset should be UNICODE_FSS or UTF8 (FireBird 2.0+) 8 To use UTF8 encoding add FIREBIRD_CHARSET = 'UTF8' to your settings.py 9 UNICODE_FSS works with all versions and uses less memory 10 """ 11 12 from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, BaseDatabaseOperations, util 13 import sys 14 try: 15 import decimal 16 except ImportError: 17 from django.utils import _decimal as decimal # for Python 2.3 18 19 try: 20 import kinterbasdb as Database 21 except ImportError, e: 22 from django.core.exceptions import ImproperlyConfigured 23 raise ImproperlyConfigured, "Error loading KInterbasDB module: %s" % e 24 25 DatabaseError = Database.DatabaseError 26 IntegrityError = Database.IntegrityError 27 28 class 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) 38 class 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 285 class FirebirdQuerySet(DefaultQuerySet): 286 __firefilter__ = None 287 ___fireargs__ = () 288 def _get_sql_clause(self): 289 from django.db.models.query import SortedDict, handle_legacy_orderlist, orderfield2column, fill_table_cache 290 qn = this.quote_name 291 opts = self.model._meta 292 293 # Construct the fundamental parts of the query: SELECT X FROM Y WHERE Z. 294 select = ["%s.%s" % (qn(opts.db_table), qn(f.column)) for f in opts.fields] 295 tables = [qn(t) for t in self._tables] 296 joins = SortedDict() 297 where = self._where[:] 298 params = self._params[:] 299 300 # Convert self._filters into SQL. 301 joins2, where2, params2 = self._filters.get_sql(opts) 302 joins.update(joins2) 303 where.extend(where2) 304 params.extend(params2) 305 306 # Add additional tables and WHERE clauses based on select_related. 307 if self._select_related: 308 fill_table_cache(opts, select, tables, where, 309 old_prefix=opts.db_table, 310 cache_tables_seen=[opts.db_table], 311 max_depth=self._max_related_depth) 312 313 # Add any additional SELECTs. 314 if self._select: 315 select.extend([('(%s AS %s') % (qn(s[1]), qn(s[0])) for s in self._select.items()]) 316 317 # Start composing the body of the SQL statement. 318 sql = [" FROM", qn(opts.db_table)] 319 320 # Compose the join dictionary into SQL describing the joins. 321 if joins: 322 sql.append(" ".join(["%s %s %s ON %s" % (join_type, table, alias, condition) 323 for (alias, (table, join_type, condition)) in joins.items()])) 324 325 # Compose the tables clause into SQL. 326 if tables: 327 sql.append(", " + ", ".join(tables)) 328 329 # Compose the where clause into SQL. 330 if where: 331 sql.append(where and "WHERE " + " AND ".join(where)) 332 333 # ORDER BY clause 334 order_by = [] 335 if self._order_by is not None: 336 ordering_to_use = self._order_by 337 else: 338 ordering_to_use = opts.ordering 339 for f in handle_legacy_orderlist(ordering_to_use): 340 if f == '?': # Special case. 341 order_by.append(connection.ops.random_function_sql()) 342 else: 343 if f.startswith('-'): 344 col_name = f[1:] 345 order = "DESC" 346 else: 347 col_name = f 348 order = "ASC" 349 if "." in col_name: 350 table_prefix, col_name = col_name.split('.', 1) 351 table_prefix = qn(table_prefix) + '.' 352 else: 353 # Use the database table as a column prefix if it wasn't given, 354 # and if the requested column isn't a custom SELECT. 355 if "." not in col_name and col_name not in (self._select or ()): 356 table_prefix = qn(opts.db_table) + '.' 357 else: 358 table_prefix = '' 359 order_by.append('%s%s %s' % \ 360 (table_prefix, qn(orderfield2column(col_name, opts)), order)) 361 if order_by: 362 sql.append("ORDER BY " + ", ".join(order_by)) 363 364 return select, " ".join(sql), params 365 366 def iterator(self): 367 "Performs the SELECT database lookup of this QuerySet." 368 from django.db.models.query import get_cached_row 369 if self.__firefilter__: 370 extra_select = self._select.items() 371 cursor = connection.cursor() 372 self.__firefilter__.execute(cursor, *self.__fireargs__) 373 else: 374 try: 375 select, sql, params = self._get_sql_clause() 376 except EmptyResultSet: 377 raise StopIteration 378 379 # self._select is a dictionary, and dictionaries' key order is 380 # undefined, so we convert it to a list of tuples. 381 extra_select = self._select.items() 382 cursor = connection.cursor() 383 limit_offset_before = "" 384 if self._limit is not None: 385 limit_offset_before += "FIRST %s " % self._limit 386 if self._offset: 387 limit_offset_before += "SKIP %s " % self._offset 388 else: 389 assert self._offset is None, "'offset' is not allowed without 'limit'" 390 cursor.execute("SELECT " + limit_offset_before + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params) 391 392 fill_cache = self._select_related 393 fields = self.model._meta.fields 394 index_end = len(fields) 395 while 1: 396 rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE) 397 if not rows: 398 raise StopIteration 399 for row in rows: 400 row = self.resolve_columns(row, fields) 401 if fill_cache: 402 obj, index_end = get_cached_row(klass=self.model, row=row, 403 index_start=0, max_depth=self._max_related_depth) 404 else: 405 obj = self.model(*row[:index_end]) 406 for i, k in enumerate(extra_select): 407 setattr(obj, k[0], row[index_end+i]) 408 yield obj 409 410 def resolve_columns(self, row, fields=()): 411 from django.db.models.fields import DateField, DateTimeField, \ 412 TimeField, BooleanField, NullBooleanField, DecimalField, Field 413 values = [] 414 for value, field in map(None, row, fields): 415 # Convert 1 or 0 to True or False 416 if value in (1, 0) and isinstance(field, (BooleanField, NullBooleanField)): 417 value = bool(value) 418 419 values.append(value) 420 return values 421 422 def firefilter(self, proc, *args, **kwargs): 423 assert not kwargs, "Keyword arguments not supported with stored procedures" 424 if len(args) > 0: 425 assert self._limit is None and self._offset is None, \ 426 "Cannot filter a query once a slice has been taken." 427 clone = self._clone() 428 clone.__firefilter__ = proc 429 clone.__fireargs__ = args 430 return clone 431 432 def extra(self, select=None, where=None, params=None, tables=None): 433 assert self._limit is None and self._offset is None, \ 434 "Cannot change a query once a slice has been taken" 435 clone = self._clone() 436 qn = this.quote_name 437 if select: clone._select.update(select) 438 if where: 439 qn_where = [] 440 for where_item in where: 441 try: 442 table, col_exact = where_item.split(".") 443 col, value = col_exact.split("=") 444 where_item = "%s.%s = %s" % (qn(table.strip()), 445 qn(col.strip()), value.strip()) 446 except: 447 try: 448 table, value = where_item.split("=") 449 where_item = "%s = %s" % (qn(table.strip()), qn(value.strip())) 450 except: 451 raise TypeError, "Can't understand extra WHERE clause: %s" % where 452 qn_where.append(where_item) 453 clone._where.extend(qn_where) 454 if params: clone._params.extend(params) 455 if tables: clone._tables.extend(tables) 456 return clone 457 458 return FirebirdQuerySet 459 460 ################################################################################ 461 # Cursor wrapper 462 class FirebirdCursorWrapper(object): 463 """ 464 Django uses "format" ('%s') style placeholders, but firebird uses "qmark" ('?') style. 465 This fixes it -- but note that if you want to use a literal "%s" in a query, 466 you'll need to use "%%s". 467 468 We also do all automatic type conversions here. 469 """ 470 import kinterbasdb.typeconv_datetime_stdlib as tc_dt 471 import kinterbasdb.typeconv_fixed_decimal as tc_fd 472 import kinterbasdb.typeconv_text_unicode as tc_tu 473 import django.utils.encoding as dj_ue 474 475 def ascii_conv_in(self, text): 476 if text is not None: 477 return self.dj_ue.smart_str(text, 'ascii') 478 479 def ascii_conv_out(self, text): 480 if text is not None: 481 return self.dj_ue.smart_unicode(text) 482 483 def blob_conv_in(self, text): 484 return self.tc_tu.unicode_conv_in((self.dj_ue.smart_unicode(text), self.FB_CHARSET_CODE)) 485 486 def blob_conv_out(self, text): 487 return self.tc_tu.unicode_conv_out((text, self.FB_CHARSET_CODE)) 488 489 def fixed_conv_in(self, (val, scale)): 490 if val is not None: 491 if isinstance(val, basestring): 492 val = decimal.Decimal(val) 493 return self.tc_fd.fixed_conv_in_precise((val, scale)) 494 495 def timestamp_conv_in(self, timestamp): 496 if isinstance(timestamp, basestring): 497 #Replaces 6 digits microseconds to 4 digits allowed in Firebird 498 timestamp = timestamp[:24] 499 return self.tc_dt.timestamp_conv_in(timestamp) 500 501 def time_conv_in(self, value): 502 import datetime 503 if isinstance(value, datetime.datetime): 504 value = datetime.time(value.hour, value.minute, value.second, value.microsecond) 505 return self.tc_dt.time_conv_in(value) 506 507 def unicode_conv_in(self, text): 508 if text[0] is not None: 509 return self.tc_tu.unicode_conv_in((self.dj_ue.smart_unicode(text[0]), self.FB_CHARSET_CODE)) 510 511 def __init__(self, cursor, connection): 512 self.cursor = cursor 513 self._connection = connection 514 self._statement = None #prepared statement 515 self.FB_CHARSET_CODE = 3 #UNICODE_FSS 516 if connection.charset == 'UTF8': 517 self.FB_CHARSET_CODE = 4 # UTF-8 with Firebird 2.0+ 518 self.cursor.set_type_trans_in({ 519 'DATE': self.tc_dt.date_conv_in, 520 'TIME': self.time_conv_in, 521 'TIMESTAMP': self.timestamp_conv_in, 522 'FIXED': self.fixed_conv_in, 523 'TEXT': self.ascii_conv_in, 524 'TEXT_UNICODE': self.unicode_conv_in, 525 'BLOB': self.blob_conv_in 526 }) 527 self.cursor.set_type_trans_out({ 528 'DATE': self.tc_dt.date_conv_out, 529 'TIME': self.tc_dt.time_conv_out, 530 'TIMESTAMP': self.tc_dt.timestamp_conv_out, 531 'FIXED': self.tc_fd.fixed_conv_out_precise, 532 'TEXT': self.ascii_conv_out, 533 'TEXT_UNICODE': self.tc_tu.unicode_conv_out, 534 'BLOB': self.blob_conv_out 535 }) 536 537 def execute_immediate(self, query, params=()): 538 query = query % tuple(params) 539 self._connection.execute_immediate(query) 540 541 def prepare(self, query): 542 """ 543 Returns prepared statement for use with execute_prepared 544 http://kinterbasdb.sourceforge.net/dist_docs/usage.html#adv_prepared_statements 545 """ 546 query.replace("%s", "?") 547 return self.cursor.prep(query) 548 549 def execute_prepared(self, statement, params): 550 return self.cursor.execute(statement, params) 551 552 def execute_straight(self, query, params=()): 553 """ 554 Kinterbasdb-style execute with '?' instead of '%s' 555 """ 556 try: 557 return self.cursor.execute(query, params) 558 except Database.ProgrammingError, e: 559 err_no = int(str(e).split()[0].strip(',()')) 560 output = ["Execute query error. FB error No. %i" % err_no] 561 output.extend(str(e).split("'")[1].split('\\n')) 562 output.append("Query:") 563 output.append(query) 564 output.append("Parameters:") 565 output.append(str(params)) 566 raise Database.ProgrammingError, "\n".join(output) 567 568 def execute(self, query, params=()): 569 cquery = self.convert_query(query, len(params)) 570 if self._get_query() != cquery: 571 try: 572 self._statement = self.cursor.prep(cquery) 573 except Database.ProgrammingError, e: 574 output = ["Prepare query error."] 575 output.extend(str(e).split("'")[1].split('\\n')) 576 output.append("Query:") 577 output.append(cquery) 578 raise Database.ProgrammingError, "\n".join(output) 579 try: 580 return self.cursor.execute(self._statement, params) 581 except Database.ProgrammingError, e: 582 err_no = int(str(e).split()[0].strip(',()')) 583 output = ["Execute query error. FB error No. %i" % err_no] 584 output.extend(str(e).split("'")[1].split('\\n')) 585 output.append("Query:") 586 output.append(cquery) 587 output.append("Parameters:") 588 output.append(str(params)) 589 raise Database.ProgrammingError, "\n".join(output) 590 591 def executemany(self, query, param_list): 592 try: 593 cquery = self.convert_query(query, len(param_list[0])) 594 except IndexError: 595 return None 596 if self._get_query() != cquery: 597 self._statement = self.cursor.prep(cquery) 598 return self.cursor.executemany(self._statement, param_list) 599 600 def convert_query(self, query, num_params): 601 try: 602 return query % tuple("?" * num_params) 603 except TypeError, e: 604 print query, num_params 605 raise TypeError, e 606 607 def _get_query(self): 608 if self._statement: 609 return self._statement.sql 610 611 def __getattr__(self, attr): 612 if attr in self.__dict__: 613 return self.__dict__[attr] 614 else: 615 return getattr(self.cursor, attr) 616 617 ################################################################################ 618 # DatabaseWrapper(db.connection) 619 class DatabaseWrapper(BaseDatabaseWrapper): 620 features = DatabaseFeatures() 621 ops = DatabaseOperations() 622 operators = { 623 'exact': '= %s', 624 'iexact': '= UPPER(%s)', 625 'contains': "LIKE %s ESCAPE'\\'", 626 'icontains': 'CONTAINING %s', #case is ignored 627 'gt': '> %s', 628 'gte': '>= %s', 629 'lt': '< %s', 630 'lte': '<= %s', 631 'startswith': 'STARTING WITH %s', #looks to be faster then LIKE 632 'endswith': "LIKE %s ESCAPE'\\'", 633 'istartswith': 'STARTING WITH UPPER(%s)', 634 'iendswith': "LIKE UPPER(%s) ESCAPE'\\'" 635 } 636 637 def __init__(self, **kwargs): 638 from django.conf import settings 639 super(DatabaseWrapper, self).__init__(**kwargs) 640 self. _current_cursor = None 641 self._raw_cursor = None 642 self.charset = 'UNICODE_FSS' 643 self.FB_MAX_VARCHAR = 10921 #32765 MAX /3 644 self.BYTES_PER_DEFAULT_CHAR = 3 645 if hasattr(settings, 'FIREBIRD_CHARSET'): 646 if settings.FIREBIRD_CHARSET == 'UTF8': 647 self.charset = 'UTF8' 648 self.FB_MAX_VARCHAR = 8191 #32765 MAX /4 649 self.BYTES_PER_DEFAULT_CHAR = 4 650 651 def _connect(self, settings): 652 if settings.DATABASE_NAME == '': 653 from django.core.exceptions import ImproperlyConfigured 654 raise ImproperlyConfigured, "You need to specify DATABASE_NAME in your Django settings file." 655 kwargs = {'charset' : self.charset } 656 if settings.DATABASE_HOST: 657 kwargs['dsn'] = "%s:%s" % (settings.DATABASE_HOST, settings.DATABASE_NAME) 658 else: 659 kwargs['dsn'] = "localhost:%s" % settings.DATABASE_NAME 660 if settings.DATABASE_USER: 661 kwargs['user'] = settings.DATABASE_USER 662 if settings.DATABASE_PASSWORD: 663 kwargs['password'] = settings.DATABASE_PASSWORD 664 self.connection = Database.connect(**kwargs) 665 assert self.connection.charset == self.charset 666 667 def cursor(self): 668 from django.conf import settings 669 cursor = self._cursor(settings) 670 if settings.DEBUG: 671 self._debug_cursor = self.make_debug_cursor(cursor) 672 return self._debug_cursor 673 return cursor 674 675 def _cursor(self, settings): 676 if self.connection is None: 677 self._connect(settings) 678 cursor = self.connection.cursor() 679 self._raw_cursor = cursor 680 cursor = FirebirdCursorWrapper(cursor, self) 681 self._current_cursor = cursor 682 return cursor 683 684 def __getattr__(self, attr): 685 if attr in self.__dict__: 686 return self.__dict__[attr] 687 else: 688 return getattr(self.connection, attr) 689 -
django/db/backends/firebird/client.py
1 from django.conf import settings 2 import os 3 4 def 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
1 from django.db import transaction 2 from django.db.backends.firebird.base import DatabaseOperations 3 4 qn = quote_name = DatabaseOperations().quote_name 5 6 def 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 13 def 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 35 def 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 58 def 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. 82 DATA_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 7 from kinterbasdb import connect, create_database 8 from django.core.management import call_command 9 from django.conf import settings 10 from django.db import connection 11 import sys, os, os.path, codecs 12 try: 13 set 14 except 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) 20 TEST_MODE = 0 21 if 'FB_DJANGO_TEST_MODE' in os.environ: 22 TEST_MODE = int(os.environ['FB_DJANGO_TEST_MODE']) 23 24 DATA_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 53 PYTHON_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 98 def 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 129 DEFAULT_MAX_LENGTH = 100 130 def 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 if 'SUBSTR' not in existing_functions: 329 final_output.append("""DECLARE EXTERNAL FUNCTION SUBSTR CSTRING(255), SMALLINT, SMALLINT 330 RETURNS CSTRING(255) FREE_IT 331 ENTRY_POINT 'IB_UDF_substr' MODULE_NAME 'ib_udf';""") 332 333 # Create stored procedures 334 if hasattr(model, 'procedures'): 335 for proc in model.procedures: 336 final_output.append(proc.create_procedure_sql()) 337 338 # Create triggers 339 if hasattr(model, 'triggers'): 340 for proc in model.triggers: 341 final_output.append(proc.create_trigger_sql()) 342 343 return final_output, pending_references 344 345 def many_to_many_sql_for_model(model, style): 346 from django.db import connection, models 347 from django.contrib.contenttypes import generic 348 from django.db.backends.util import truncate_name 349 350 opts = model._meta 351 final_output = [] 352 qn = connection.ops.quote_name 353 for f in opts.many_to_many: 354 if not isinstance(f.rel, generic.GenericRel): 355 table_output = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + \ 356 style.SQL_TABLE(qn(f.m2m_db_table())) + ' ('] 357 table_output.append(' %s %s %s,' % 358 (style.SQL_FIELD(qn('id')), 359 style.SQL_COLTYPE(models.AutoField(primary_key=True).db_type()), 360 style.SQL_KEYWORD('NOT NULL PRIMARY KEY'))) 361 362 table_output.append(' %s %s %s,' % 363 (style.SQL_FIELD(qn(f.m2m_column_name())), 364 style.SQL_COLTYPE(models.ForeignKey(model).db_type()), 365 style.SQL_KEYWORD('NOT NULL'))) 366 table_output.append(' %s %s %s,' % 367 (style.SQL_FIELD(qn(f.m2m_reverse_name())), 368 style.SQL_COLTYPE(models.ForeignKey(f.rel.to).db_type()), 369 style.SQL_KEYWORD('NOT NULL'))) 370 deferred = [ 371 (f.m2m_db_table(), f.m2m_column_name(), opts.db_table, 372 opts.pk.column), 373 ( f.m2m_db_table(), f.m2m_reverse_name(), 374 f.rel.to._meta.db_table, f.rel.to._meta.pk.column) 375 ] 376 377 table_output.append(' %s (%s, %s)' % 378 (style.SQL_KEYWORD('UNIQUE'), 379 style.SQL_FIELD(qn(f.m2m_column_name())), 380 style.SQL_FIELD(qn(f.m2m_reverse_name())))) 381 table_output.append(');') 382 final_output.append('\n'.join(table_output)) 383 384 autoinc_sql = connection.ops.autoinc_sql(style, f.m2m_db_table(), 'id') 385 if autoinc_sql: 386 for stmt in autoinc_sql: 387 final_output.append(stmt) 388 389 if TEST_MODE < 2: 390 for r_table, r_col, table, col in deferred: 391 r_name = '%s_refs_%s_%x' % (r_col, col, 392 abs(hash((r_table, table)))) 393 final_output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' % 394 (qn(r_table), 395 truncate_name(r_name, connection.ops.max_name_length()), 396 qn(r_col), qn(table), qn(col), 397 'ON DELETE CASCADE ON UPDATE CASCADE')) 398 399 return final_output 400 401 def sql_for_pending_references(model, style, pending_references): 402 """ 403 Returns any ALTER TABLE statements to add constraints after the fact. 404 """ 405 from django.db import connection 406 407 qn = connection.ops.quote_name 408 final_output = [] 409 if TEST_MODE < 2: 410 opts = model._meta 411 if model in pending_references: 412 for rel_class, f in pending_references[model]: 413 rel_opts = rel_class._meta 414 r_table = rel_opts.db_table 415 r_col = f.column 416 table = opts.db_table 417 col = opts.get_field(f.rel.field_name).column 418 r_name = connection.ops.reference_name(r_col, col, r_table, table) 419 if not f.on_update: 420 f.on_update = 'CASCADE' 421 if not f.on_delete: 422 if TEST_MODE > 0: 423 f.on_delete = 'CASCADE' 424 else: 425 if f.null: 426 f.on_delete = 'SET NULL' 427 else: 428 f.on_delete = 'NO ACTION' 429 final_output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s) ON UPDATE %s ON DELETE %s;' % \ 430 (qn(r_table), r_name, qn(r_col), qn(table), qn(col), 431 f.on_update, f.on_delete)) 432 433 del pending_references[model] 434 return final_output 435 436 437 TEST_DATABASE_PREFIX = 'test_' 438 def create_test_db(settings, connection, verbosity, autoclobber): 439 # KInterbasDB supports dynamic database creation and deletion 440 # via the module-level function create_database and the method Connection.drop_database. 441 442 if settings.TEST_DATABASE_NAME: 443 TEST_DATABASE_NAME = settings.TEST_DATABASE_NAME 444 else: 445 dbnametuple = os.path.split(settings.DATABASE_NAME) 446 TEST_DATABASE_NAME = os.path.join(dbnametuple[0], TEST_DATABASE_PREFIX + dbnametuple[1]) 447 448 dsn = "localhost:%s" % TEST_DATABASE_NAME 449 if settings.DATABASE_HOST: 450 dsn = "%s:%s" % (settings.DATABASE_HOST, TEST_DATABASE_NAME) 451 452 if os.path.isfile(TEST_DATABASE_NAME): 453 sys.stderr.write("Database %s already exists\n" % TEST_DATABASE_NAME) 454 if not autoclobber: 455 confirm = raw_input("Type 'yes' if you would like to try deleting the test database '%s', or 'no' to cancel: " % TEST_DATABASE_NAME) 456 if autoclobber or confirm == 'yes': 457 if verbosity >= 1: 458 print "Destroying old test database..." 459 old_connection = connect(dsn=dsn, user=settings.DATABASE_USER, password=settings.DATABASE_PASSWORD) 460 old_connection.drop_database() 461 else: 462 print "Tests cancelled." 463 sys.exit(1) 464 465 if verbosity >= 1: 466 print "Creating test database..." 467 try: 468 charset = 'UNICODE_FSS' 469 if hasattr(settings, 'FIREBIRD_CHARSET'): 470 if settings.FIREBIRD_CHARSET == 'UTF8': 471 charset='UTF8' 472 create_database("create database '%s' user '%s' password '%s' default character set %s" % \ 473 (dsn, settings.DATABASE_USER, settings.DATABASE_PASSWORD, charset)) 474 except Exception, e: 475 sys.stderr.write("Got an error creating the test database: %s\n" % e) 476 sys.exit(2) 477 478 connection.close() 479 settings.DATABASE_NAME = TEST_DATABASE_NAME 480 481 call_command('syncdb', verbosity=verbosity, interactive=False) 482 483 if settings.CACHE_BACKEND.startswith('db://'): 484 cache_name = settings.CACHE_BACKEND[len('db://'):] 485 call_command('createcachetable', cache_name) 486 487 # Get a cursor (even though we don't need one yet). This has 488 # the side effect of initializing the test database. 489 cursor = connection.cursor() 490 491 return TEST_DATABASE_NAME 492 493 def destroy_test_db(settings, connection, old_database_name, verbosity): 494 # KInterbasDB supports dynamic database deletion via the method Connection.drop_database. 495 if verbosity >= 1: 496 print "Destroying test database..." 497 connection.drop_database() 498 499 -
django/core/cache/backends/db.py
47 47 if timeout is None: 48 48 timeout = self.default_timeout 49 49 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)) 51 52 num = cursor.fetchone()[0] 52 53 now = datetime.now().replace(microsecond=0) 53 54 exp = datetime.fromtimestamp(time.time() + timeout).replace(microsecond=0) 54 55 if num > self._max_entries: 55 56 self._cull(cursor, now) 56 57 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]) 58 59 try: 59 60 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]) 61 62 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)]) 63 64 except DatabaseError: 64 65 # To be threadsafe, updates/inserts are allowed to fail silently 65 66 pass -
django/core/management/sql.py
1 1 from django.core.management.base import CommandError 2 from django.conf import settings 2 3 import os 3 4 import re 4 5 … … 66 67 67 68 def sql_create(app, style): 68 69 "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 70 71 from django.conf import settings 71 72 72 73 if settings.DATABASE_ENGINE == 'dummy': … … 97 98 # Create the many-to-many join tables. 98 99 for model in app_models: 99 100 final_output.extend(many_to_many_sql_for_model(model, style)) 100 101 101 102 # Handle references to tables that are from other apps 102 103 # but don't exist physically. 103 104 not_installed_models = set(pending_references.keys()) … … 163 164 col = f.column 164 165 r_table = model._meta.db_table 165 166 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) 167 168 output.append('%s %s %s %s;' % \ 168 169 (style.SQL_KEYWORD('ALTER TABLE'), 169 170 style.SQL_TABLE(qn(table)), … … 245 246 Returns the SQL required to create a single model, as a tuple of: 246 247 (list_of_sql, pending_references_dict) 247 248 """ 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) 250 254 opts = model._meta 251 255 final_output = [] 252 256 table_output = [] … … 315 319 """ 316 320 Returns any ALTER TABLE statements to add constraints after the fact. 317 321 """ 318 from django.db import connection 322 from django.db import connection, get_creation_module 319 323 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 321 330 qn = connection.ops.quote_name 322 331 final_output = [] 323 332 if connection.features.supports_constraints: … … 331 340 col = opts.get_field(f.rel.field_name).column 332 341 # For MySQL, r_name must be unique in the first 64 characters. 333 342 # 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) 335 344 final_output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' % \ 336 345 (qn(r_table), truncate_name(r_name, connection.ops.max_name_length()), 337 346 qn(r_col), qn(table), qn(col), … … 340 349 return final_output 341 350 342 351 def 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 344 353 from django.contrib.contenttypes import generic 345 354 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 347 361 opts = model._meta 348 362 final_output = [] 349 363 qn = connection.ops.quote_name … … 406 420 final_output.append('\n'.join(table_output)) 407 421 408 422 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) 411 424 final_output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' % 412 425 (qn(r_table), 413 426 truncate_name(r_name, connection.ops.max_name_length()), -
tests/modeltests/custom_methods/models.py
27 27 """ 28 28 from django.db import connection 29 29 cursor = connection.cursor() 30 # Some backends really really need quotes! 30 31 cursor.execute(""" 31 SELECT id, headline, pub_date32 FROM custom_methods_article33 WHERE pub_date= %s34 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]) 35 36 # The asterisk in "(*row)" tells Python to expand the list into 36 37 # positional arguments to Article(). 37 38 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,) 38 65 66 39 67 __test__ = {'API_TESTS':""" 40 68 # Create a couple of Articles. 41 69 >>> from datetime import date … … 51 79 [<Article: Beatles reunite>] 52 80 >>> a.articles_from_same_day_2() 53 81 [<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>] 54 86 >>> b.articles_from_same_day_1() 55 87 [<Article: Area man programs in Python>] 56 88 >>> b.articles_from_same_day_2() -
tests/modeltests/lookup/models.py
274 274 >>> a4.save() 275 275 >>> a5 = Article(pub_date=now, headline='hey-Foo') 276 276 >>> a5.save() 277 """} 277 278 279 # Firebird support 'magic values' 280 if 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 303 if settings.DATABASE_ENGINE not in ('firebird',): 304 __test__['API_TESTS'] += r""" 278 305 # zero-or-more 279 306 >>> Article.objects.filter(headline__regex=r'fo*') 280 307 [<Article: f>, <Article: fo>, <Article: foo>, <Article: fooo>] … … 348 375 [<Article: barfoobaz>, <Article: baz>, <Article: bazbaRFOO>, <Article: foobarbaz>, <Article: foobaz>] 349 376 >>> Article.objects.filter(headline__iregex=r'b.*ar') 350 377 [<Article: bar>, <Article: barfoobaz>, <Article: bazbaRFOO>, <Article: foobar>, <Article: foobarbaz>] 351 """ }378 """ 352 379 353 380 354 if settings.DATABASE_ENGINE not in ('mysql', 'mysql_old' ):381 if settings.DATABASE_ENGINE not in ('mysql', 'mysql_old', 'firebird'): 355 382 __test__['API_TESTS'] += r""" 356 383 # grouping and backreferences 357 384 >>> Article.objects.filter(headline__regex=r'b(.).*b\1') -
tests/regressiontests/serializers_regress/tests.py
22 22 import decimal 23 23 except ImportError: 24 24 from django.utils import _decimal as decimal 25 try: 26 set 27 except NameError: 28 from sets import Set as set # Python 2.3 fallback 25 29 26 30 # A set of functions that can be used to recreate 27 31 # test data objects of various kinds. … … 83 87 testcase.assertEqual(data, instance.data_id) 84 88 85 89 def m2m_compare(testcase, pk, klass, data): 90 # Use sets to ignore order of data 86 91 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()])) 88 93 89 94 def o2o_compare(testcase, pk, klass, data): 90 95 instance = klass.objects.get(data=data) -
tests/regressiontests/initial_sql_regress/sql/simple.sql
1 INSERT INTO initial_sql_regress_simple (name) VALUES ('John');2 INSERT INTO initial_sql_regress_simple (name) VALUES ('Paul');3 INSERT INTO initial_sql_regress_simple (name) VALUES ('Ringo');4 INSERT INTO initial_sql_regress_simple (name) VALUES ('George');5 INSERT INTO initial_sql_regress_simple (name) VALUES ('Miles O''Brien');6 INSERT INTO initial_sql_regress_simple (name) VALUES ('Semicolon;Man');7 INSERT INTO initial_sql_regress_simple (name) VALUES ('This line has a Windows line ending');1 INSERT INTO "initial_sql_regress_simple" ("name") VALUES ('John'); 2 INSERT INTO "initial_sql_regress_simple" ("name") VALUES ('Paul'); 3 INSERT INTO "initial_sql_regress_simple" ("name") VALUES ('Ringo'); 4 INSERT INTO "initial_sql_regress_simple" ("name") VALUES ('George'); 5 INSERT INTO "initial_sql_regress_simple" ("name") VALUES ('Miles O''Brien'); 6 INSERT INTO "initial_sql_regress_simple" ("name") VALUES ('Semicolon;Man'); 7 INSERT INTO "initial_sql_regress_simple" ("name") VALUES ('This line has a Windows line ending'); 8 8 -
tests/regressiontests/backends/models.py
22 22 >>> opts = Square._meta 23 23 >>> f1, f2 = opts.get_field('root'), opts.get_field('square') 24 24 >>> 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)))) 26 26 >>> cursor.executemany(query, [(i, i**2) for i in range(-5, 6)]) and None or None 27 27 >>> Square.objects.order_by('root') 28 28 [<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>]