--- 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)