Ticket #1261: firebird-6669-update.diff
| File firebird-6669-update.diff, 98.2 kB (added by i_i, 10 months ago) |
|---|
-
django/db/models/base.py
old new 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
old new 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
old new 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
old new 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
old new 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
old new 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
old new 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
old new 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
old new 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
old new 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
old new 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 &n
