Ticket #87: unified_diff_oracle_backend_rev_656.txt

File unified_diff_oracle_backend_rev_656.txt, 29.3 KB (added by Jason Huggns, 19 years ago)

Updated unified diff for Oracle backend

Line 
1--- trunk/django/django/models/auth.py (revision 656)
2+++ trunk/django/django/models/auth.py (working copy)
3@@ -181,7 +181,7 @@
4 action_time = meta.DateTimeField(auto_now=True)
5 user = meta.ForeignKey(User)
6 content_type = meta.ForeignKey(core.ContentType, blank=True, null=True) # TODO: content_type_id name?
7- object_id = meta.TextField(blank=True, null=True)
8+ object_id = meta.CharField(maxlength=100, blank=True, null=True)
9 object_repr = meta.CharField(maxlength=200)
10 action_flag = meta.PositiveSmallIntegerField()
11 change_message = meta.TextField(blank=True)
12--- trunk/django/django/core/db/__init__.py (revision 656)
13+++ trunk/django/django/core/db/__init__.py (working copy)
14@@ -44,3 +44,4 @@
15 OPERATOR_MAPPING = dbmod.OPERATOR_MAPPING
16 DATA_TYPES = dbmod.DATA_TYPES
17 DATA_TYPES_REVERSE = dbmod.DATA_TYPES_REVERSE
18+EMPTY_STR_EQUIV = dbmod.EMPTY_STR_EQUIV
19--- trunk/django/django/core/db/backends/postgresql.py (revision 656)
20+++ trunk/django/django/core/db/backends/postgresql.py (working copy)
21@@ -188,3 +188,5 @@
22 1266: 'TimeField',
23 1700: 'FloatField',
24 }
25+
26+EMPTY_STR_EQUIV = ''
27\ No newline at end of file
28--- trunk/django/django/core/db/backends/sqlite3.py (revision 656)
29+++ trunk/django/django/core/db/backends/sqlite3.py (working copy)
30@@ -174,3 +174,5 @@
31 }
32
33 DATA_TYPES_REVERSE = {}
34+
35+EMPTY_STR_EQUIV = ''
36\ No newline at end of file
37--- trunk/django/django/core/db/backends/mysql.py (revision 656)
38+++ trunk/django/django/core/db/backends/mysql.py (working copy)
39@@ -155,3 +155,5 @@
40 FIELD_TYPE.LONG_BLOB: 'TextField',
41 FIELD_TYPE.VAR_STRING: 'CharField',
42 }
43+
44+EMPTY_STR_EQUIV = ''
45\ No newline at end of file
46--- None (revision 656)
47+++ trunk/django/django/core/db/backends/oracle.py (working copy)
48@@ -0,0 +1,162 @@
49+"""
50+Oracle database backend for Django.
51+
52+Requires cx_Oracle: http://www.computronix.com/utilities.shtml
53+"""
54+
55+from django.core.db import base, typecasts
56+import cx_Oracle as Database
57+
58+#needed for fetchone, fetchmany, fetchall support
59+from django.core.db.dicthelpers import *
60+
61+
62+DatabaseError = Database.DatabaseError
63+
64+class DatabaseWrapper:
65+ def __init__(self):
66+ self.connection = None
67+ self.queries = []
68+
69+ def cursor(self):
70+ from django.conf.settings import DATABASE_USER, DATABASE_NAME, DATABASE_HOST, DATABASE_PASSWORD, DEBUG
71+ if self.connection is None:
72+ if DATABASE_NAME == '' or DATABASE_USER == '' or DATABASE_PASSWORD == '':
73+ from django.core.exceptions import ImproperlyConfigured
74+ raise ImproperlyConfigured, "You need to specify DATABASE_NAME, DATABASE_USER, and DATABASE_PASSWORD in your Django settings file."
75+ conn_string = "%s/%s@%s" % (DATABASE_USER, DATABASE_PASSWORD, DATABASE_NAME)
76+ self.connection = Database.connect(conn_string)
77+ return FormatStylePlaceholderCursor(self.connection)
78+
79+ def commit(self):
80+ self.connection.commit()
81+
82+ def rollback(self):
83+ if self.connection:
84+ self.connection.rollback()
85+
86+ def close(self):
87+ if self.connection is not None:
88+ self.connection.close()
89+ self.connection = None
90+
91+class FormatStylePlaceholderCursor(Database.Cursor):
92+ """
93+ Django uses "format" (e.g. '%s') style placeholders, but Oracle uses ":var" style.
94+ This fixes it -- but note that if you want to use a literal "%s" in a query,
95+ you'll need to use "%%s" (which I belive is true of other wrappers as well).
96+ """
97+
98+ def execute(self, query, params=[]):
99+ query = self.convert_arguments(query, len(params))
100+ return Database.Cursor.execute(self, query, params)
101+
102+ def executemany(self, query, params=[]):
103+ query = self.convert_arguments(query, len(params[0]))
104+ return Database.Cursor.executemany(self, query, params)
105+
106+ def convert_arguments(self, query, num_params):
107+ # replace occurances of "%s" with ":arg" - Oracle requires colons for parameter placeholders.
108+ args = [':arg' for i in range(num_params)]
109+ return query % tuple(args)
110+
111+def get_last_insert_id(cursor, table_name, pk_name):
112+ query = "SELECT %s_sq.currval from dual" % table_name
113+ cursor.execute(query)
114+ return cursor.fetchone()[0]
115+
116+def get_date_extract_sql(lookup_type, table_name):
117+ # lookup_type is 'year', 'month', 'day'
118+ # http://www.psoug.org/reference/date_func.html
119+ return "EXTRACT(%s FROM %s)" % (lookup_type, table_name)
120+
121+def get_date_trunc_sql(lookup_type, field_name):
122+ return "EXTRACT(%s FROM TRUNC(%s))" % (lookup_type, table_name)
123+
124+def get_limit_offset_sql(limit, offset=None):
125+ # Limits and offset are too complicated to be handled here.
126+ #Instead, they are handled in django.core.meta.__init__.py
127+ pass
128+
129+def get_random_function_sql():
130+ return "DBMS_RANDOM.RANDOM"
131+
132+def get_table_list(cursor):
133+ "Returns a list of table names in the current database."
134+ cursor.execute("SELECT TABLE_NAME FROM USER_TABLES")
135+ return [row[0] for row in cursor.fetchall()]
136+
137+def get_relations(cursor, table_name):
138+ """
139+ Returns a dictionary of {field_index: (field_index_other_table, other_table)}
140+ representing all relationships to the given table. Indexes are 0-based.
141+ """
142+ raise NotImplementedError
143+
144+
145+OPERATOR_MAPPING = {
146+ 'exact': '=',
147+ 'iexact': 'LIKE',
148+ 'contains': 'LIKE',
149+ 'icontains': 'LIKE',
150+ 'ne': '!=',
151+ 'gt': '>',
152+ 'gte': '>=',
153+ 'lt': '<',
154+ 'lte': '<=',
155+ 'startswith': 'LIKE',
156+ 'endswith': 'LIKE',
157+ 'istartswith': 'LIKE',
158+ 'iendswith': 'LIKE',
159+}
160+
161+# This dictionary maps Field objects to their associated MySQL column
162+# types, as strings. Column-type strings can contain format strings; they'll
163+# be interpolated against the values of Field.__dict__ before being output.
164+# If a column type is set to None, it won't be included in the output.
165+DATA_TYPES = {
166+ 'AutoField': 'number(38)',
167+ 'BooleanField': 'number(1)',
168+ 'CharField': 'varchar2(%(maxlength)s)',
169+ 'CommaSeparatedIntegerField': 'varchar2(%(maxlength)s)',
170+ 'DateField': 'date',
171+ 'DateTimeField': 'date',
172+ 'EmailField': 'varchar2(75)',
173+ 'FileField': 'varchar2(100)',
174+ 'FloatField': 'number(%(max_digits)s, %(decimal_places)s)',
175+ 'ImageField': 'varchar2(100)',
176+ 'IntegerField': 'integer',
177+ 'IPAddressField': 'char(15)',
178+ 'ManyToManyField': None,
179+ 'NullBooleanField': 'integer',
180+ 'OneToOneField': 'integer',
181+ 'PhoneNumberField': 'varchar(20)',
182+ 'PositiveIntegerField': 'integer',
183+ 'PositiveSmallIntegerField': 'smallint',
184+ 'SlugField': 'varchar(50)',
185+ 'SmallIntegerField': 'smallint',
186+ 'SmallTextField': 'varchar2(4000)',
187+ 'TextField': 'long',
188+ 'TimeField': 'timestamp',
189+ 'URLField': 'varchar(200)',
190+ 'USStateField': 'varchar(2)',
191+ 'XMLField': 'long',
192+}
193+
194+# Maps type codes to Django Field types.
195+DATA_TYPES_REVERSE = {
196+ 16: 'BooleanField',
197+ 21: 'SmallIntegerField',
198+ 23: 'IntegerField',
199+ 25: 'TextField',
200+ 869: 'IPAddressField',
201+ 1043: 'CharField',
202+ 1082: 'DateField',
203+ 1083: 'TimeField',
204+ 1114: 'DateTimeField',
205+ 1184: 'DateTimeField',
206+ 1266: 'TimeField',
207+ 1700: 'FloatField',
208+}
209+
210+EMPTY_STR_EQUIV = ' '
211\ No newline at end of file
212--- trunk/django/django/core/meta/__init__.py (revision 656)
213+++ trunk/django/django/core/meta/__init__.py (working copy)
214@@ -777,8 +777,8 @@
215 pk_set = bool(pk_val)
216 record_exists = True
217 if pk_set:
218- # Determine whether a record with the primary key already exists.
219- cursor.execute("SELECT 1 FROM %s WHERE %s=%%s LIMIT 1" % (opts.db_table, opts.pk.column), [pk_val])
220+ # Determine whether a record with the primary key already exists.
221+ cursor.execute("SELECT 1 FROM %s WHERE %s=%%s" % (opts.db_table, opts.pk.column), [pk_val])
222 # If it does already exist, do an UPDATE.
223 if cursor.fetchone():
224 db_values = [f.get_db_prep_save(f.pre_save(getattr(self, f.column), False)) for f in non_pks]
225@@ -1102,10 +1102,11 @@
226 kwargs['select'] = kwargs.get('select', {}).items()
227
228 cursor = db.db.cursor()
229- select, sql, params = function_get_sql_clause(opts, **kwargs)
230- cursor.execute("SELECT " + (kwargs.get('distinct') and "DISTINCT " or "") + ",".join(select) + sql, params)
231+ select, sql, params, full_query = function_get_sql_clause(opts, **kwargs)
232+ cursor.execute(full_query, params)
233 fill_cache = kwargs.get('select_related')
234 index_end = len(opts.fields)
235+
236 while 1:
237 rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE)
238 if not rows:
239@@ -1127,7 +1128,7 @@
240 kwargs['offset'] = None
241 kwargs['limit'] = None
242 kwargs['select_related'] = False
243- _, sql, params = function_get_sql_clause(opts, **kwargs)
244+ _, sql, params, full_query = function_get_sql_clause(opts, **kwargs)
245 cursor = db.db.cursor()
246 cursor.execute("SELECT COUNT(*)" + sql, params)
247 return cursor.fetchone()[0]
248@@ -1144,7 +1145,7 @@
249 fields = [f.column for f in opts.fields]
250
251 cursor = db.db.cursor()
252- _, sql, params = function_get_sql_clause(opts, **kwargs)
253+ _, sql, params, full_query = function_get_sql_clause(opts, **kwargs)
254 select = ['%s.%s' % (opts.db_table, f) for f in fields]
255 cursor.execute("SELECT " + (kwargs.get('distinct') and "DISTINCT " or "") + ",".join(select) + sql, params)
256 while 1:
257@@ -1171,9 +1172,9 @@
258 new_prefix = '%s%s' % (db_table, len(cache_tables_seen))
259 tables.append('%s %s' % (db_table, new_prefix))
260 db_table = new_prefix
261- cache_tables_seen.append(db_table)
262+ cache_tables_seen.append(db_table)
263 where.append('%s.%s = %s.%s' % (old_prefix, f.column, db_table, f.rel.get_related_field().column))
264- select.extend(['%s.%s' % (db_table, f2.column) for f2 in f.rel.to.fields])
265+ select.extend(['%s.%s as "%s.%s"' % (db_table, f2.column, db_table, f2.column) for f2 in f.rel.to.fields])
266 _fill_table_cache(f.rel.to, select, tables, where, db_table, cache_tables_seen)
267
268 def _throw_bad_kwarg_error(kwarg):
269@@ -1188,7 +1189,7 @@
270 # is specifically a list of where clauses to use for JOINs. This
271 # distinction is necessary because of support for "_or".
272
273- # table_count is used to ensure table aliases are unique.
274+ # table_count is used to ensure table aliases are unique.
275 tables, join_where, where, params = [], [], [], []
276 for kwarg, kwarg_value in kwarg_items:
277 if kwarg in ('order_by', 'limit', 'offset', 'select_related', 'distinct', 'select', 'tables', 'where', 'params'):
278@@ -1333,14 +1334,58 @@
279 order_by.append('%s%s ASC' % (table_prefix, orderfield2column(f, opts)))
280 order_by = ", ".join(order_by)
281
282- # LIMIT and OFFSET clauses
283- if kwargs.get('limit') is not None:
284- limit_sql = " %s " % db.get_limit_offset_sql(kwargs['limit'], kwargs.get('offset'))
285- else:
286- assert kwargs.get('offset') is None, "'offset' is not allowed without 'limit'"
287- limit_sql = ""
288
289- return select, " FROM " + ",".join(tables) + (where and " WHERE " + " AND ".join(where) or "") + (order_by and " ORDER BY " + order_by or "") + limit_sql, params
290+ sql = " FROM " + ",".join(tables) + (where and " WHERE " + " AND ".join(where) or "") + (order_by and " ORDER BY " + order_by or "")
291+
292+ if (db.DATABASE_ENGINE != 'oracle'):
293+ # LIMIT and OFFSET clauses
294+ if kwargs.get('limit') is not None:
295+ limit_sql = " %s " % db.get_limit_offset_sql(kwargs['limit'], kwargs.get('offset'))
296+ else:
297+ assert kwargs.get('offset') is None, "'offset' is not allowed without 'limit'"
298+ limit_sql = ""
299+
300+
301+ full_query = "SELECT " + (kwargs.get('distinct') and "DISTINCT " or "") + ",".join(select) + sql + limit_sql
302+ return select, sql + limit_sql, params, full_query
303+ else:
304+ # To support limits and offsets, Oracle requires some funky rewriting of an otherwise normal looking query.
305+
306+ select_clause = ",".join(select)
307+ distinct = (kwargs.get('distinct') and "DISTINCT " or "")
308+ from_clause = ",".join(tables)
309+ where_clause = (where and " WHERE " + " AND ".join(where) or "")
310+
311+ if order_by:
312+ order_by_clause = " OVER (ORDER BY %s )" % (order_by)
313+ else:
314+ #Oracle's row_number() function always requires an order-by clause.
315+ #So we need to define a default order-by, since none was provided.
316+ order_by_clause = " OVER (ORDER BY %s.%s)" % (opts.db_table, opts.fields[0].name)
317+
318+ # limit_and_offset_clause
319+ limit = kwargs.get('limit',0)
320+ offset = kwargs.get('offset',0)
321+
322+ limit_and_offset_clause = ''
323+ if limit:
324+ limit = int(limit)
325+ offset = int(offset)
326+ limit_and_offset_clause = "WHERE rn > %s AND rn <= %s" % (offset, limit+offset)
327+ else:
328+ limit_and_offset_clause = "WHERE rn > %s" % (offset)
329+
330+ full_query = """SELECT * FROM
331+ (SELECT %s
332+ %s,
333+ ROW_NUMBER() %s AS rn
334+ FROM %s
335+ %s
336+ )
337+ %s
338+ """ % (distinct, select_clause, order_by_clause, from_clause, where_clause, limit_and_offset_clause)
339+ return select, sql, params, full_query
340+
341
342 def function_get_in_bulk(opts, klass, *args, **kwargs):
343 id_list = args and args[0] or kwargs['id_list']
344@@ -1366,7 +1411,7 @@
345 kwargs['order_by'] = [] # Clear this because it'll mess things up otherwise.
346 if field.null:
347 kwargs.setdefault('where', []).append('%s.%s IS NOT NULL' % (opts.db_table, field.column))
348- select, sql, params = function_get_sql_clause(opts, **kwargs)
349+ select, sql, params, full_query = function_get_sql_clause(opts, **kwargs)
350 sql = 'SELECT %s %s GROUP BY 1 ORDER BY 1' % (db.get_date_trunc_sql(kind, '%s.%s' % (opts.db_table, field.column)), sql)
351 cursor = db.db.cursor()
352 cursor.execute(sql, params)
353--- trunk/django/django/core/meta/fields.py (revision 656)
354+++ trunk/django/django/core/meta/fields.py (working copy)
355@@ -1,4 +1,5 @@
356 from django.conf import settings
357+from django.core import db
358 from django.core import formfields, validators
359 from django.core.exceptions import ObjectDoesNotExist
360 from django.utils.functional import curry
361@@ -120,6 +121,12 @@
362
363 def get_db_prep_save(self, value):
364 "Returns field's value prepared for saving into a database."
365+
366+ # Oracle treats empty strings ('') the same as NULLs.
367+ # To get around this wart, we need to change it to something else...
368+ if value == '':
369+ string_replacement = getattr(db,'EMPTY_STR_EQUIV','')
370+ value = string_replacement
371 return value
372
373 def get_db_prep_lookup(self, lookup_type, value):
374@@ -129,7 +136,12 @@
375 elif lookup_type in ('range', 'in'):
376 return value
377 elif lookup_type == 'year':
378- return ['%s-01-01' % value, '%s-12-31' % value]
379+ if settings.DATABASE_ENGINE == 'oracle':
380+ from_dt = datetime.date(int(value), 01, 01)
381+ to_dt = datetime.date(int(value), 12, 31)
382+ return [from_dt, to_dt]
383+ else:
384+ return ['%s-01-01' % value, '%s-12-31' % value]
385 elif lookup_type in ('contains', 'icontains'):
386 return ["%%%s%%" % prep_for_like_query(value)]
387 elif lookup_type == 'iexact':
388@@ -287,6 +299,14 @@
389 kwargs['blank'] = True
390 Field.__init__(self, *args, **kwargs)
391
392+ def get_db_prep_lookup(self, lookup_type, value):
393+ if settings.DATABASE_ENGINE == 'oracle':
394+ if value == 'True':
395+ value = 1
396+ elif value == 'False':
397+ value = 0
398+ return Field.get_db_prep_lookup(self, lookup_type, value)
399+
400 def get_manipulator_field_objs(self):
401 return [formfields.CheckboxField]
402
403@@ -305,12 +325,30 @@
404 if auto_now or auto_now_add:
405 kwargs['editable'] = False
406 Field.__init__(self, verbose_name, name, **kwargs)
407-
408- def get_db_prep_lookup(self, lookup_type, value):
409- if lookup_type == 'range':
410- value = [str(v) for v in value]
411- else:
412- value = str(value)
413+
414+ def get_db_prep_lookup(self, lookup_type, value):
415+ # Oracle driver can deal with datetime.time objects natively
416+ # and doesn't need to convert to string
417+ if settings.DATABASE_ENGINE == 'oracle':
418+ if lookup_type == 'range':
419+ value = [v for v in value]
420+ #elif lookup_type == 'day':
421+ # value = int(value)
422+ elif lookup_type in ('exact', 'gt', 'gte', 'lt', 'lte', 'ne'):
423+ if type(value) == str:
424+ pattern = r'^\d\d\d\d-\d\d-\d\d$' # "YYYY-MM-DD"
425+ from re import search
426+ if search(pattern, value) != None:
427+ year, month, day = map(int, value.split('-'))
428+ value = datetime.date(year, month, day)
429+ else:
430+ value = value
431+ else:
432+ if lookup_type == 'range':
433+ value = [str(v) for v in value]
434+ else:
435+ value = str(value)
436+
437 return Field.get_db_prep_lookup(self, lookup_type, value)
438
439 def pre_save(self, value, add):
440@@ -321,22 +359,30 @@
441 def get_db_prep_save(self, value):
442 # Casts dates into string format for entry into database.
443 if value is not None:
444- value = value.strftime('%Y-%m-%d')
445- return Field.get_db_prep_save(self, value)
446+ if settings.DATABASE_ENGINE != 'oracle':
447+ #Oracle does not need a string conversion
448+ value = value.strftime('%Y-%m-%d')
449+ return Field.get_db_prep_save(self, value)
450
451 def get_manipulator_field_objs(self):
452 return [formfields.DateField]
453
454 class DateTimeField(DateField):
455 def get_db_prep_save(self, value):
456- # Casts dates into string format for entry into database.
457- if value is not None:
458- # MySQL will throw a warning if microseconds are given, because it
459- # doesn't support microseconds.
460- if settings.DATABASE_ENGINE == 'mysql':
461- value = value.replace(microsecond=0)
462- value = str(value)
463- return Field.get_db_prep_save(self, value)
464+ # Casts dates into string format for entry into database.
465+ if value is not None:
466+ # MySQL will throw a warning if microseconds are given, because it
467+ # doesn't support microseconds.
468+ if settings.DATABASE_ENGINE == 'mysql':
469+ value = value.replace(microsecond=0)
470+ value = str(value)
471+ # Oracle will choke if microseconds are given...
472+ elif settings.DATABASE_ENGINE == 'oracle':
473+ value = value.replace(microsecond=0)
474+ else:
475+ value = str(value)
476+
477+ return Field.get_db_prep_save(self, value)
478
479 def get_manipulator_field_objs(self):
480 return [formfields.DateField, formfields.TimeField]
481@@ -513,12 +559,21 @@
482 if auto_now or auto_now_add:
483 kwargs['editable'] = False
484 Field.__init__(self, verbose_name, name, **kwargs)
485-
486+
487 def get_db_prep_lookup(self, lookup_type, value):
488- if lookup_type == 'range':
489- value = [str(v) for v in value]
490- else:
491- value = str(value)
492+ # Oracle driver can deal with datetime.time objects natively
493+ # and doesn't need to convert to string
494+ if settings.DATABASE_ENGINE == 'oracle':
495+ if lookup_type == 'range':
496+ value = [v for v in value]
497+ else:
498+ value = value
499+ else:
500+ if lookup_type == 'range':
501+ value = [str(v) for v in value]
502+ else:
503+ value = str(value)
504+
505 return Field.get_db_prep_lookup(self, lookup_type, value)
506
507 def pre_save(self, value, add):
508@@ -529,11 +584,16 @@
509 def get_db_prep_save(self, value):
510 # Casts dates into string format for entry into database.
511 if value is not None:
512- # MySQL will throw a warning if microseconds are given, because it
513- # doesn't support microseconds.
514- if settings.DATABASE_ENGINE == 'mysql':
515- value = value.replace(microsecond=0)
516- value = str(value)
517+ # MySQL will throw a warning if microseconds are given, because it
518+ # doesn't support microseconds.
519+ if settings.DATABASE_ENGINE == 'mysql':
520+ value = value.replace(microsecond=0)
521+ value = str(value)
522+ # Oracle will choke if microseconds are given...
523+ elif settings.DATABASE_ENGINE == 'oracle':
524+ value = value.replace(microsecond=0)
525+ else:
526+ value = str(value)
527 return Field.get_db_prep_save(self, value)
528
529 def get_manipulator_field_objs(self):
530--- trunk/django/django/core/management.py (revision 656)
531+++ trunk/django/django/core/management.py (working copy)
532@@ -20,7 +20,7 @@
533 ADMIN_TEMPLATE_DIR = os.path.join(django.__path__[0], 'conf/admin_templates')
534
535 def _get_packages_insert(app_label):
536- return "INSERT INTO packages (label, name) VALUES ('%s', '%s');" % (app_label, app_label)
537+ return "INSERT INTO packages (label, name) VALUES ('%s', '%s')" % (app_label, app_label)
538
539 def _get_permission_codename(action, opts):
540 return '%s_%s' % (action, opts.object_name.lower())
541@@ -34,11 +34,11 @@
542 return perms + list(opts.permissions)
543
544 def _get_permission_insert(name, codename, opts):
545- return "INSERT INTO auth_permissions (name, package, codename) VALUES ('%s', '%s', '%s');" % \
546+ return "INSERT INTO auth_permissions (name, package, codename) VALUES ('%s', '%s', '%s')" % \
547 (name.replace("'", "''"), opts.app_label, codename)
548
549 def _get_contenttype_insert(opts):
550- return "INSERT INTO content_types (name, package, python_module_name) VALUES ('%s', '%s', '%s');" % \
551+ return "INSERT INTO content_types (name, package, python_module_name) VALUES ('%s', '%s', '%s')" % \
552 (opts.verbose_name, opts.app_label, opts.module_name)
553
554 def _is_valid_dir_name(s):
555@@ -85,9 +85,26 @@
556 full_statement = ['CREATE TABLE %s (' % opts.db_table]
557 for i, line in enumerate(table_output): # Combine and add commas.
558 full_statement.append(' %s%s' % (line, i < len(table_output)-1 and ',' or ''))
559- full_statement.append(');')
560+ full_statement.append(')')
561 final_output.append('\n'.join(full_statement))
562+
563+ if (db.DATABASE_ENGINE == 'oracle') & (opts.has_auto_field):
564+ # To simulate auto-incrementing primary keys in Oracle
565+
566+ sequence_statement = 'CREATE SEQUENCE %s_sq\n' % opts.db_table
567+ final_output.append(sequence_statement)
568+
569+ trigger_statement = "" + \
570+ "CREATE OR REPLACE trigger %s_tr\n" % opts.db_table + \
571+ " before insert on %s\n" % opts.db_table + \
572+ " for each row\n" + \
573+ " begin\n" + \
574+ " select %s_sq.NEXTVAL into :new.id from DUAL;\n" % opts.db_table + \
575+ " end;\n"
576+ final_output.append(trigger_statement)
577+
578
579+
580 for klass in mod._MODELS:
581 opts = klass._meta
582 for f in opts.many_to_many:
583@@ -98,10 +115,24 @@
584 table_output.append(' %s_id %s NOT NULL REFERENCES %s (%s),' % \
585 (f.rel.to.object_name.lower(), db.DATA_TYPES['IntegerField'], f.rel.to.db_table, f.rel.to.pk.column))
586 table_output.append(' UNIQUE (%s_id, %s_id)' % (opts.object_name.lower(), f.rel.to.object_name.lower()))
587- table_output.append(');')
588+ table_output.append(')')
589 final_output.append('\n'.join(table_output))
590+
591+ if (db.DATABASE_ENGINE == 'oracle') & (opts.has_auto_field):
592+ sequence_statement = 'CREATE SEQUENCE %s_sq\n' % f.get_m2m_db_table(opts)
593+ final_output.append(sequence_statement)
594+
595+ trigger_statement = "" + \
596+ "CREATE OR REPLACE trigger %s_tr\n" % f.get_m2m_db_table(opts) + \
597+ " before insert on %s\n" % f.get_m2m_db_table(opts) + \
598+ " for each row\n" + \
599+ " begin\n" + \
600+ " select %s_sq.NEXTVAL into :new.id from DUAL;\n" % f.get_m2m_db_table(opts) + \
601+ " end;\n"
602+ final_output.append(trigger_statement)
603+
604 return final_output
605-get_sql_create.help_doc = "Prints the CREATE TABLE SQL statements for the given model module name(s)."
606+get_sql_create.help_doc = "Prints the CREATE TABLE SQL statements for the given model module name(s)."
607 get_sql_create.args = APP_ARGS
608
609 def get_sql_delete(mod):
610@@ -116,23 +147,27 @@
611 try:
612 if cursor is not None:
613 # Check whether the table exists.
614- cursor.execute("SELECT 1 FROM %s LIMIT 1" % klass._meta.db_table)
615+ cursor.execute("SELECT COUNT(1) FROM %s" % klass._meta.db_table)
616 except:
617 # The table doesn't exist, so it doesn't need to be dropped.
618 db.db.rollback()
619 else:
620 output.append("DROP TABLE %s;" % klass._meta.db_table)
621+ if db.DATABASE_ENGINE == 'oracle':
622+ output.append("DROP SEQUENCE %s_sq" % klass._meta.db_table)
623+
624 for klass in mod._MODELS:
625 opts = klass._meta
626 for f in opts.many_to_many:
627 try:
628 if cursor is not None:
629- cursor.execute("SELECT 1 FROM %s LIMIT 1" % f.get_m2m_db_table(opts))
630+ cursor.execute("SELECT COUNT(1) FROM %s" % f.get_m2m_db_table(opts))
631 except:
632 db.db.rollback()
633 else:
634 output.append("DROP TABLE %s;" % f.get_m2m_db_table(opts))
635-
636+ if db.DATABASE_ENGINE == 'oracle':
637+ output.append("DROP SEQUENCE %s_sq" % f.get_m2m_db_table(opts))
638 app_label = mod._MODELS[0]._meta.app_label
639
640 # Delete from packages, auth_permissions, content_types.
641@@ -181,12 +216,15 @@
642
643 def get_sql_sequence_reset(mod):
644 "Returns a list of the SQL statements to reset PostgreSQL sequences for the given module."
645- from django.core import meta
646+ from django.core import db, meta
647 output = []
648 for klass in mod._MODELS:
649 for f in klass._meta.fields:
650- if isinstance(f, meta.AutoField):
651- output.append("SELECT setval('%s_%s_seq', (SELECT max(%s) FROM %s));" % (klass._meta.db_table, f.column, f.column, klass._meta.db_table))
652+ if isinstance(f, meta.AutoField):
653+ if db.DATABASE_ENGINE == 'postgresql':
654+ output.append("SELECT setval('%s_%s_seq', (SELECT max(%s) FROM %s));" % (klass._meta.db_table, f.column, f.column, klass._meta.db_table))
655+ else:
656+ raise "Not implemented yet for %s!" % (db.DATABASE_ENGINE)
657 return output
658 get_sql_sequence_reset.help_doc = "Prints the SQL statements for resetting PostgreSQL sequences for the given model module name(s)."
659 get_sql_sequence_reset.args = APP_ARGS
660--- trunk/django/django/middleware/sessions.py (revision 656)
661+++ trunk/django/django/middleware/sessions.py (working copy)
662@@ -1,4 +1,4 @@
663-from django.conf.settings import SESSION_COOKIE_NAME, SESSION_COOKIE_AGE, SESSION_COOKIE_DOMAIN
664+from django.conf.settings import SESSION_COOKIE_NAME, SESSION_COOKIE_AGE, SESSION_COOKIE_DOMAIN, DATABASE_ENGINE
665 from django.models.core import sessions
666 import datetime
667
668@@ -64,8 +64,12 @@
669 modified = False
670 if modified:
671 session_key = request.session.session_key or sessions.get_new_session_key()
672+ expire_date = datetime.datetime.now()
673+ #Oracle and Mysql can't deal with microseconds
674+ if DATABASE_ENGINE in ['oracle','mysql']:
675+ expire_date = expire_date.replace(microsecond=0)
676 new_session = sessions.save(session_key, request.session._session,
677- datetime.datetime.now() + datetime.timedelta(seconds=SESSION_COOKIE_AGE))
678+ expire_date + datetime.timedelta(seconds=SESSION_COOKIE_AGE))
679 # TODO: Accept variable session length and domain.
680 response.set_cookie(SESSION_COOKIE_NAME, session_key,
681 max_age=SESSION_COOKIE_AGE, domain=SESSION_COOKIE_DOMAIN)
Back to Top