--- trunk/django/django/models/auth.py  (revision 656)
+++ trunk/django/django/models/auth.py  (working copy)
@@ -181,7 +181,7 @@
     action_time = meta.DateTimeField(auto_now=True)
     user = meta.ForeignKey(User)
     content_type = meta.ForeignKey(core.ContentType, blank=True, null=True) # TODO: content_type_id name?
-    object_id = meta.TextField(blank=True, null=True)
+    object_id = meta.CharField(maxlength=100, blank=True, null=True)
     object_repr = meta.CharField(maxlength=200)
     action_flag = meta.PositiveSmallIntegerField()
     change_message = meta.TextField(blank=True)
--- trunk/django/django/core/db/__init__.py (revision 656)
+++ trunk/django/django/core/db/__init__.py (working copy)
@@ -44,3 +44,4 @@
 OPERATOR_MAPPING = dbmod.OPERATOR_MAPPING
 DATA_TYPES = dbmod.DATA_TYPES
 DATA_TYPES_REVERSE = dbmod.DATA_TYPES_REVERSE
+EMPTY_STR_EQUIV = dbmod.EMPTY_STR_EQUIV
--- trunk/django/django/core/db/backends/postgresql.py  (revision 656)
+++ trunk/django/django/core/db/backends/postgresql.py  (working copy)
@@ -188,3 +188,5 @@
     1266: 'TimeField',
     1700: 'FloatField',
 }
+
+EMPTY_STR_EQUIV = ''
\ No newline at end of file
--- trunk/django/django/core/db/backends/sqlite3.py (revision 656)
+++ trunk/django/django/core/db/backends/sqlite3.py (working copy)
@@ -174,3 +174,5 @@
 }
 
 DATA_TYPES_REVERSE = {}
+
+EMPTY_STR_EQUIV = ''
\ No newline at end of file
--- trunk/django/django/core/db/backends/mysql.py   (revision 656)
+++ trunk/django/django/core/db/backends/mysql.py   (working copy)
@@ -155,3 +155,5 @@
     FIELD_TYPE.LONG_BLOB: 'TextField',
     FIELD_TYPE.VAR_STRING: 'CharField',
 }
+
+EMPTY_STR_EQUIV = ''
\ No newline at end of file
--- None    (revision 656)
+++ trunk/django/django/core/db/backends/oracle.py  (working copy)
@@ -0,0 +1,162 @@
+"""
+Oracle database backend for Django.
+
+Requires cx_Oracle: http://www.computronix.com/utilities.shtml
+"""
+
+from django.core.db import base, typecasts
+import cx_Oracle  as Database
+
+#needed for fetchone, fetchmany, fetchall support
+from django.core.db.dicthelpers import *
+
+
+DatabaseError = Database.DatabaseError
+
+class DatabaseWrapper:
+    def __init__(self):
+        self.connection = None
+        self.queries = []
+
+    def cursor(self):
+        from django.conf.settings import DATABASE_USER, DATABASE_NAME, DATABASE_HOST, DATABASE_PASSWORD, DEBUG
+        if self.connection is None:
+            if DATABASE_NAME == '' or DATABASE_USER == '' or DATABASE_PASSWORD == '':
+                from django.core.exceptions import ImproperlyConfigured
+                raise ImproperlyConfigured, "You need to specify DATABASE_NAME, DATABASE_USER, and DATABASE_PASSWORD in your Django settings file."
+            conn_string = "%s/%s@%s" % (DATABASE_USER, DATABASE_PASSWORD, DATABASE_NAME)
+            self.connection = Database.connect(conn_string)               
+        return FormatStylePlaceholderCursor(self.connection)
+
+    def commit(self):
+        self.connection.commit()
+
+    def rollback(self):
+        if self.connection:
+            self.connection.rollback()
+
+    def close(self):
+        if self.connection is not None:
+            self.connection.close()
+            self.connection = None
+
+class FormatStylePlaceholderCursor(Database.Cursor):
+    """
+    Django uses "format" (e.g. '%s') style placeholders, but Oracle uses ":var" style.
+    This fixes it -- but note that if you want to use a literal "%s" in a query,
+    you'll need to use "%%s" (which I belive is true of other wrappers as well).
+    """
+    
+    def execute(self, query, params=[]):
+        query = self.convert_arguments(query, len(params))
+        return Database.Cursor.execute(self, query, params)
+        
+    def executemany(self, query, params=[]):  
+        query = self.convert_arguments(query, len(params[0]))
+        return Database.Cursor.executemany(self, query, params)
+        
+    def convert_arguments(self, query, num_params):
+        # replace occurances of "%s" with ":arg" - Oracle requires colons for parameter placeholders.
+        args = [':arg' for i in range(num_params)]
+        return query % tuple(args)
+
+def get_last_insert_id(cursor, table_name, pk_name):
+    query = "SELECT %s_sq.currval from dual" % table_name
+    cursor.execute(query)
+    return cursor.fetchone()[0]
+
+def get_date_extract_sql(lookup_type, table_name):
+    # lookup_type is 'year', 'month', 'day'
+    # http://www.psoug.org/reference/date_func.html   
+    return "EXTRACT(%s FROM %s)" % (lookup_type, table_name)
+
+def get_date_trunc_sql(lookup_type, field_name):
+    return "EXTRACT(%s FROM TRUNC(%s))" % (lookup_type, table_name)
+    
+def get_limit_offset_sql(limit, offset=None):
+    # Limits and offset are too complicated to be handled here.
+    #Instead, they are handled in django.core.meta.__init__.py
+    pass
+    
+def get_random_function_sql():
+    return "DBMS_RANDOM.RANDOM"    
+    
+def get_table_list(cursor):
+    "Returns a list of table names in the current database."
+    cursor.execute("SELECT TABLE_NAME FROM USER_TABLES")
+    return [row[0] for row in cursor.fetchall()]
+
+def get_relations(cursor, table_name):
+    """
+    Returns a dictionary of {field_index: (field_index_other_table, other_table)}
+    representing all relationships to the given table. Indexes are 0-based.
+    """
+    raise NotImplementedError
+
+
+OPERATOR_MAPPING = {
+    'exact': '=',
+    'iexact': 'LIKE',
+    'contains': 'LIKE',
+    'icontains': 'LIKE',
+    'ne': '!=',
+    'gt': '>',
+    'gte': '>=',
+    'lt': '<',
+    'lte': '<=',
+    'startswith': 'LIKE',
+    'endswith': 'LIKE',
+    'istartswith': 'LIKE',
+    'iendswith': 'LIKE',
+}
+
+# This dictionary maps Field objects to their associated MySQL 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.
+DATA_TYPES = {
+    'AutoField':         'number(38)',
+    'BooleanField':      'number(1)',
+    'CharField':         'varchar2(%(maxlength)s)',
+    'CommaSeparatedIntegerField': 'varchar2(%(maxlength)s)',
+    'DateField':         'date',
+    'DateTimeField':     'date',
+    'EmailField':        'varchar2(75)',
+    'FileField':         'varchar2(100)',
+    'FloatField':        'number(%(max_digits)s, %(decimal_places)s)',
+    'ImageField':        'varchar2(100)',
+    'IntegerField':      'integer',
+    'IPAddressField':    'char(15)',
+    'ManyToManyField':   None,
+    'NullBooleanField':  'integer',
+    'OneToOneField':     'integer',
+    'PhoneNumberField':  'varchar(20)',
+    'PositiveIntegerField': 'integer',
+    'PositiveSmallIntegerField': 'smallint',
+    'SlugField':         'varchar(50)',
+    'SmallIntegerField': 'smallint',
+    'SmallTextField':    'varchar2(4000)',
+    'TextField':         'long',
+    'TimeField':         'timestamp',
+    'URLField':          'varchar(200)',
+    'USStateField':      'varchar(2)',
+    'XMLField':          'long',
+}
+
+# Maps type codes to Django Field types.
+DATA_TYPES_REVERSE = {
+    16: 'BooleanField',
+    21: 'SmallIntegerField',
+    23: 'IntegerField',
+    25: 'TextField',
+    869: 'IPAddressField',
+    1043: 'CharField',
+    1082: 'DateField',
+    1083: 'TimeField',
+    1114: 'DateTimeField',
+    1184: 'DateTimeField',
+    1266: 'TimeField',
+    1700: 'FloatField',
+}
+
+EMPTY_STR_EQUIV = ' '
\ No newline at end of file
--- trunk/django/django/core/meta/__init__.py   (revision 656)
+++ trunk/django/django/core/meta/__init__.py   (working copy)
@@ -777,8 +777,8 @@
     pk_set = bool(pk_val)
     record_exists = True
     if pk_set:
-        # Determine whether a record with the primary key already exists.
-        cursor.execute("SELECT 1 FROM %s WHERE %s=%%s LIMIT 1" % (opts.db_table, opts.pk.column), [pk_val])
+        # Determine whether a record with the primary key already exists.
+        cursor.execute("SELECT 1 FROM %s WHERE %s=%%s" % (opts.db_table, opts.pk.column), [pk_val])
         # If it does already exist, do an UPDATE.
         if cursor.fetchone():
             db_values = [f.get_db_prep_save(f.pre_save(getattr(self, f.column), False)) for f in non_pks]
@@ -1102,10 +1102,11 @@
     kwargs['select'] = kwargs.get('select', {}).items()
 
     cursor = db.db.cursor()
-    select, sql, params = function_get_sql_clause(opts, **kwargs)
-    cursor.execute("SELECT " + (kwargs.get('distinct') and "DISTINCT " or "") + ",".join(select) + sql, params)
+    select, sql, params, full_query = function_get_sql_clause(opts, **kwargs)
+    cursor.execute(full_query, params)
     fill_cache = kwargs.get('select_related')
     index_end = len(opts.fields)
+    
     while 1:
         rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE)
         if not rows:
@@ -1127,7 +1128,7 @@
     kwargs['offset'] = None
     kwargs['limit'] = None
     kwargs['select_related'] = False
-    _, sql, params = function_get_sql_clause(opts, **kwargs)
+    _, sql, params, full_query = function_get_sql_clause(opts, **kwargs)
     cursor = db.db.cursor()
     cursor.execute("SELECT COUNT(*)" + sql, params)
     return cursor.fetchone()[0]
@@ -1144,7 +1145,7 @@
         fields = [f.column for f in opts.fields]
 
     cursor = db.db.cursor()
-    _, sql, params = function_get_sql_clause(opts, **kwargs)
+    _, sql, params, full_query = function_get_sql_clause(opts, **kwargs)
     select = ['%s.%s' % (opts.db_table, f) for f in fields]
     cursor.execute("SELECT " + (kwargs.get('distinct') and "DISTINCT " or "") + ",".join(select) + sql, params)
     while 1:
@@ -1171,9 +1172,9 @@
                 new_prefix = '%s%s' % (db_table, len(cache_tables_seen))
                 tables.append('%s %s' % (db_table, new_prefix))
                 db_table = new_prefix
-            cache_tables_seen.append(db_table)
+            cache_tables_seen.append(db_table)
             where.append('%s.%s = %s.%s' % (old_prefix, f.column, db_table, f.rel.get_related_field().column))
-            select.extend(['%s.%s' % (db_table, f2.column) for f2 in f.rel.to.fields])
+            select.extend(['%s.%s as "%s.%s"' % (db_table, f2.column, db_table, f2.column) for f2 in f.rel.to.fields])
             _fill_table_cache(f.rel.to, select, tables, where, db_table, cache_tables_seen)
 
 def _throw_bad_kwarg_error(kwarg):
@@ -1188,7 +1189,7 @@
     # is specifically a list of where clauses to use for JOINs. This
     # distinction is necessary because of support for "_or".
 
-    # table_count is used to ensure table aliases are unique.
+    # table_count is used to ensure table aliases are unique.    
     tables, join_where, where, params = [], [], [], []
     for kwarg, kwarg_value in kwarg_items:
         if kwarg in ('order_by', 'limit', 'offset', 'select_related', 'distinct', 'select', 'tables', 'where', 'params'):
@@ -1333,14 +1334,58 @@
                 order_by.append('%s%s ASC' % (table_prefix, orderfield2column(f, opts)))
     order_by = ", ".join(order_by)
 
-    # LIMIT and OFFSET clauses
-    if kwargs.get('limit') is not None:
-        limit_sql = " %s " % db.get_limit_offset_sql(kwargs['limit'], kwargs.get('offset'))
-    else:
-        assert kwargs.get('offset') is None, "'offset' is not allowed without 'limit'"
-        limit_sql = ""
 
-    return select, " FROM " + ",".join(tables) + (where and " WHERE " + " AND ".join(where) or "") + (order_by and " ORDER BY " + order_by or "") + limit_sql, params
+    sql = " FROM " + ",".join(tables) + (where and " WHERE " + " AND ".join(where) or "") + (order_by and " ORDER BY " + order_by or "")
+
+    if (db.DATABASE_ENGINE != 'oracle'):
+        # LIMIT and OFFSET clauses
+        if kwargs.get('limit') is not None:
+            limit_sql = " %s " % db.get_limit_offset_sql(kwargs['limit'], kwargs.get('offset'))
+        else:
+            assert kwargs.get('offset') is None, "'offset' is not allowed without 'limit'"
+            limit_sql = ""
+
+
+        full_query = "SELECT " + (kwargs.get('distinct') and "DISTINCT " or "") + ",".join(select) + sql + limit_sql
+        return select, sql + limit_sql, params, full_query
+    else:
+        # To support limits and offsets, Oracle requires some funky rewriting of an otherwise normal looking query.
+        
+        select_clause = ",".join(select)
+        distinct = (kwargs.get('distinct') and "DISTINCT " or "")
+        from_clause = ",".join(tables)
+        where_clause = (where and " WHERE " + " AND ".join(where) or "")
+        
+        if order_by:            
+            order_by_clause = " OVER (ORDER BY %s )" % (order_by)
+        else:
+            #Oracle's row_number() function always requires an order-by clause.
+            #So we need to define a default order-by, since none was provided.
+            order_by_clause = " OVER (ORDER BY %s.%s)" % (opts.db_table, opts.fields[0].name)            
+        
+        # limit_and_offset_clause
+        limit = kwargs.get('limit',0)
+        offset = kwargs.get('offset',0)
+        
+        limit_and_offset_clause = ''
+        if limit:
+            limit = int(limit)
+            offset = int(offset)
+            limit_and_offset_clause = "WHERE rn > %s AND rn <= %s" % (offset, limit+offset)
+        else:
+            limit_and_offset_clause = "WHERE rn > %s" % (offset)
+                                 
+        full_query = """SELECT * FROM 
+                         (SELECT %s                  
+                          %s,
+                          ROW_NUMBER() %s AS rn
+                          FROM %s
+                          %s
+                         )
+                         %s
+                     """ % (distinct, select_clause, order_by_clause, from_clause, where_clause, limit_and_offset_clause)
+        return select, sql, params, full_query                               
+
 
 def function_get_in_bulk(opts, klass, *args, **kwargs):
     id_list = args and args[0] or kwargs['id_list']
@@ -1366,7 +1411,7 @@
     kwargs['order_by'] = [] # Clear this because it'll mess things up otherwise.
     if field.null:
         kwargs.setdefault('where', []).append('%s.%s IS NOT NULL' % (opts.db_table, field.column))
-    select, sql, params = function_get_sql_clause(opts, **kwargs)
+    select, sql, params, full_query = function_get_sql_clause(opts, **kwargs)
     sql = 'SELECT %s %s GROUP BY 1 ORDER BY 1' % (db.get_date_trunc_sql(kind, '%s.%s' % (opts.db_table, field.column)), sql)
     cursor = db.db.cursor()
     cursor.execute(sql, params)
--- trunk/django/django/core/meta/fields.py (revision 656)
+++ trunk/django/django/core/meta/fields.py (working copy)
@@ -1,4 +1,5 @@
 from django.conf import settings
+from django.core import db
 from django.core import formfields, validators
 from django.core.exceptions import ObjectDoesNotExist
 from django.utils.functional import curry
@@ -120,6 +121,12 @@
 
     def get_db_prep_save(self, value):
         "Returns field's value prepared for saving into a database."
+        
+        # Oracle treats empty strings ('') the same as NULLs.
+        # To get around this wart, we need to change it to something else...
+        if value == '':
+            string_replacement = getattr(db,'EMPTY_STR_EQUIV','')
+            value = string_replacement
         return value
 
     def get_db_prep_lookup(self, lookup_type, value):
@@ -129,7 +136,12 @@
         elif lookup_type in ('range', 'in'):
             return value
         elif lookup_type == 'year':
-            return ['%s-01-01' % value, '%s-12-31' % value]
+            if settings.DATABASE_ENGINE == 'oracle':              
+                from_dt = datetime.date(int(value), 01, 01)
+                to_dt = datetime.date(int(value), 12, 31)
+                return [from_dt, to_dt]
+            else:            
+                return ['%s-01-01' % value, '%s-12-31' % value]
         elif lookup_type in ('contains', 'icontains'):
             return ["%%%s%%" % prep_for_like_query(value)]
         elif lookup_type == 'iexact':
@@ -287,6 +299,14 @@
         kwargs['blank'] = True
         Field.__init__(self, *args, **kwargs)
 
+    def get_db_prep_lookup(self, lookup_type, value):
+        if settings.DATABASE_ENGINE == 'oracle':
+            if value == 'True':
+                value = 1
+            elif value == 'False':
+                value = 0
+        return Field.get_db_prep_lookup(self, lookup_type, value)
+
     def get_manipulator_field_objs(self):
         return [formfields.CheckboxField]
 
@@ -305,12 +325,30 @@
         if auto_now or auto_now_add:
             kwargs['editable'] = False
         Field.__init__(self, verbose_name, name, **kwargs)
-
-    def get_db_prep_lookup(self, lookup_type, value):
-        if lookup_type == 'range':
-            value = [str(v) for v in value]
-        else:
-            value = str(value)
+
+    def get_db_prep_lookup(self, lookup_type, value):           
+        # Oracle driver can deal with datetime.time objects natively 
+        # and doesn't need to convert to string
+        if settings.DATABASE_ENGINE == 'oracle':        
+            if lookup_type == 'range':
+                value = [v for v in value]
+            #elif lookup_type == 'day':
+            #    value = int(value)
+            elif lookup_type in ('exact', 'gt', 'gte', 'lt', 'lte', 'ne'):
+                if type(value) == str:
+                    pattern = r'^\d\d\d\d-\d\d-\d\d$' # "YYYY-MM-DD"
+                    from re import search
+                    if search(pattern, value) != None:
+                        year, month, day = map(int, value.split('-'))
+                        value = datetime.date(year, month, day)
+            else:
+                value = value
+        else:
+            if lookup_type == 'range':
+                value = [str(v) for v in value]
+            else:
+                value = str(value)            
+            
         return Field.get_db_prep_lookup(self, lookup_type, value)
 
     def pre_save(self, value, add):
@@ -321,22 +359,30 @@
     def get_db_prep_save(self, value):
         # Casts dates into string format for entry into database.
         if value is not None:
-            value = value.strftime('%Y-%m-%d')
-        return Field.get_db_prep_save(self, value)
+            if settings.DATABASE_ENGINE != 'oracle':
+            #Oracle does not need a string conversion
+                value = value.strftime('%Y-%m-%d')
+        return Field.get_db_prep_save(self, value)
 
     def get_manipulator_field_objs(self):
         return [formfields.DateField]
 
 class DateTimeField(DateField):
     def get_db_prep_save(self, value):
-        # Casts dates into string format for entry into database.
-        if value is not None:
-            # MySQL will throw a warning if microseconds are given, because it
-            # doesn't support microseconds.
-            if settings.DATABASE_ENGINE == 'mysql':
-                value = value.replace(microsecond=0)
-            value = str(value)
-        return Field.get_db_prep_save(self, value)
+        # Casts dates into string format for entry into database.
+        if value is not None:
+            # MySQL will throw a warning if microseconds are given, because it
+            # doesn't support microseconds.
+            if settings.DATABASE_ENGINE == 'mysql':
+                value = value.replace(microsecond=0)
+                value = str(value)
+            # Oracle will choke if microseconds are given...
+            elif settings.DATABASE_ENGINE == 'oracle':
+                value = value.replace(microsecond=0)
+            else:
+                value = str(value)
+            
+        return Field.get_db_prep_save(self, value)
 
     def get_manipulator_field_objs(self):
         return [formfields.DateField, formfields.TimeField]
@@ -513,12 +559,21 @@
         if auto_now or auto_now_add:
             kwargs['editable'] = False
         Field.__init__(self, verbose_name, name, **kwargs)
-
+
     def get_db_prep_lookup(self, lookup_type, value):
-        if lookup_type == 'range':
-            value = [str(v) for v in value]
-        else:
-            value = str(value)
+        # Oracle driver can deal with datetime.time objects natively 
+        # and doesn't need to convert to string
+        if settings.DATABASE_ENGINE == 'oracle':        
+            if lookup_type == 'range':
+                value = [v for v in value]
+            else:
+                value = value
+        else:
+            if lookup_type == 'range':
+                value = [str(v) for v in value]
+            else:
+                value = str(value)
+                
         return Field.get_db_prep_lookup(self, lookup_type, value)
 
     def pre_save(self, value, add):
@@ -529,11 +584,16 @@
     def get_db_prep_save(self, value):
         # Casts dates into string format for entry into database.
         if value is not None:
-            # MySQL will throw a warning if microseconds are given, because it
-            # doesn't support microseconds.
-            if settings.DATABASE_ENGINE == 'mysql':
-                value = value.replace(microsecond=0)
-            value = str(value)
+            # MySQL will throw a warning if microseconds are given, because it
+            # doesn't support microseconds.
+            if settings.DATABASE_ENGINE == 'mysql':
+                value = value.replace(microsecond=0)
+                value = str(value)
+            # Oracle will choke if microseconds are given...
+            elif settings.DATABASE_ENGINE == 'oracle':
+                value = value.replace(microsecond=0)
+            else:
+                value = str(value)
         return Field.get_db_prep_save(self, value)
 
     def get_manipulator_field_objs(self):
--- trunk/django/django/core/management.py  (revision 656)
+++ trunk/django/django/core/management.py  (working copy)
@@ -20,7 +20,7 @@
 ADMIN_TEMPLATE_DIR = os.path.join(django.__path__[0], 'conf/admin_templates')
 
 def _get_packages_insert(app_label):
-    return "INSERT INTO packages (label, name) VALUES ('%s', '%s');" % (app_label, app_label)
+    return "INSERT INTO packages (label, name) VALUES ('%s', '%s')" % (app_label, app_label)
 
 def _get_permission_codename(action, opts):
     return '%s_%s' % (action, opts.object_name.lower())
@@ -34,11 +34,11 @@
     return perms + list(opts.permissions)
 
 def _get_permission_insert(name, codename, opts):
-    return "INSERT INTO auth_permissions (name, package, codename) VALUES ('%s', '%s', '%s');" % \
+    return "INSERT INTO auth_permissions (name, package, codename) VALUES ('%s', '%s', '%s')" % \
         (name.replace("'", "''"), opts.app_label, codename)
 
 def _get_contenttype_insert(opts):
-    return "INSERT INTO content_types (name, package, python_module_name) VALUES ('%s', '%s', '%s');" % \
+    return "INSERT INTO content_types (name, package, python_module_name) VALUES ('%s', '%s', '%s')" % \
         (opts.verbose_name, opts.app_label, opts.module_name)
 
 def _is_valid_dir_name(s):
@@ -85,9 +85,26 @@
         full_statement = ['CREATE TABLE %s (' % opts.db_table]
         for i, line in enumerate(table_output): # Combine and add commas.
             full_statement.append('    %s%s' % (line, i < len(table_output)-1 and ',' or ''))
-        full_statement.append(');')
+        full_statement.append(')')
         final_output.append('\n'.join(full_statement))
+        
+        if (db.DATABASE_ENGINE == 'oracle') & (opts.has_auto_field):
+            # To simulate auto-incrementing primary keys in Oracle
+                        
+            sequence_statement = 'CREATE SEQUENCE %s_sq\n' % opts.db_table
+            final_output.append(sequence_statement)
+            
+            trigger_statement = "" + \
+                "CREATE OR REPLACE trigger %s_tr\n"    % opts.db_table + \
+                "  before insert on %s\n"           % opts.db_table + \
+                "    for each row\n"  + \
+                "      begin\n" + \
+                "        select %s_sq.NEXTVAL into :new.id from DUAL;\n" % opts.db_table + \
+                "      end;\n"
+            final_output.append(trigger_statement)
+                                
 
+    
     for klass in mod._MODELS:
         opts = klass._meta
         for f in opts.many_to_many:
@@ -98,10 +115,24 @@
             table_output.append('    %s_id %s NOT NULL REFERENCES %s (%s),' % \
                 (f.rel.to.object_name.lower(), db.DATA_TYPES['IntegerField'], f.rel.to.db_table, f.rel.to.pk.column))
             table_output.append('    UNIQUE (%s_id, %s_id)' % (opts.object_name.lower(), f.rel.to.object_name.lower()))
-            table_output.append(');')
+            table_output.append(')')
             final_output.append('\n'.join(table_output))
+            
+            if (db.DATABASE_ENGINE == 'oracle') & (opts.has_auto_field):
+                sequence_statement = 'CREATE SEQUENCE %s_sq\n' % f.get_m2m_db_table(opts)
+                final_output.append(sequence_statement)    
+                                
+                trigger_statement = "" + \
+                    "CREATE OR REPLACE trigger %s_tr\n"    % f.get_m2m_db_table(opts) + \
+                    "  before insert on %s\n"           % f.get_m2m_db_table(opts) + \
+                    "    for each row\n"  + \
+                    "      begin\n" + \
+                    "        select %s_sq.NEXTVAL into :new.id from DUAL;\n" % f.get_m2m_db_table(opts) + \
+                    "      end;\n"
+                final_output.append(trigger_statement)                
+            
     return final_output
-get_sql_create.help_doc = "Prints the CREATE TABLE SQL statements for the given model module name(s)."
+get_sql_create.help_doc = "Prints the CREATE TABLE SQL statements for the given model module name(s)."
 get_sql_create.args = APP_ARGS
 
 def get_sql_delete(mod):
@@ -116,23 +147,27 @@
         try:
             if cursor is not None:
                 # Check whether the table exists.
-                cursor.execute("SELECT 1 FROM %s LIMIT 1" % klass._meta.db_table)
+                cursor.execute("SELECT COUNT(1) FROM %s" % klass._meta.db_table)
         except:
             # The table doesn't exist, so it doesn't need to be dropped.
             db.db.rollback()
         else:
             output.append("DROP TABLE %s;" % klass._meta.db_table)
+            if db.DATABASE_ENGINE == 'oracle':
+                output.append("DROP SEQUENCE %s_sq" % klass._meta.db_table)
+            
     for klass in mod._MODELS:
         opts = klass._meta
         for f in opts.many_to_many:
             try:
                 if cursor is not None:
-                    cursor.execute("SELECT 1 FROM %s LIMIT 1" % f.get_m2m_db_table(opts))
+                    cursor.execute("SELECT COUNT(1) FROM %s" % f.get_m2m_db_table(opts))
             except:
                 db.db.rollback()
             else:
                 output.append("DROP TABLE %s;" % f.get_m2m_db_table(opts))
-
+                if db.DATABASE_ENGINE == 'oracle':
+                    output.append("DROP SEQUENCE %s_sq" % f.get_m2m_db_table(opts))
     app_label = mod._MODELS[0]._meta.app_label
 
     # Delete from packages, auth_permissions, content_types.
@@ -181,12 +216,15 @@
 
 def get_sql_sequence_reset(mod):
     "Returns a list of the SQL statements to reset PostgreSQL sequences for the given module."
-    from django.core import meta
+    from django.core import db, meta
     output = []
     for klass in mod._MODELS:
         for f in klass._meta.fields:
-            if isinstance(f, meta.AutoField):
-                output.append("SELECT setval('%s_%s_seq', (SELECT max(%s) FROM %s));" % (klass._meta.db_table, f.column, f.column, klass._meta.db_table))
+            if isinstance(f, meta.AutoField):
+                if db.DATABASE_ENGINE == 'postgresql':
+                    output.append("SELECT setval('%s_%s_seq', (SELECT max(%s) FROM %s));" % (klass._meta.db_table, f.column, f.column, klass._meta.db_table))
+                else:
+                    raise "Not implemented yet for %s!" % (db.DATABASE_ENGINE)
     return output
 get_sql_sequence_reset.help_doc = "Prints the SQL statements for resetting PostgreSQL sequences for the given model module name(s)."
 get_sql_sequence_reset.args = APP_ARGS
--- trunk/django/django/middleware/sessions.py  (revision 656)
+++ trunk/django/django/middleware/sessions.py  (working copy)
@@ -1,4 +1,4 @@
-from django.conf.settings import SESSION_COOKIE_NAME, SESSION_COOKIE_AGE, SESSION_COOKIE_DOMAIN
+from django.conf.settings import SESSION_COOKIE_NAME, SESSION_COOKIE_AGE, SESSION_COOKIE_DOMAIN, DATABASE_ENGINE
 from django.models.core import sessions
 import datetime
 
@@ -64,8 +64,12 @@
             modified = False
         if modified:
             session_key = request.session.session_key or sessions.get_new_session_key()
+            expire_date = datetime.datetime.now()
+            #Oracle and Mysql can't deal with microseconds
+            if DATABASE_ENGINE in ['oracle','mysql']:
+                expire_date = expire_date.replace(microsecond=0)            
             new_session = sessions.save(session_key, request.session._session,
-                datetime.datetime.now() + datetime.timedelta(seconds=SESSION_COOKIE_AGE))
+                expire_date + datetime.timedelta(seconds=SESSION_COOKIE_AGE))
             # TODO: Accept variable session length and domain.
             response.set_cookie(SESSION_COOKIE_NAME, session_key,
                 max_age=SESSION_COOKIE_AGE, domain=SESSION_COOKIE_DOMAIN)
