Ticket #5052: db2_9.patch

File db2_9.patch, 41.3 KB (added by Koen Biermans <koen.biermans@…>, 17 years ago)

DB2 backend

Line 
1=== django/db/models/query.py
2==================================================================
3--- django/db/models/query.py (/local/django/db2_9) (revision 3643)
4
5+++ django/db/models/query.py (/mirror/django/trunk) (revision 3643)
6
7@@ -963,10 +963,7 @@
8
9 # Does the name belong to a defined many-to-many field?
10 field = find_field(name, current_opts.many_to_many, False)
11 if field:
12- if hasattr(backend, 'get_alias'):
13- new_table = backend.get_alias(current_table, name)
14- else:
15- new_table = current_table + '__' + name
16+ new_table = current_table + '__' + name
17 new_opts = field.rel.to._meta
18 new_column = new_opts.pk.column
19
20@@ -983,10 +980,7 @@
21
22 # Does the name belong to a reverse defined many-to-many field?
23 field = find_field(name, current_opts.get_all_related_many_to_many_objects(), True)
24 if field:
25- if hasattr(backend, 'get_alias'):
26- new_table = backend.get_alias(current_table, name)
27- else:
28- new_table = current_table + '__' + name
29+ new_table = current_table + '__' + name
30 new_opts = field.opts
31 new_column = new_opts.pk.column
32
33@@ -1003,10 +997,7 @@
34
35 # Does the name belong to a one-to-many field?
36 field = find_field(name, current_opts.get_all_related_objects(), True)
37 if field:
38- if hasattr(backend, 'get_alias'):
39- new_table = backend.get_alias(current_table, name)
40- else:
41- new_table = current_table + '__' + name
42+ new_table = table + '__' + name
43 new_opts = field.opts
44 new_column = field.field.column
45 join_column = opts.pk.column
46@@ -1020,10 +1011,7 @@
47
48 field = find_field(name, current_opts.fields, False)
49 if field:
50 if field.rel: # One-to-One/Many-to-one field
51- if hasattr(backend, 'get_alias'):
52- new_table = backend.get_alias(current_table, name)
53- else:
54- new_table = current_table + '__' + name
55+ new_table = current_table + '__' + name
56 new_opts = field.rel.to._meta
57 new_column = new_opts.pk.column
58 join_column = field.column
59=== django/db/backends/db2_9 (deleted directory)
60==================================================================
61=== django/db/backends/db2_9/base.py
62==================================================================
63--- django/db/backends/db2_9/base.py (/local/django/db2_9) (revision 3643)
64
65+++ django/db/backends/db2_9/base.py (/mirror/django/trunk) (revision 3643)
66
67@@ -1,624 +0,0 @@
68
69-"""
70
71-
72
73-IBM DB2 database backend for Django
74
75-
76
77-Requires PyDB2: http://sourceforge.net/projects/pydb2/
78
79-With this patch: http://sourceforge.net/tracker/index.php?func=detail&aid=1731609&group_id=67548&atid=518208
80
81-
82
83-Authors:
84
85- Javier Villavicencio
86
87- Koen Biermans
88
89-
90
91-Current issues:
92
93- DB2 does not support deferring foreign key constraint checking.
94
95- --> prereferencing does not work (e.g. in serializer_regress test)
96
97-
98
99- No regex lookups.
100
101-
102
103- Character sets:
104
105- put encoding/decoding into settings.DATABASE_CHARSET into the cursor code.
106
107-
108
109- Floats as primary key does not work.
110
111-
112
113-Specific items:
114
115- Connection:
116
117- use a DNS for DATABASE_NAME
118
119- this DNS can be system DNS or defined in db2cli.ini
120
121- example for db2cli.ini:
122
123- [DNSNAME]
124
125- Protocol=TCPIP
126
127- Hostname=DB2HOST
128
129- ServiceName=50001
130
131- Database=DATABASE
132
133- Port=50001
134
135-
136
137- Tables may be set in models.py with their schema: 'db_table = SCHEMA.TABLENAME'
138
139- --> modified query.py to use get_alias function (no dots allowed in alias,
140
141- default implementation uses db_table)
142
143-
144
145- DB2 table names may be 128 characters, but constraint names are limited to 18.
146
147- --> modified management.py to use get_max_constraint_length function
148
149-
150
151-"""
152
153-
154
155-from django.db.backends import util
156
157-import re
158
159-from django.utils.encoding import smart_str, force_unicode
160
161-
162
163-try:
164
165- import DB2 as Database
166
167-except ImportError, e:
168
169- from django.core.exceptions import ImproperlyConfigured
170
171- raise ImproperlyConfigured, "Error loading DB2 python module: %s" % e
172
173-import datetime
174
175-from django.utils.datastructures import SortedDict
176
177-
178
179-Warning = Database.Warning
180
181-Error = Database.Error
182
183-InterfaceError = Database.InterfaceError
184
185-DatabaseError = Database.DatabaseError
186
187-DataError = Database.DataError
188
189-OperationalError = Database.OperationalError
190
191-IntegrityError = Database.IntegrityError
192
193-InternalError = Database.InternalError
194
195-ProgrammingError = Database.ProgrammingError
196
197-NotSupportedError = Database.NotSupportedError
198
199-
200
201-class Cursor(Database.Cursor):
202
203- """Return a cursor.
204
205- Doing some translation tricks here.
206
207- Set the database charset in DATABASE_CHARSET setting"""
208
209- try:
210
211- charset = settings.DATABASE_CHARSET
212
213- except:
214
215- charset = 'iso-8859-1'
216
217-
218
219- def _rewrite_args(self, query, params=None):
220
221- # formatting parameters into charset
222
223- if params is None:
224
225- params = []
226
227- else:
228
229- params = self._format_params(params)
230
231- # formatting query into charset
232
233- query = smart_str(query, self.charset)
234
235- a = query.find('CREATE')
236
237- if a >= 0 and a < 10: # assuming this is a create command
238
239- # DB2 doesn't want a 'lone' NULL (it's implied).
240
241- query = re.sub('(?<!NOT) NULL', '', query)
242
243- # DB2 does not like primary key definition without NOT NULL
244
245- query = re.sub('(?<!NOT NULL) PRIMARY KEY', ' NOT NULL PRIMARY KEY',query)
246
247- # PyDB2 uses '?' as the parameter style.
248
249- query = query.replace("%s", "?")
250
251- return query, params
252
253-
254
255- def _format_params(self, params=None):
256
257- return self._smart_str(params)
258
259-
260
261- def execute(self, query, params=None):
262
263- query, params = self._rewrite_args(query, params)
264
265- try:
266
267- return Database.Cursor.execute(self, query, params)
268
269- except:
270
271- print 'Error executing query %s with params %s' % (query, params)
272
273- raise
274
275-
276
277- def executemany(self, query, params=None):
278
279- query, params = self._rewrite_args(query, params)
280
281- return Database.Cursor.executemany(self, query, params)
282
283-
284
285- def fetchone(self):
286
287- # force results into unicode
288
289- return self._force_unicode(Database.Cursor.fetchone(self))
290
291-
292
293- def fetchmany(self, size=None):
294
295- if size is None:
296
297- size = self.arraysize
298
299- # force results into unicode
300
301- return self._force_unicode(Database.Cursor.fetchmany(self, size))
302
303-
304
305- def fetchall(self):
306
307- # is this ever used ?
308
309- # force results into unicode
310
311- return self._force_unicode(Database.Cursor.fetchall(self))
312
313-
314
315- def _smart_str(self, s=None):
316
317- if s is None:
318
319- return s
320
321- if isinstance(s, dict):
322
323- result = {}
324
325- for key, value in s.items():
326
327- result[_smart_str(key)] = self._smart_str(value)
328
329- return result
330
331- elif isinstance(s, (tuple,list)):
332
333- return tuple([self._smart_str(p) for p in s])
334
335- else:
336
337- if isinstance(s, basestring):
338
339- try:
340
341- return smart_str(s, self.charset, True)
342
343- except UnicodeEncodeError:
344
345- return ''
346
347- return s
348
349-
350
351- def _force_unicode(self,s=None):
352
353- if s is None:
354
355- return s
356
357- if isinstance(s, dict):
358
359- result = {}
360
361- for key, value in s.items():
362
363- result[force_unicode(key, charset)] = self._force_unicode(value, charset)
364
365- return result
366
367- elif isinstance(s, (tuple,list)):
368
369- return tuple([self._force_unicode(p) for p in s])
370
371- else:
372
373- if isinstance(s, basestring):
374
375- try:
376
377- return force_unicode(s, encoding=self.charset)
378
379- except UnicodeEncodeError:
380
381- return u''
382
383- return s
384
385-
386
387-class Connection(Database.Connection):
388
389- def cursor(self):
390
391- return Cursor(self._db.cursor())
392
393-
394
395-Database.connect = Connection
396
397-
398
399-class DatabaseWrapper(object):
400
401- def __init__(self, **kwargs):
402
403- self.connection = None
404
405- self.queries = []
406
407- self.server_version = None
408
409- self.options = kwargs
410
411-
412
413- def _valid_connection(self):
414
415- return self.connection is not None
416
417-
418
419- def cursor(self):
420
421- from django.conf import settings
422
423- from warnings import filterwarnings
424
425- if self.connection is None:
426
427- conn_dict = {}
428
429- # A DB2 client is configured with nodes, and then with databases connected
430
431- # to these nodes, I don't know if there is a way of specifying a complete
432
433- # DSN with a hostname and port, the PyDB2 module shows no sign of this either.
434
435- # So this DSN is actually the database name configured in the host's client instance.
436
437- if settings.DATABASE_NAME == '':
438
439- from django.core.exceptions import ImproperlyConfigured
440
441- raise ImproperlyConfigured, "You need to specify DATABASE_NAME in your Django settings file."
442
443- conn_dict['dsn'] = settings.DATABASE_NAME
444
445- if settings.DATABASE_USER == '':
446
447- from django.core.exceptions import ImproperlyConfigured
448
449- raise ImproperlyConfigured, "You need to specify DATABASE_USER in your Django settings file."
450
451- conn_dict['uid'] = settings.DATABASE_USER
452
453- if settings.DATABASE_PASSWORD == '':
454
455- from django.core.exceptions import ImproperlyConfigured
456
457- raise ImproperlyConfigured, "You need to specify DATABASE_PASSWORD in your Django settings file."
458
459- conn_dict['pwd'] = settings.DATABASE_PASSWORD
460
461- # Just imitating others here, I haven't seen any "options" for DB2.
462
463- conn_dict.update(self.options)
464
465- self.connection = Database.connect(**conn_dict)
466
467- cursor = self.connection.cursor()
468
469- else:
470
471- cursor = self.connection.cursor()
472
473- if settings.DEBUG:
474
475- return util.CursorDebugWrapper(cursor, self)
476
477- return cursor
478
479-
480
481- def _commit(self):
482
483- if self.connection is not None:
484
485- return self.connection.commit()
486
487-
488
489- def _rollback(self):
490
491- if self.connection is not None:
492
493- return self.connection.rollback()
494
495-
496
497- def close(self):
498
499- if self.connection is not None:
500
501- self.connection.close()
502
503- self.connection = None
504
505-
506
507-allows_group_by_ordinal = False
508
509-allows_unique_and_pk = True
510
511-needs_datetime_string_cast = True
512
513-needs_upper_for_iops = True
514
515-autoindexes_primary_keys = True
516
517-supports_constraints = True
518
519-supports_tablespaces = False
520
521-supports_compound_statements = True
522
523-uses_case_insensitive_names = True
524
525-
526
527-def quote_name(name):
528
529- """Name quoting.
530
531- Names of type schema.tablename become "schema"."tablename"."""
532
533- from django.conf import settings
534
535- if not name.startswith('"') and not name.endswith('"'):
536
537- return '.'.join(['"%s"' % util.truncate_name(f.upper(), get_max_name_length()) for f in name.split('.')])
538
539- return name.upper()
540
541-
542
543-dictfetchone = util.dictfetchone
544
545-dictfetchmany = util.dictfetchmany
546
547-dictfetchall = util.dictfetchall
548
549-
550
551-def get_last_insert_id(cursor, table_name, pk_name):
552
553- # There is probably a 'better' way to do this.
554
555- cursor.execute("SELECT MAX(%s) FROM %s" % (quote_name(pk_name), quote_name(table_name)))
556
557- a = cursor.fetchone()
558
559- if a is None:
560
561- return 0
562
563- return a[0]
564
565-
566
567-def get_date_extract_sql(lookup_type, field_name):
568
569- # lookup_type is 'year', 'month', 'day'
570
571- if lookup_type == 'year':
572
573- return "YEAR(%s)" % field_name
574
575- elif lookup_type == 'month':
576
577- return "MONTH(%s)" % field_name
578
579- elif lookup_type == 'day':
580
581- return "DAY(%s)" % field_name
582
583-
584
585-def get_date_trunc_sql(lookup_type, field_name):
586
587- # lookup_type is 'year', 'month', 'day'
588
589- # Hard one. I have seen TRUNC_TIMESTAMP somewhere but is not present
590
591- # in any of my databases.
592
593- # Doesn't work 'directly' since the GROUP BY needs this as well,
594
595- # DB2 can't take "GROUP BY 1"
596
597- if lookup_type == 'year':
598
599- return "TIMESTAMP(DATE(%s -DAYOFYEAR(%s) DAYS +1 DAY),'00:00:00')" % (field_name, field_name)
600
601- elif lookup_type == 'month':
602
603- return "TIMESTAMP(DATE(%s -DAY(%s) DAYS +1 DAY),'00:00:00')" % (field_name, field_name)
604
605- elif lookup_type == 'day':
606
607- return "TIMESTAMP(DATE(%s),'00:00:00')" % field_name
608
609-
610
611-def get_datetime_cast_sql():
612
613- return ""
614
615-
616
617-def get_limit_offset_sql(limit, offset=None):
618
619- # Limits and offset are too complicated to be handled here.
620
621- # Instead, they are handled in DB2QuerySet.
622
623- return ""
624
625-
626
627-def get_random_function_sql():
628
629- return "RAND()"
630
631-
632
633-def get_deferrable_sql():
634
635- # DB2 does not support deferring constraints to end of transaction
636
637- # using cascade avoid constraint problems
638
639- return " ON DELETE CASCADE"
640
641-
642
643-def get_fulltext_search_sql(field_name):
644
645- # DB2 has some nice extra packages that enables a CONTAINS() function,
646
647- # but they're not available in my dbs.
648
649- raise NotImplementedError
650
651-
652
653-def get_drop_foreignkey_sql():
654
655- return "DROP CONSTRAINT"
656
657-
658
659-def get_pk_default_value():
660
661- return "DEFAULT"
662
663-
664
665-def get_max_name_length():
666
667- return 128;
668
669-
670
671-def get_max_constraint_length():
672
673- # This needs a patch of management.py to detect and use this function
674
675- # 18 for primarykeys and constraint names
676
677- return 18;
678
679-
680
681-def get_alias(table, column):
682
683- if table.count('.')==0:
684
685- return "%s__%s" % (table, column)
686
687- return "%s__%s" % (table.split('.')[-1], column)
688
689-
690
691-def get_start_transaction_sql():
692
693- return "BEGIN;"
694
695-
696
697-def get_autoinc_sql(table):
698
699- return None
700
701-
702
703-def get_drop_sequence(table):
704
705- return "DROP SEQUENCE %s;" % quote_name(get_sequence_name(table))
706
707-
708
709-def _get_sequence_reset_sql():
710
711- return 'ALTER TABLE %s ALTER COLUMN %s RESTART WITH %s'
712
713-
714
715-def _get_sql_flush(style, table, emptytables):
716
717- """ Recursive function to retrieve the list of delete statements in the
718
719- right order.
720
721- """
722
723- from django.db.backends.db2_9.introspection import get_foreignkey_relations
724
725- from django.db import connection
726
727- from django.utils.encoding import smart_str
728
729- cursor = connection.cursor()
730
731- sql = []
732
733- te = []
734
735- if emptytables:
736
737- temptytables = emptytables[:]
738
739- else:
740
741- temptytables = []
742
743- relations = get_foreignkey_relations(cursor, table)
744
745- for relation in relations:
746
747- relation = smart_str(relation)
748
749- if relation not in temptytables:
750
751- if temptytables:
752
753- tt2 = temptytables[:].extend(table) # avoid circular reference endless loop
754
755- else:
756
757- tt2 = [table]
758
759- tsql, tem = _get_sql_flush(style, relation, tt2)
760
761- sql.extend(tsql)
762
763- te.extend(tem)
764
765- temptytables.extend(tem)
766
767- if table not in temptytables:
768
769- sql.append('%s %s %s;' % (style.SQL_KEYWORD('DELETE'), style.SQL_KEYWORD('FROM'),
770
771- style.SQL_FIELD(quote_name(table))))
772
773- te.append(table)
774
775- return sql, te
776
777-
778
779-def get_sql_flush(style, tables, sequences):
780
781- """Return a list of SQL statements required to remove all data from
782
783- all tables in the database (without actually removing the tables
784
785- themselves) and put the database in an empty 'initial' state"""
786
787- if tables:
788
789- emptytables = []
790
791- sql = []
792
793- for table in tables:
794
795- if table.count('.') == 0:
796
797- if table not in emptytables:
798
799- tsql, te = _get_sql_flush(style, table, emptytables)
800
801- sql.extend(tsql)
802
803- emptytables.extend(te)
804
805- for sequence_info in sequences:
806
807- if sequence_info['table'].upper() == table.upper():
808
809- column = sequence_info['column']
810
811- if column is not None:
812
813- query = _get_sequence_reset_sql() % (quote_name(table),quote_name(column),1)
814
815- sql.append(query)
816
817- return sql
818
819- else:
820
821- return []
822
823-
824
825-def get_sql_sequence_reset(style, model_list):
826
827- "Returns a list of the SQL statements to reset sequences for the given models."
828
829- from django.db import models
830
831- from django.db import connection
832
833- output = []
834
835- query = _get_sequence_reset_sql()
836
837- for model in model_list:
838
839- for f in model._meta.fields:
840
841- if isinstance(f, models.AutoField):
842
843- cursor = connection.cursor()
844
845- max_id = get_last_insert_id(cursor, model._meta.db_table, f.column) + 1
846
847- output.append(query % (quote_name(model._meta.db_table), quote_name(f.column), max_id))
848
849- cursor.close()
850
851- cursor = None
852
853- break # Only one AutoField is allowed per model, so don't bother continuing.
854
855- #~ for f in model._meta.many_to_many:
856
857- #~ cursor = connection.cursor()
858
859- #~ max_id = get_last_insert_id(cursor, model._meta.db_table, f.column) + 1
860
861- #~ output.append(query % (quote_name(f.m2m_db_table()), quote_name(f.m2m_column_name()), max_id))
862
863- return output
864
865-
866
867-def get_query_set_class(DefaultQuerySet):
868
869- "Create a custom QuerySet class for DB2."
870
871-
872
873- from django.db import backend, connection
874
875- from django.db.models.query import EmptyResultSet, GET_ITERATOR_CHUNK_SIZE, quote_only_if_word
876
877-
878
879- class DB2QuerySet(DefaultQuerySet):
880
881-
882
883- def iterator(self):
884
885- "Performs the SELECT database lookup of this QuerySet."
886
887-
888
889- from django.db.models.query import get_cached_row
890
891-
892
893- # self._select is a dictionary, and dictionaries' key order is
894
895- # undefined, so we convert it to a list of tuples.
896
897- extra_select = self._select.items()
898
899-
900
901- full_query = None
902
903-
904
905- try:
906
907- try:
908
909- select, sql, params, full_query = self._get_sql_clause(get_full_query=True)
910
911- except TypeError:
912
913- select, sql, params = self._get_sql_clause()
914
915- except EmptyResultSet:
916
917- raise StopIteration
918
919- if not full_query:
920
921- full_query = "SELECT %s%s\n%s" % \
922
923- ((self._distinct and "DISTINCT " or ""),
924
925- ', '.join(select), sql)
926
927-
928
929- cursor = connection.cursor()
930
931- cursor.execute(full_query, params)
932
933-
934
935- fill_cache = self._select_related
936
937- fields = self.model._meta.fields
938
939- index_end = len(fields)
940
941-
942
943- # so here's the logic;
944
945- # 1. retrieve each row in turn
946
947- # 2. convert NCLOBs
948
949-
950
951- while 1:
952
953- rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE)
954
955- if not rows:
956
957- raise StopIteration
958
959- for row in rows:
960
961- row = self.resolve_columns(row, fields)
962
963- if fill_cache:
964
965- obj, index_end = get_cached_row(klass=self.model, row=row,
966
967- index_start=0, max_depth=self._max_related_depth)
968
969- else:
970
971- obj = self.model(*row[:index_end])
972
973- for i, k in enumerate(extra_select):
974
975- setattr(obj, k[0], row[index_end+i])
976
977- yield obj
978
979-
980
981- def _get_sql_clause(self, get_full_query=False):
982
983- from django.db.models.query import fill_table_cache, \
984
985- handle_legacy_orderlist, orderfield2column
986
987-
988
989- opts = self.model._meta
990
991-
992
993- select = ["%s.%s" % (backend.quote_name(opts.db_table), backend.quote_name(f.column)) for f in opts.fields]
994
995- tables = [quote_only_if_word(t) for t in self._tables]
996
997- joins = SortedDict()
998
999- where = self._where[:]
1000
1001- params = self._params[:]
1002
1003-
1004
1005- # Convert self._filters into SQL.
1006
1007- joins2, where2, params2 = self._filters.get_sql(opts)
1008
1009- joins.update(joins2)
1010
1011- where.extend(where2)
1012
1013- params.extend(params2)
1014
1015-
1016
1017- # Add additional tables and WHERE clauses based on select_related.
1018
1019- if self._select_related:
1020
1021- fill_table_cache(opts, select, tables, where, opts.db_table, [opts.db_table])
1022
1023-
1024
1025- # Add any additional SELECTs.
1026
1027- if self._select:
1028
1029- select.extend(['(%s) AS %s' % (quote_only_if_word(s[1]), backend.quote_name(s[0])) for s in self._select.items()])
1030
1031-
1032
1033- # Start composing the body of the SQL statement.
1034
1035- sql = [" FROM", backend.quote_name(opts.db_table)]
1036
1037-
1038
1039- # Compose the join dictionary into SQL describing the joins.
1040
1041- if joins:
1042
1043- sql.append(" ".join(["%s %s %s ON %s" % (join_type, table, alias, condition)
1044
1045- for (alias, (table, join_type, condition)) in joins.items()]))
1046
1047-
1048
1049- # Compose the tables clause into SQL.
1050
1051- if tables:
1052
1053- sql.append(", " + ", ".join(tables))
1054
1055-
1056
1057- # Compose the where clause into SQL.
1058
1059- if where:
1060
1061- sql.append(where and "WHERE " + " AND ".join(where))
1062
1063-
1064
1065- # ORDER BY clause
1066
1067- order_by = []
1068
1069- if self._order_by is not None:
1070
1071- ordering_to_use = self._order_by
1072
1073- else:
1074
1075- ordering_to_use = opts.ordering
1076
1077- for f in handle_legacy_orderlist(ordering_to_use):
1078
1079- if f == '?': # Special case.
1080
1081- order_by.append(backend.get_random_function_sql())
1082
1083- else:
1084
1085- if f.startswith('-'):
1086
1087- col_name = f[1:]
1088
1089- order = "DESC"
1090
1091- else:
1092
1093- col_name = f
1094
1095- order = "ASC"
1096
1097- if "." in col_name:
1098
1099- table_prefix, col_name = col_name.split('.', 1)
1100
1101- table_prefix = backend.quote_name(table_prefix) + '.'
1102
1103- else:
1104
1105- # Use the database table as a column prefix if it wasn't given,
1106
1107- # and if the requested column isn't a custom SELECT.
1108
1109- if "." not in col_name and col_name not in (self._select or ()):
1110
1111- table_prefix = backend.quote_name(opts.db_table) + '.'
1112
1113- else:
1114
1115- table_prefix = ''
1116
1117- order_by.append('%s%s %s' % (table_prefix, backend.quote_name(orderfield2column(col_name, opts)), order))
1118
1119- if order_by:
1120
1121- sql.append("ORDER BY " + ", ".join(order_by))
1122
1123-
1124
1125- # Look for column name collisions in the select elements
1126
1127- # and fix them with an AS alias. This allows us to do a
1128
1129- # SELECT * later in the paging query.
1130
1131- cols = [clause.split('.')[-1] for clause in select]
1132
1133- for index, col in enumerate(cols):
1134
1135- if cols.count(col) > 1:
1136
1137- col = '%s%d' % (col.replace('"', ''), index)
1138
1139- cols[index] = col
1140
1141- select[index] = '%s AS %s' % (select[index], col)
1142
1143-
1144
1145- # LIMIT and OFFSET clauses
1146
1147- # To support limits and offsets, Oracle requires some funky rewriting of an otherwise normal looking query.
1148
1149- select_clause = ",".join(select)
1150
1151- distinct = (self._distinct and "DISTINCT " or "")
1152
1153-
1154
1155- if order_by:
1156
1157- order_by_clause = " OVER (ORDER BY %s )" % (", ".join(order_by))
1158
1159- else:
1160
1161- #DB2's row_number() function always requires an order-by clause.
1162
1163- #So we need to define a default order-by, since none was provided.
1164
1165- order_by_clause = " OVER (ORDER BY %s.%s)" % \
1166
1167- (backend.quote_name(opts.db_table),
1168
1169- backend.quote_name(opts.fields[0].db_column or opts.fields[0].column))
1170
1171- # limit_and_offset_clause
1172
1173- if self._limit is None:
1174
1175- assert self._offset is None, "'offset' is not allowed without 'limit'"
1176
1177-
1178
1179- if self._offset is not None:
1180
1181- offset = int(self._offset)
1182
1183- else:
1184
1185- offset = 0
1186
1187- if self._limit is not None:
1188
1189- limit = int(self._limit)
1190
1191- else:
1192
1193- limit = None
1194
1195-# if limit == 0:
1196
1197-# limit = None
1198
1199- limit_and_offset_clause = ''
1200
1201- if limit is not None:
1202
1203- limit_and_offset_clause = "WHERE rn > %s AND rn <= %s" % (offset, limit+offset)
1204
1205- elif offset:
1206
1207- limit_and_offset_clause = "WHERE rn > %s" % (offset)
1208
1209-
1210
1211- if len(limit_and_offset_clause) > 0:
1212
1213- if limit is not None:
1214
1215- fmt = "SELECT * FROM (SELECT %s%s, ROW_NUMBER()%s AS rn %s FETCH FIRST %s ROWS ONLY) AS foo %s"
1216
1217- full_query = fmt % (distinct, select_clause,
1218
1219- order_by_clause, ' '.join(sql).strip(), limit+offset,
1220
1221- limit_and_offset_clause)
1222
1223- else:
1224
1225- fmt = "SELECT * FROM (SELECT %s%s, ROW_NUMBER()%s AS rn %s ) AS foo %s"
1226
1227- full_query = fmt % (distinct, select_clause,
1228
1229- order_by_clause, ' '.join(sql).strip(),
1230
1231- limit_and_offset_clause)
1232
1233-
1234
1235- else:
1236
1237- full_query = None
1238
1239-
1240
1241- if get_full_query:
1242
1243- return select, " ".join(sql), params, full_query
1244
1245- else:
1246
1247- return select, " ".join(sql), params
1248
1249-
1250
1251- def resolve_columns(self, row, fields=()):
1252
1253- from django.db.models.fields import Field, CharField, BooleanField, TextField
1254
1255- values = []
1256
1257- for value, field in map(None, row, fields):
1258
1259- # strip trailing spaces in char and text fields
1260
1261- #if isinstance(field, (CharField, TextField,)):
1262
1263- if isinstance(value, basestring):
1264
1265- if value:
1266
1267- value = value.strip()
1268
1269- # create real booleans
1270
1271- if isinstance(field, BooleanField):
1272
1273- value = {0: False, 1: True}.get(value, False)
1274
1275- values.append(value)
1276
1277- return values
1278
1279-
1280
1281- return DB2QuerySet
1282
1283-
1284
1285-# UPPER needs typecasting or DB2 does not know which upper function to use
1286
1287-# it does not matter if the typecast is correct
1288
1289-OPERATOR_MAPPING = {
1290
1291- 'exact': "= %s",
1292
1293- 'iexact': "= UPPER(CAST(%s AS VARCHAR(50)))",
1294
1295- 'contains': "LIKE %s ESCAPE '\\'",
1296
1297- 'icontains': "LIKE UPPER(CAST(%s AS VARCHAR(50))) ESCAPE '\\'",
1298
1299- 'gt': "> %s",
1300
1301- 'gte': ">= %s",
1302
1303- 'lt': "< %s",
1304
1305- 'lte': "<= %s",
1306
1307- 'startswith': "LIKE %s ESCAPE '\\'",
1308
1309- 'endswith': "LIKE %s ESCAPE '\\'",
1310
1311- 'istartswith': "LIKE UPPER(CAST(%s AS VARCHAR(50))) ESCAPE '\\'",
1312
1313- 'iendswith': "LIKE UPPER(CAST(%s AS VARCHAR(50))) ESCAPE '\\'",
1314
1315-}
1316
1317=== django/db/backends/db2_9/client.py
1318==================================================================
1319--- django/db/backends/db2_9/client.py (/local/django/db2_9) (revision 3643)
1320
1321+++ django/db/backends/db2_9/client.py (/mirror/django/trunk) (revision 3643)
1322
1323@@ -1,7 +0,0 @@
1324
1325-from django.conf import settings
1326-import os
1327-
1328-def runshell():
1329- # Nothing fancy here, as the environment should have the
1330- # instnace properly configured for anything to work at all.
1331- os.execvp('db2')
1332=== django/db/backends/db2_9/__init__.py
1333==================================================================
1334=== django/db/backends/db2_9/introspection.py
1335==================================================================
1336--- django/db/backends/db2_9/introspection.py (/local/django/db2_9) (revision 3643)
1337
1338+++ django/db/backends/db2_9/introspection.py (/mirror/django/trunk) (revision 3643)
1339
1340@@ -1,177 +0,0 @@
1341
1342-"""
1343
1344-Optional: use a DATABASE_SCHEMA setting to let inspectdb find only tables from
1345
1346-a specific schema.
1347
1348-"""
1349
1350-from django.db.backends.db2_9.base import quote_name
1351
1352-from django.conf import settings
1353
1354-
1355
1356-def get_table_list(cursor):
1357
1358- """ Here's the tricky part. The tables are accessed by their schema
1359
1360- for example, all the systems tables are prefixed by SYSIBM as their schema.
1361
1362- Since we can get really *lots* of tables without filtering by creator this can get
1363
1364- really messy, So I've used DATABASE_SCHEMA to filter it.
1365
1366- RTRIMs are needed because the fields return in full column size filled with spaces.
1367
1368- Using the SYSIBM.TABLES VIEW.
1369
1370- """
1371
1372- from django.conf import settings
1373
1374- from django.utils.encoding import smart_str, force_unicode
1375
1376- try:
1377
1378- schema = settings.DATABASE_SCHEMA.upper()
1379
1380- except:
1381
1382- schema = ''
1383
1384- if schema != '':
1385
1386- cursor.execute("""SELECT RTRIM(table_name) as TABLES, RTRIM(table_schema) as SCHEMA FROM SYSIBM.TABLES
1387
1388- WHERE TABLE_SCHEMA = %s AND TABLE_TYPE = 'BASE TABLE' ORDER BY table_name""", [schema])
1389
1390- else: # Fallback to everything but SYS*
1391
1392- cursor.execute("""SELECT RTRIM(table_name) as TABLES, RTRIM(table_schema) as SCHEMA FROM SYSIBM.tables
1393
1394- WHERE table_schema NOT LIKE 'SYS%%' AND TABLE_TYPE = 'BASE TABLE' ORDER BY table_name""", [])
1395
1396- rows = cursor.fetchall()
1397
1398- res = []
1399
1400- # for tables from the user: do not append schema, others: use SCHEMA.TABLE as name
1401
1402- for row in rows:
1403
1404- if row[1].upper() == settings.DATABASE_USER.upper():
1405
1406- res.append(smart_str(row[0].upper()))
1407
1408- else:
1409
1410- res.append(smart_str("%s.%s" % (row[1].upper(), row[0].upper())))
1411
1412- return res
1413
1414-
1415
1416-def get_table_description(cursor, table_name):
1417
1418- "Returns a description of the table with the DB-API cursor.description interface."
1419
1420- cursor.execute("SELECT * FROM %s FETCH FIRST 1 ROWS ONLY" % (table_name,))
1421
1422- return cursor.description
1423
1424-
1425
1426-def _name_to_index(cursor, table_name):
1427
1428- """
1429
1430- Returns a dictionary of {field_name: field_index} for the given table.
1431
1432- Indexes are 0-based.
1433
1434- """
1435
1436- return dict([(d[0], i) for i, d in enumerate(get_table_description(cursor, table_name))])
1437
1438-
1439
1440-def get_relations(cursor, table_name):
1441
1442- """
1443
1444- Returns a dictionary of {field_index: (field_index_other_table, other_table)}
1445
1446- representing all relationships to the given table. Indexes are 0-based.
1447
1448- Beware, PyDB2 returns the full length of the field, filled with spaces,
1449
1450- I have to .strip() every single string field >.<
1451
1452- """
1453
1454- from django.conf import settings
1455
1456-
1457
1458- if table_name.count('.') == 1:
1459
1460- schema, tn = table_name.split('.')
1461
1462- else:
1463
1464- tn = table_name
1465
1466- schema = settings.DATABASE_USER
1467
1468- cursor.execute("""SELECT fk.ORDINAL_POSITION, pk.ORDINAL_POSITION, pk.TABLE_NAME
1469
1470- FROM SYSIBM.SQLFOREIGNKEYS r, SYSIBM.SQLCOLUMNS fk, SYSIBM.SQLCOLUMNS pk
1471
1472- WHERE r.FKTABLE_SCHEM = %s AND r.FKTABLE_NAME = %s
1473
1474- AND r.FKTABLE_SCHEM = fk.TABLE_SCHEM AND r.FKTABLE_NAME = fk.TABLE_NAME
1475
1476- AND r.FKCOLUMN_NAME = fk.COLUMN_NAME
1477
1478- AND r.PKTABLE_SCHEM = pk.TABLE_SCHEM AND r.PKTABLE_NAME = pk.TABLE_NAME
1479
1480- AND r.PKCOLUMN_NAME = pk.COLUMN_NAME
1481
1482- """, (schema.upper(), tn))
1483
1484-
1485
1486- relations = {}
1487
1488- for row in cursor.fetchall():
1489
1490- relations[int(row[0]) - 1] = (int(row[1]) -1, row[2].strip())
1491
1492- return relations
1493
1494-
1495
1496-def get_foreignkey_relations(cursor, table_name):
1497
1498- """
1499
1500- Returns a dictionary of {field_index: other_table}
1501
1502- representing all relationships to other tables. Indexes are 0-based.
1503
1504- Beware, PyDB2 returns the full length of the field, filled with spaces,
1505
1506- I have to .strip() every single string field >.<
1507
1508- """
1509
1510- from django.conf import settings
1511
1512-
1513
1514- if table_name.count('.') == 1:
1515
1516- schema, tn = table_name.split('.')
1517
1518- else:
1519
1520- tn = table_name
1521
1522- schema = settings.DATABASE_USER
1523
1524- cursor.execute("""SELECT r.FKCOLUMN_NAME, r.PKTABLE_NAME
1525
1526- FROM SYSIBM.SQLFOREIGNKEYS r
1527
1528- WHERE r.FKTABLE_SCHEM = %s AND r.FKTABLE_NAME = %s
1529
1530- """, (schema.upper(), tn))
1531
1532-
1533
1534- relations = []
1535
1536- for row in cursor.fetchall():
1537
1538- relations.append(row[1].strip())
1539
1540- return relations
1541
1542-
1543
1544-def get_indexes(cursor, table_name):
1545
1546- """
1547
1548- Returns a dictionary of fieldname -> infodict for the given table,
1549
1550- where each infodict is in the format:
1551
1552- {'primary_key': boolean representing whether it's the primary key,
1553
1554- 'unique': boolean representing whether it's a unique index}
1555
1556- DB2 is weird here, like this seems to be ok but I don't get 100% of the indexes
1557
1558- syscolumns.keyseq means the column part of a "parent key".
1559
1560- sysindexes.uniquerule == P means it's primary, U == unique,
1561
1562- and D == duplicates allowed.
1563
1564- """
1565
1566- from django.conf import settings
1567
1568- if table_name.count('.') == 1:
1569
1570- schema, tn = table_name.split('.')
1571
1572- else:
1573
1574- tn = table_name
1575
1576- schema = settings.DATABASE_USER
1577
1578- cursor.execute("""SELECT c.COLUMN_NAME, i.UNIQUERULE
1579
1580- FROM SYSIBM.SQLCOLUMNS c, SYSCAT.INDEXES i, SYSCAT.INDEXCOLUSE k
1581
1582- WHERE c.TABLE_SCHEM = %s AND c.TABLE_NAME = %s
1583
1584- AND C.TABLE_SCHEM = i.TABSCHEMA AND c.TABLE_NAME = i.TABNAME
1585
1586- AND i.INDSCHEMA = k.INDSCHEMA AND i.INDNAME = k.INDNAME
1587
1588- AND c.COLUMN_NAME = k.COLNAME
1589
1590- """, (schema.upper(), tn))
1591
1592-
1593
1594- indexes = {}
1595
1596- for row in cursor.fetchall():
1597
1598- urule = row[1].strip()
1599
1600- name = row[0].strip()
1601
1602- if urule == "P":
1603
1604- # Override everything, as this is a primary key.
1605
1606- indexes[name] = {'primary_key':True, 'unique':False}
1607
1608- elif urule == "U":
1609
1610- try:
1611
1612- if indexes[name]['primary_key'] == True:
1613
1614- # Can appear twice, but primary is primary anyway.
1615
1616- continue
1617
1618- else:
1619
1620- indexes[name] = {'primary_key':False, 'unique':True}
1621
1622- except: # TODO: Only a keyerror can happen here, right?
1623
1624- indexes[name] = {'primary_key':False, 'unique':True}
1625
1626- else: # urule = "D" not sure if there are others.
1627
1628- try:
1629
1630- # Should never happen, but...
1631
1632- if indexes[name]['primary_key'] == True or indexes[name]['unique'] == True:
1633
1634- continue
1635
1636- else:
1637
1638- indexes[name] = {'primary_key':False, 'unique':False}
1639
1640- except: # TODO: same as above ^_^
1641
1642- indexes[name] = {'primary_key':False, 'unique':False}
1643
1644- return indexes
1645
1646-
1647
1648-DATA_TYPES_REVERSE = {
1649
1650- -99:'TextField', # CLOB
1651
1652- -98:'TextField', # BLOB
1653
1654- -97:'TextField', # Long VarGraphic
1655
1656- -96:'TextField', # VarGraphic
1657
1658- -95:'TextField', # Graphic
1659
1660- -5: 'IntegerField', # Big Int
1661
1662- -4: 'TextField', # Binary Long VarChar
1663
1664- -3: 'TextField', # Binary VarChar
1665
1666- -2: 'TextField', # Binary
1667
1668- -1: 'TextField', # Long VarChar
1669
1670- 1: 'CharField', # Char
1671
1672- 2: 'FloatField', # Numeric
1673
1674- 3: 'FloatField', # Decimal
1675
1676- 4: 'IntegerField', # Integer
1677
1678- 5: 'BooleanField', # SmallInt
1679
1680- 6: 'FloatField', # Float
1681
1682- 7: 'FloatField', # Real
1683
1684- 8: 'FloatField', # Double
1685
1686- 12: 'CharField', # VarChar
1687
1688- 91: 'DateField', # Date
1689
1690- 92: 'TimeField', # Time
1691
1692- 93: 'DateTimeField', # TimeStamp
1693
1694-}
1695
1696=== django/db/backends/db2_9/creation.py
1697==================================================================
1698--- django/db/backends/db2_9/creation.py (/local/django/db2_9) (revision 3643)
1699
1700+++ django/db/backends/db2_9/creation.py (/mirror/django/trunk) (revision 3643)
1701
1702@@ -1,80 +0,0 @@
1703
1704-"""
1705
1706-TEST_DATABASE_USER must be an existing user (DB2 v9 uses OS authentication)
1707
1708-"""
1709
1710-import sys, time
1711
1712-from django.core import management
1713
1714-from django.db.models.signals import pre_save
1715
1716-from django.db.backends.db2_9 import base
1717
1718-
1719
1720-# Not quite sure about the Boolean and Float fields.
1721
1722-# Not sure what to use for Boolean, since there seems to be a 'BOOLEAN' type,
1723
1724-# but it isn't 'documented' anywhere. For Float, we have DOUBLE, DECIMAL and NUMERIC
1725
1726-# to choose from :+/, too many.
1727
1728-DATA_TYPES = {
1729
1730- 'AutoField': 'INTEGER GENERATED BY DEFAULT AS IDENTITY', # ALWAYS breaks basic test (specifying key explicitly)
1731
1732- 'BooleanField': 'SMALLINT',
1733
1734- 'CharField': 'VARCHAR(%(maxlength)s)',
1735
1736- 'CommaSeparatedIntegerField': 'VARCHAR(%(maxlength)s)',
1737
1738- 'DateField': 'DATE',
1739
1740- 'DateTimeField': 'TIMESTAMP',
1741
1742- 'DecimalField': 'DECIMAL(%(max_digits)s, %(decimal_places)s)',
1743
1744- 'FileField': 'VARCHAR(100)',
1745
1746- 'FilePathField': 'VARCHAR(100)',
1747
1748- 'FloatField': 'FLOAT',
1749
1750- 'ImageField': 'VARCHAR(100)',
1751
1752- 'IntegerField': 'INTEGER',
1753
1754- 'IPAddressField': 'CHAR(15)',
1755
1756- 'ManyToManyField': None,
1757
1758- 'NullBooleanField': 'SMALLINT',
1759
1760- 'OneToOneField': 'INTEGER',
1761
1762- 'PhoneNumberField': 'CHAR(20)',
1763
1764- 'PositiveIntegerField': 'INTEGER',
1765
1766- 'PositiveSmallIntegerField': 'SMALLINT',
1767
1768- 'SlugField': 'VARCHAR(%(maxlength)s)',
1769
1770- 'SmallIntegerField': 'SMALLINT',
1771
1772- 'TextField': 'LONG VARCHAR',
1773
1774- 'TimeField': 'TIME',
1775
1776- 'USStateField': 'CHAR(2)',
1777
1778-}
1779
1780-
1781
1782-REMEMBER = {}
1783
1784-
1785
1786-def create_test_db(settings, connection, backend, verbosity=1, autoclobber=False):
1787
1788- """Assuming here that a TEST_DATABASE_USER is set in settings that is known to the OS and DB2
1789
1790- with DB2ADM rights.
1791
1792- Best is that this user has a separate default tablespace defined in DB2 (but this is not required)."""
1793
1794- REMEMBER['user'] = settings.DATABASE_USER
1795
1796- REMEMBER['passwd'] = settings.DATABASE_PASSWORD
1797
1798- settings.DATABASE_USER = settings.TEST_DATABASE_USER
1799
1800- settings.DATABASE_PASSWORD = settings.TEST_DATABASE_PASSWORD
1801
1802- management.syncdb(verbosity, interactive=False)
1803
1804-
1805
1806-def destroy_test_db(settings, connection, backend, old_database_name, verbosity=1):
1807
1808- """Delete all tables from the test user."""
1809
1810- connection.close()
1811
1812-
1813
1814- settings.DATABASE_USER = REMEMBER['user']
1815
1816- settings.DATABASE_PASSWORD = REMEMBER['passwd']
1817
1818-
1819
1820- cursor = connection.cursor()
1821
1822-
1823
1824- time.sleep(1) # To avoid "database is being accessed by other users" errors.
1825
1826-
1827
1828- if verbosity >= 2:
1829
1830- print "_destroy_test_db(): removing tables from schema %s" % settings.TEST_DATABASE_USER
1831
1832-
1833
1834- cursor.execute("""SELECT RTRIM(table_name) as TABLES FROM SYSIBM.TABLES
1835
1836- WHERE TABLE_SCHEMA = %s AND TABLE_TYPE = 'BASE TABLE' ORDER BY table_name""", [settings.TEST_DATABASE_USER.upper()])
1837
1838-
1839
1840- rows = cursor.fetchall()
1841
1842- for row in rows:
1843
1844- stmt = 'DROP TABLE %s.%s' % (settings.TEST_DATABASE_USER.upper(), row[0])
1845
1846- if verbosity >= 2:
1847
1848- print stmt
1849
1850- try:
1851
1852- cursor.execute(stmt)
1853
1854- except Exception, err:
1855
1856- sys.stderr.write("Failed (%s)\n" % (err))
1857
1858- raise
1859
1860- connection._commit()
1861
1862- connection.close()
1863
1864\ No newline at end of file
1865
1866
1867Property changes on: django/db/backends/db2_9
1868___________________________________________________________________
1869Name: svn:ignore
1870 -*.pyc
1871 -
1872
1873=== django/core/management.py
1874==================================================================
1875--- django/core/management.py (/local/django/db2_9) (revision 3643)
1876
1877+++ django/core/management.py (/mirror/django/trunk) (revision 3643)
1878
1879@@ -235,12 +235,8 @@
1880
1881 # For MySQL, r_name must be unique in the first 64 characters.
1882 # So we are careful with character usage here.
1883 r_name = '%s_refs_%s_%x' % (r_col, col, abs(hash((r_table, table))))
1884- if hasattr(backend, 'get_max_constraint_length'):
1885- get_max = backend.get_max_constraint_length
1886- else:
1887- get_max = backend.get_max_name_length
1888 final_output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' % \
1889- (backend.quote_name(r_table), truncate_name(r_name, get_max()),
1890+ (backend.quote_name(r_table), truncate_name(r_name, backend.get_max_name_length()),
1891 backend.quote_name(r_col), backend.quote_name(table), backend.quote_name(col),
1892 backend.get_deferrable_sql()))
1893 del pending_references[model]
1894@@ -923,7 +919,7 @@
1895
1896 extra_params['decimal_places'] = row[5]
1897
1898 # Add primary_key and unique, if necessary.
1899- column_name = extra_params.get('db_column', row[0])
1900+ column_name = extra_params.get('db_column', att_name)
1901 if column_name in indexes:
1902 if indexes[column_name]['primary_key']:
1903 extra_params['primary_key'] = True
Back to Top