-
Notifications
You must be signed in to change notification settings - Fork 48
/
Copy pathbase.py
700 lines (623 loc) · 28.8 KB
/
base.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
"""
Firebird database backend for Django.
"""
try:
import firebird.driver as Database
except ImportError as e:
from django.core.exceptions import ImproperlyConfigured
raise ImproperlyConfigured("Error loading firebird driver: %s" % e)
from firebird.driver import CHARSET_MAP, tpb, TPB, Isolation, TraAccessMode, TableShareMode, TableAccessMode
from django.db import utils
from django.db.backends.base.base import BaseDatabaseWrapper
from django.utils.asyncio import async_unsafe
from django.utils.encoding import smart_str
from django.utils.functional import cached_property
from .operations import DatabaseOperations
from .features import DatabaseFeatures
from .client import DatabaseClient
from .creation import DatabaseCreation
from .introspection import DatabaseIntrospection
from .schema import DatabaseSchemaEditor
from .validation import DatabaseValidation
DatabaseError = Database.DatabaseError
IntegrityError = Database.IntegrityError
InterfaceError = Database.InterfaceError
OperationalError = Database.OperationalError
class DatabaseWrapper(BaseDatabaseWrapper):
vendor = 'firebird'
display_name = "Firebird"
# This dictionary maps Field objects to their associated Firebird column
# types, as strings. Column-type strings can contain format strings; they'll
# be interpolated against the values of Field.__dict__ before being output.
# If a column type is set to None, it won't be included in the output.
#
# Any format strings starting with "qn_" are quoted before being used in the
# output (the "qn_" prefix is stripped before the lookup is performed.
data_types = {
'AutoField': 'integer',
'BigAutoField': 'bigint',
'BinaryField': 'blob sub_type 0',
'BooleanField': 'smallint', # for firebird 3 it changes in init_connection_state
'CharField': 'varchar(%(max_length)s)',
'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
'DateField': 'date',
'DateTimeField': 'timestamp',
'DecimalField': 'decimal(%(max_digits)s, %(decimal_places)s)',
'DurationField': 'bigint',
'FileField': 'varchar(%(max_length)s)',
'FilePathField': 'varchar(%(max_length)s)',
'FloatField': 'double precision',
'IntegerField': 'integer',
'BigIntegerField': 'bigint',
'IPAddressField': 'char(15)',
'GenericIPAddressField': 'char(39)',
'NullBooleanField': 'smallint', # for firebird 3 it changes in init_connection_state
'OneToOneField': 'integer',
'PositiveBigIntegerField': 'bigint',
'PositiveIntegerField': 'integer',
'PositiveSmallIntegerField': 'smallint',
'SlugField': 'varchar(%(max_length)s)',
'SmallAutoField': 'smallint',
'SmallIntegerField': 'smallint',
'TextField': 'blob sub_type 1',
'TimeField': 'time',
'UUIDField': 'char(32)',
}
data_type_check_constraints = {
'BooleanField': '%(qn_column)s IN (0,1)', # for firebird 3 it changes in init_connection_state
'NullBooleanField': '(%(qn_column)s IN (0,1)) OR (%(qn_column)s IS NULL)',
'PositiveIntegerField': '%(qn_column)s >= 0',
'PositiveSmallIntegerField': '%(qn_column)s >= 0',
}
operators = {
'exact': '= %s',
'iexact': '= UPPER(%s)',
'contains': "LIKE %s ESCAPE'\\'",
'icontains': "LIKE UPPER(%s) ESCAPE'\\'", # 'CONTAINING %s', #case is ignored
'gt': '> %s',
'gte': '>= %s',
'lt': '< %s',
'lte': '<= %s',
'startswith': "LIKE %s ESCAPE'\\'", # 'STARTING WITH %s', #looks to be faster than LIKE
'endswith': "LIKE %s ESCAPE'\\'",
'istartswith': "LIKE UPPER(%s) ESCAPE'\\'", # 'STARTING WITH UPPER(%s)',
'iendswith': "LIKE UPPER(%s) ESCAPE'\\'",
'regex': "SIMILAR TO %s",
'iregex': "SIMILAR TO %s", # Case Sensitive depends on collation
}
# The patterns below are used to generate SQL pattern lookup clauses when
# the right-hand side of the lookup isn't a raw string (it might be an expression
# or the result of a bilateral transformation).
# In those cases, special characters for LIKE operators (e.g. \, *, _) should be
# escaped on database side.
#
# Note: we use str.format() here for readability as '%' is used as a wildcard for
# the LIKE operator.
pattern_esc = r"REPLACE(REPLACE(REPLACE({}, '\', '\\'), '%%', '\%%'), '_', '\_')"
pattern_ops = {
'contains': "LIKE '%%' || {} || '%%'",
'icontains': "LIKE '%%' || UPPER({}) || '%%'",
'startswith': "LIKE {} || '%%'",
'istartswith': "LIKE UPPER({}) || '%%'",
'endswith': "LIKE '%%' || {}",
'iendswith': "LIKE '%%' || UPPER({})",
}
@staticmethod
def binary(value):
return bytes(value)
Database.Binary = staticmethod(binary)
Database = Database
SchemaEditorClass = DatabaseSchemaEditor
# Classes instantiated in __init__().
client_class = DatabaseClient
creation_class = DatabaseCreation
features_class = DatabaseFeatures
introspection_class = DatabaseIntrospection
ops_class = DatabaseOperations
connections = []
def __init__(self, *args, **kwargs):
super(DatabaseWrapper, self).__init__(*args, **kwargs)
self._server_version = None
self._db_charset = None
self.encoding = None
opts = self.settings_dict["OPTIONS"]
RC = Database.core.TraIsolation.READ_COMMITTED
self.isolation_level = opts.get('isolation_level', RC)
self.features = DatabaseFeatures(self)
self.ops = DatabaseOperations(self)
self.client = DatabaseClient(self)
self.creation = DatabaseCreation(self)
self.introspection = DatabaseIntrospection(self)
self.validation = DatabaseValidation(self)
# #### Backend-specific methods for creating connections and cursors #####
def get_connection_params(self):
"""Returns a dict of parameters suitable for get_new_connection."""
settings_dict = self.settings_dict
if settings_dict['NAME'] == '':
from django.core.exceptions import ImproperlyConfigured
raise ImproperlyConfigured(
"settings.DATABASES is improperly configured. "
"Please supply the NAME value.")
# The port param is not used by fdb. It must be setting by database string
if settings_dict['PORT']:
database = '%(HOST)s/%(PORT)s:%(NAME)s'
else:
database = '%(HOST)s:%(NAME)s'
conn_params = {'charset': 'UTF8'}
conn_params['database'] = database % settings_dict
if settings_dict['USER']:
conn_params['user'] = settings_dict['USER']
if settings_dict['PASSWORD']:
conn_params['password'] = settings_dict['PASSWORD']
if 'ROLE' in settings_dict:
conn_params['role'] = settings_dict['ROLE']
if 'TIME_ZONE' in settings_dict:
conn_params['session_time_zone'] = settings_dict['TIME_ZONE']
options = settings_dict['OPTIONS'].copy()
conn_params.update(options)
self._db_charset = conn_params.get('charset')
self.encoding = CHARSET_MAP.get(self._db_charset, 'utf_8')
return conn_params
@async_unsafe
def get_new_connection(self, conn_params):
"""Opens a connection to the database."""
connection = Database.connect(**conn_params)
self.connections.append(connection)
for cc in self.connections:
if cc.is_closed():
self.connections.remove(cc)
return connection
def init_connection_state(self):
"""Initializes the database connection settings."""
if int(self.ops.firebird_version[3]) >= 3:
self.data_types['BooleanField'] = 'boolean'
self.data_types['NullBooleanField'] = 'boolean'
self.data_type_check_constraints['BooleanField'] = '%(qn_column)s IN (False,True)'
self.data_type_check_constraints[
'NullBooleanField'] = '(%(qn_column)s IN (False,True)) OR (%(qn_column)s IS NULL)'
if int(self.ops.firebird_version[3]) >= 4:
self.features.supports_over_clause = True
self.features.supports_partial_indexes = True
self.features.supports_timezones = True
self.data_types['DateTimeField'] = 'timestamp with time zone'
self.data_types['TimeField'] = 'time with time zone'
@async_unsafe
def close(self):
for cc in self.connections:
if cc.is_closed():
self.connections.remove(cc)
BaseDatabaseWrapper.close(self)
def create_cursor(self, name=None):
"""Creates a cursor. Assumes that a connection is established."""
if self.connection.is_closed():
raise InterfaceError("Cannot create cursor for closed connection")
if self.get_autocommit():
self.connection.begin(TPB(isolation=Isolation.READ_COMMITTED,
access_mode=TraAccessMode.WRITE,
auto_commit=False,
ignore_limbo=True).get_buffer())
cursor = self.connection.cursor()
return FirebirdCursorWrapper(cursor, self.encoding, self.get_autocommit())
def _commit(self):
if self.connection is not None:
with self.wrap_database_errors:
if self.connection.main_transaction.is_active():
return self.connection.commit()
def _rollback(self):
if self.connection is not None:
with self.wrap_database_errors:
if self.connection.main_transaction.is_active():
return self.connection.rollback()
# ##### Foreign key constraints checks handling #####
def disable_constraint_checking(self):
"""
Backends can implement as needed to temporarily disable foreign key
constraint checking. Should return True if the constraints were
disabled and will need to be reenabled.
"""
self.disable_constraints()
return True
def enable_constraint_checking(self):
"""
Backends can implement as needed to re-enable foreign key constraint
checking.
"""
self.enable_constraints()
def disable_constraints(self):
"""
Disables restrictions such as foreign keys, checks and unique.
.. important::
The server does not support explicit disabling of restrictions,
therefore, implementation is made using additional tables.
Note:
If there is an error when disable restrictions an exception is thrown.
"""
create_django_constraint = """
create table django$constraint (
django$constraint_name varchar(31) not null constraint pk_djangocopyconstraint primary key,
django$constraint_type varchar(11) not null,
django$relation_name varchar(31) not null,
django$index_name varchar(31),
django$const_name_uq varchar(31),
django$update_rule varchar(11),
django$delete_rule varchar(11),
django$constraint_source blob sub_type 1
)
"""
create_django_constraint_segment = """
create table django$constraint_segment (
django$constraint_name varchar(31) not null,
django$field_name varchar(31) not null,
django$position integer not null,
constraint pk_djangocopyconstraintsegment primary key (django$constraint_name, django$field_name)
)
"""
save_constraints = """
merge into django$constraint dc
using (select
trim(trailing from c.rdb$constraint_name) as constraint_name,
trim(trailing from c.rdb$constraint_type) as constraint_type,
trim(trailing from c.rdb$relation_name) as relation_name,
nullif(trim(trailing from c.rdb$index_name), '') as index_name,
t.rdb$trigger_source as constraint_source,
nullif(trim(trailing from r.rdb$const_name_uq), '') as const_name_uq,
nullif(trim(trailing from r.rdb$update_rule), '') as update_rule,
nullif(trim(trailing from r.rdb$delete_rule), '') as delete_rule
from
rdb$relation_constraints c left join
(select * from rdb$check_constraints p
where p.rdb$trigger_name = (select first 1 rdb$trigger_name from rdb$check_constraints o
where p.rdb$constraint_name = o.rdb$constraint_name)) h on c.rdb$constraint_name = h.rdb$constraint_name
left join rdb$triggers t on t.rdb$trigger_name = h.rdb$trigger_name
left join rdb$ref_constraints r on r.rdb$constraint_name = c.rdb$constraint_name
where c.rdb$constraint_type in ('FOREIGN KEY', 'CHECK', 'UNIQUE')
and
c.rdb$relation_name not starting with 'RDB$') rc
on dc.django$constraint_name = rc.constraint_name
when matched then
update set
dc.django$constraint_type = rc.constraint_type,
dc.django$relation_name = rc.relation_name,
dc.django$index_name = rc.index_name,
dc.django$constraint_source = rc.constraint_source,
dc.django$const_name_uq = rc.const_name_uq,
dc.django$update_rule = rc.update_rule,
dc.django$delete_rule = rc.delete_rule
when not matched then
insert (dc.django$constraint_name,
dc.django$constraint_type,
dc.django$relation_name,
dc.django$index_name,
dc.django$constraint_source,
dc.django$const_name_uq,
dc.django$update_rule,
dc.django$delete_rule)
values (rc.constraint_name,
rc.constraint_type,
rc.relation_name,
rc.index_name,
rc.constraint_source,
rc.const_name_uq,
update_rule,
rc.delete_rule)
"""
save_segment_constraints = """
merge into django$constraint_segment dcs
using (
select c.rdb$constraint_name as constraint_name,
s.rdb$field_name as field_name,
s.rdb$field_position as field_position
from rdb$relation_constraints c, rdb$index_segments s
where s.rdb$index_name = c.rdb$index_name
and c.rdb$constraint_type in ('FOREIGN KEY', 'CHECK', 'UNIQUE')
and c.rdb$relation_name not starting with 'RDB$') cs
on dcs.django$constraint_name = cs.constraint_name
and dcs.django$field_name = cs.field_name
when matched then
update set
dcs.django$position = cs.field_position
when not matched then
insert (django$constraint_name,
django$field_name,
django$position)
values (
cs.constraint_name,
cs.field_name,
cs.field_position)
"""
select_drop_constraints = """
select trim(trailing from rdb$constraint_name) as constr_name,
trim(trailing from c.rdb$relation_name) as table_name
from rdb$relation_constraints c
where c.rdb$constraint_type in ('FOREIGN KEY', 'CHECK', 'UNIQUE')
and
c.rdb$relation_name not starting with 'RDB$'
order by c.rdb$constraint_type
"""
editor = self.schema_editor()
if not self.table_exists("django$constraint"):
editor.execute(create_django_constraint)
if not self.table_exists("django$constraint_segment"):
editor.execute(create_django_constraint_segment)
editor.execute(save_constraints)
editor.execute(save_segment_constraints)
constraints = self.get_drop_constraints(select_drop_constraints)
for hm in constraints:
sql = """
alter table "%(table)s" drop constraint "%(constraint)s"
""" % {
'table': hm['TABLE_NAME'],
'constraint': hm['CONSTR_NAME']
}
editor.execute(sql)
def enable_constraints(self):
"""
Enables restrictions that have been disabled by calling the method `disable_constraint_checking`.
.. important::
The server does not support explicit disabling of restrictions,
therefore, implementation is made using additional tables.
Note:
If there is an error when enable restrictions an exception is thrown.
"""
editor = self.schema_editor()
select_django_constraint = """
select django$constraint_name as constraint_name,
django$constraint_type as constraint_type,
django$relation_name as relation_name,
django$index_name as index_name,
django$constraint_source as constraint_source,
django$const_name_uq as const_name_uq,
django$update_rule as update_rule,
django$delete_rule as delete_rule
from django$constraint
order by django$constraint_type desc"""
constraints = self.get_drop_constraints(select_django_constraint)
for hm in constraints:
if not self.table_exists(hm['RELATION_NAME'].strip()):
continue
create_string = "alter table \"" + hm['RELATION_NAME'] + "\" add "
if not hm['CONSTRAINT_NAME'].startswith("RDB$"):
create_string += "constraint " + "\"" + hm['CONSTRAINT_NAME'] + "\""
if hm['CONSTRAINT_TYPE'].casefold() == "CHECK".casefold():
create_string += " " + hm['CONSTRAINT_SOURCE']
elif hm['CONSTRAINT_TYPE'].casefold() == "FOREIGN KEY".casefold():
select_relation = """select coalesce(trim(trailing from rdb$relation_name), '')
from rdb$relation_constraints where rdb$constraint_name = '%s'
""" % hm['CONST_NAME_UQ']
table = ''
with self.cursor() as cursor:
cursor.execute(select_relation)
res = cursor.fetchone()
if not res:
continue
table = '"' + res[0].strip() + '"'
select_django_constraint_segment = """
select django$field_name as field_name from django$constraint_segment s
where s.django$constraint_name = '%s'
order by django$position
""" % hm['CONSTRAINT_NAME']
segments = []
with self.cursor() as cursor:
cursor.execute(select_django_constraint_segment)
for row in cursor.fetchall():
map = {}
for i, desc in enumerate(cursor.cursor.cursor.description):
map[desc[0]] = row[i]
segments.append(map)
field_list = []
for field in segments:
field_list.append('"' + field['FIELD_NAME'].strip() + '"')
create_string += " foreign key(" + ','.join(str(x) for x in field_list) + \
") references " + table + "("
select_index_segment = """
select trim(trailing from rdb$field_name) as field_name from rdb$index_segments s
join rdb$relation_constraints r on r.rdb$index_name = s.rdb$index_name
where r.rdb$constraint_name = '%s'
order by rdb$field_position
""" % hm['CONST_NAME_UQ']
index_segments = []
with self.cursor() as cursor:
cursor.execute(select_index_segment)
for row in cursor.fetchall():
index_segments.append('"' + row[0].strip() + '"')
create_string += ','.join(str(x) for x in index_segments) + ")"
if hm['UPDATE_RULE'].casefold() != "RESTRICT".casefold():
create_string += " on update " + hm['UPDATE_RULE']
if hm['DELETE_RULE'].casefold() != "RESTRICT".casefold():
create_string += " on delete " + hm['DELETE_RULE']
if not hm['INDEX_NAME'].startswith("RDB$"):
create_string += " using index " + hm['INDEX_NAME']
elif hm['CONSTRAINT_TYPE'].casefold() == "UNIQUE".casefold():
select_django_constraint_segment = """
select django$field_name as field_name from django$constraint_segment s
where s.django$constraint_name = '%s'
order by django$position
""" % hm['CONSTRAINT_NAME']
segments = []
with self.cursor() as cursor:
cursor.execute(select_django_constraint_segment)
for row in cursor.fetchall():
map = {}
for i, desc in enumerate(cursor.cursor.cursor.description):
map[desc[0]] = row[i]
segments.append(map)
field_list = []
for field in segments:
field_list.append('"' + field['FIELD_NAME'].strip() + '"')
create_string += " unique(" + ','.join(str(x) for x in field_list) + ")"
try:
editor.execute(create_string)
except Exception as e:
print(e)
try:
editor.execute("delete from django$constraint_segment")
editor.execute("delete from django$constraint")
except Exception as e:
print(e)
def table_exists(self, table_name):
"""
Checks whether a table with a specified name exists.
Args:
table_name (str): Table name for checking
"""
sql = """
select null from rdb$relations where rdb$system_flag=0 and rdb$view_blr is null and rdb$relation_name='%s'
""" % str(table_name).upper()
value = None
with self.cursor() as cursor:
cursor.execute(sql)
value = cursor.fetchone()
return True if value else False
def get_drop_constraints(self, query):
"""
Returns objects that should be dropped when restrictions are disabled.
Args:
query (str): SQL query to execute
.. important::
The server does not support explicit disabling of restrictions,
therefore, implementation is made using additional tables.
Note:
If there is an error when execute query an exception is thrown.
"""
value = []
with self.cursor() as cursor:
cursor.execute(query)
for row in cursor.fetchall():
map = {}
for i, desc in enumerate(cursor.cursor.cursor.description):
map[desc[0]] = row[i]
value.append(map)
return value
def _savepoint_allowed(self):
# Savepoints cannot be created outside a transaction
return self.features.uses_savepoints and not self.get_autocommit()
# #### Backend-specific transaction management methods #####
def _set_autocommit(self, autocommit):
"""
Backend-specific implementation to enable or disable autocommit.
FDB doesn't support auto-commit feature directly, but developers may
achieve the similar result using explicit transaction start, taking
advantage of default_action and its default value (commit).
See:
http://www.firebirdsql.org/file/documentation/drivers_documentation/python/fdb/usage-guide.html#auto-commit
Pay attention at _close() method below
"""
self.autocommit = autocommit
with self.wrap_database_errors:
self.connection.autocommit = autocommit
# #### Backend-specific wrappers for PEP-249 connection methods #####
def _close(self):
if self.connection is not None:
with self.wrap_database_errors:
if self.autocommit and self.connection.main_transaction.is_active():
self.connection.commit()
return self.connection.close()
# #### Connection termination handling #####
def is_usable(self):
"""
Tests if the database connection is usable.
This function may assume that self.connection is not None.
"""
try:
cur = self.connection.cursor()
cur.execute('SELECT 1 FROM RDB$DATABASE')
except DatabaseError:
return False
else:
return True
@cached_property
def server_version(self):
"""
Access method for engine_version property.
engine_version return a full version in string format
(ie: 'WI-V6.3.5.4926 Firebird 1.5' )
"""
if not self._server_version:
if not self.connection:
self.cursor()
self._server_version = self.connection.info.server_version
return self._server_version
class FirebirdCursorWrapper(object):
"""
Django uses "format" style placeholders, but firebird uses "qmark" style.
This fixes it -- but note that if you want to use a literal "%s" in a query,
you'll need to use "%%s".
"""
codes_for_integrityerror = (-803, -625, -530)
def close(self, *args, **kwargs): # real signature unknown
""" Closes the cursor. """
self.cursor.close()
def __init__(self, cursor, encoding, autocommit):
self.cursor = cursor
self.encoding = encoding
self.autocommit = autocommit
def execute(self, query, params=None):
if params is None:
params = []
try:
q = self.convert_query(query, len(params))
return self.cursor.execute(q, params)
except Database.IntegrityError as e:
raise utils.IntegrityError(*self.error_info(e, query, params))
except Database.DatabaseError as e:
# Map some error codes to IntegrityError, since they seem to be
# misclassified and Django would prefer the more logical place.
# fdb: raise exception as tuple with (error_msg, sqlcode, error_code)
code = self.get_sql_code(e)
if code in self.codes_for_integrityerror:
raise utils.IntegrityError(*self.error_info(e, query, params))
raise
def executemany(self, query, param_list):
try:
q = self.convert_query(query, len(param_list[0]))
return self.cursor.executemany(q, param_list)
except Database.IntegrityError as e:
raise utils.IntegrityError(*self.error_info(e, query, param_list[0]))
except Database.DatabaseError as e:
# Map some error codes to IntegrityError, since they seem to be
# misclassified and Django would prefer the more logical place.
# fdb: raise exception as tuple with (error_msg, sqlcode, error_code)
code = self.get_sql_code(e)
if code in self.codes_for_integrityerror:
raise utils.IntegrityError(*self.error_info(e, query, param_list[0]))
raise
def convert_query(self, query, num_params):
# kinterbasdb tries to convert the passed SQL to string.
# But if the connection charset is NONE, ASCII or OCTETS it will fail.
# So we convert it to string first.
if num_params == 0:
return smart_str(query, self.encoding)
return smart_str(query % tuple("?" * num_params), self.encoding)
def get_sql_code(self, e):
try:
sql_code = e.sqlcode
except IndexError:
sql_code = None
return sql_code
def error_info(self, e, q, p):
# fdb: raise exception as tuple with (error_msg, sqlcode, error_code)
# just when it uses exception_from_status function. Ticket #44.
try:
error_msg = e.args[0]
except IndexError:
error_msg = ''
try:
sql_code = e.sqlcode
except IndexError:
sql_code = None
try:
error_code = e.sqlstate
except IndexError:
error_code = None
if q:
sql_text = q % tuple(p)
else:
sql_text = q
return tuple([error_msg, sql_code, error_code, {'sql': sql_text, 'params': p}])
def __getattr__(self, attr):
if attr in self.__dict__:
return self.__dict__[attr]
else:
return getattr(self.cursor, attr)
def __iter__(self):
return iter(self.cursor)