Opened 9 years ago

Closed 23 months ago

#24407 closed Bug (fixed)

Initial migration fails if app name has upper-case letters on Oracle

Reported by: Carsten Fuchs Owned by: nobody
Component: Migrations Version: 1.7
Severity: Normal Keywords: oracle
Cc: Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

In a Django project that was created pre-1.7, did not use South before and was recently upgraded to Django 1.7.4 and is otherwise fine, I was trying to convert it to use migrations, following the docs at https://docs.djangoproject.com/en/1.7/topics/migrations/#adding-migrations-to-apps

Creating the initial migrations with makemigrations went well, but the first run of migrate aborted with error "django.db.utils.DatabaseError: ORA-00955: name is already used by an existing object". Please see my post to django-users at https://groups.google.com/forum/#!topic/django-users/lVS24BGFouo for details.

Debugging this quickly brought me to MigrationExecutor.detect_soft_applied() in django/db/migrations/executor.py, whose lines 153 to 154 say:

                if model._meta.db_table not in self.connection.introspection.get_table_list(self.connection.cursor()):
                    return False

It seems that get_table_list() returns all lower-case list items, whereas model._meta.db_table was, in my case, "Lori_aub". Consequently, False was wrongly returned.

Adding lower(), i.e. changing this to

                if model._meta.db_table.lower() not in self.connection.introspection.get_table_list(self.connection.cursor()):
                    return False

entirely fixed the problem for me! :-)

(Sorry for not having a PR readily available.)

Change History (5)

comment:1 by Markus Holtermann, 9 years ago

Has patch: unset
Triage Stage: UnreviewedAccepted

Your fix will break on all other backends as they keep the table name as given (e.g. AppLabel_modelname). As far as I know Oracle converts table names to all uppercase internally. Since app_label is supposed to be all lower case and model_name = object_name.lower() the problem is with not explicitly making the app_label lower case.

This is the get_table_list() implementation in 1.7 for Oracle:

# django/db/backends/oracle/introspection.py
def get_table_list(self, cursor):
    cursor.execute("SELECT TABLE_NAME FROM USER_TABLES")
    return [row[0].lower() for row in cursor.fetchall()]

and for 1.8+

# django/db/backends/oracle/introspection.py
def get_table_list(self, cursor):
    cursor.execute("SELECT TABLE_NAME, 't' FROM USER_TABLES UNION ALL "
                   "SELECT VIEW_NAME, 'v' FROM USER_VIEWS")
    return [TableInfo(row[0].lower(), row[1]) for row in cursor.fetchall()]

comment:2 by Shai Berger, 9 years ago

Keywords: oracle added
Summary: Initial migration fails if app name has upper-case lettersInitial migration fails if app name has upper-case letters on Oracle

While it is different in symptoms and code-paths, this is essentially the same issue as #20487: The Oracle backend (not Oracle itself) converts names to uppercase on the way into the db, and so it must convert them back to lowercase on the way out -- which, in some cases, is not the correct transformation.

The right workaround for you is, of course, to fake the initial migration. I'm not closing this as a dupe (yet), because the migrations behavior deserves more careful study.

comment:3 by Carsten Fuchs, 9 years ago

Ok, many thanks for your feedback! I was not aware that the scope of this issue is actually much broader...

(In hindsight, it is clear to me that faking the initial migration was the right workaround, but I deliberately did not use --fake because from a user's perspective, knowing little about the implementation details, I did not want to forgo any other issues it might have identified. I did not expect any, but it seemed like a good extra verification step.)

comment:4 by Markus Holtermann, 9 years ago

FWIW this is also a bit related to #24337.

comment:5 by Mariusz Felisiak, 23 months ago

Resolution: fixed
Status: newclosed
Note: See TracTickets for help on using tickets.
Back to Top