#29199 closed Bug (fixed)

Oracle backend won't connect if password contains '@'

Reported by: Shane Allgeier Owned by: felixxm
Component: Database layer (models, ORM) Version: master
Severity: Normal Keywords: oracle
Cc: Triage Stage: Ready for checkin
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description (last modified by Shane Allgeier)

The Oracle backend won't connect if password contains '@'.

For example, using this DATABASES config in settings.py:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.oracle',
        'NAME': 'mydsn',
        'USER': 'valid_username',
        'PASSWORD': 'p@ssword',
        'HOST': '',
        'PORT': '',
    }
}

I get this traceback:

Unhandled exception in thread started by <function check_errors.<locals>.wrapper at 0x7fab58f99ae8>
Traceback (most recent call last):
  File "/home/shane/.virtualenvs/django-rms/lib/python3.6/site-packages/django/db/backends/base/base.py", line 216, in ensure_connection
    self.connect()
  File "/home/shane/.virtualenvs/django-rms/lib/python3.6/site-packages/django/db/backends/base/base.py", line 194, in connect
    self.connection = self.get_new_connection(conn_params)
  File "/home/shane/.virtualenvs/django-rms/lib/python3.6/site-packages/django/db/backends/oracle/base.py", line 208, in get_new_connection
    return Database.connect(self._connect_string(), **conn_params)
cx_Oracle.DatabaseError: ORA-12154: TNS:could not resolve the connect identifier specified

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/shane/.virtualenvs/django-rms/lib/python3.6/site-packages/django/utils/autoreload.py", line 225, in wrapper
    fn(*args, **kwargs)
  File "/home/shane/.virtualenvs/django-rms/lib/python3.6/site-packages/django/core/management/commands/runserver.py", line 123, in inner_run
    self.check_migrations()
  File "/home/shane/.virtualenvs/django-rms/lib/python3.6/site-packages/django/core/management/base.py", line 427, in check_migrations
    executor = MigrationExecutor(connections[DEFAULT_DB_ALIAS])
  File "/home/shane/.virtualenvs/django-rms/lib/python3.6/site-packages/django/db/migrations/executor.py", line 18, in __init__
    self.loader = MigrationLoader(self.connection)
  File "/home/shane/.virtualenvs/django-rms/lib/python3.6/site-packages/django/db/migrations/loader.py", line 49, in __init__
    self.build_graph()
  File "/home/shane/.virtualenvs/django-rms/lib/python3.6/site-packages/django/db/migrations/loader.py", line 206, in build_graph
    self.applied_migrations = recorder.applied_migrations()
  File "/home/shane/.virtualenvs/django-rms/lib/python3.6/site-packages/django/db/migrations/recorder.py", line 61, in applied_migrations
    if self.has_table():
  File "/home/shane/.virtualenvs/django-rms/lib/python3.6/site-packages/django/db/migrations/recorder.py", line 44, in has_table
    return self.Migration._meta.db_table in self.connection.introspection.table_names(self.connection.cursor())
  File "/home/shane/.virtualenvs/django-rms/lib/python3.6/site-packages/django/db/backends/base/base.py", line 255, in cursor
    return self._cursor()
  File "/home/shane/.virtualenvs/django-rms/lib/python3.6/site-packages/django/db/backends/base/base.py", line 232, in _cursor
    self.ensure_connection()
  File "/home/shane/.virtualenvs/django-rms/lib/python3.6/site-packages/django/db/backends/base/base.py", line 216, in ensure_connection
    self.connect()
  File "/home/shane/.virtualenvs/django-rms/lib/python3.6/site-packages/django/db/utils.py", line 89, in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
  File "/home/shane/.virtualenvs/django-rms/lib/python3.6/site-packages/django/db/backends/base/base.py", line 216, in ensure_connection
    self.connect()
  File "/home/shane/.virtualenvs/django-rms/lib/python3.6/site-packages/django/db/backends/base/base.py", line 194, in connect
    self.connection = self.get_new_connection(conn_params)
  File "/home/shane/.virtualenvs/django-rms/lib/python3.6/site-packages/django/db/backends/oracle/base.py", line 208, in get_new_connection
    return Database.connect(self._connect_string(), **conn_params)
django.db.utils.DatabaseError: ORA-12154: TNS:could not resolve the connect identifier specified

After delving into the Oracle backend, I realized that Django is calling the cx_Oracle.connect() function like so: (django/db/backends/oracle/base.py line 208)

Database.connect('valid_username/p@ssword@mydsn')

You can probably guess why cx_Oracle doesn't like this. As far as I can tell, there's no possible way to work around this bug without directly editing Django's (or cx_Oracle's) code.
The following connection method works just fine:

Database.connect('valid_username', 'p@ssword', 'mydsn')

I should also mention that there have been others that have this issue. In my case, the bureaucracy at my company won't allow me to choose my own password, so fixing Django is my only option.

Change History (6)

comment:1 Changed 21 months ago by Shane Allgeier

Description: modified (diff)
Type: UncategorizedBug

comment:2 Changed 21 months ago by felixxm

Owner: changed from nobody to felixxm
Status: newassigned
Triage Stage: UnreviewedAccepted
Version: 2.0master

comment:3 Changed 21 months ago by felixxm

Has patch: set

comment:4 Changed 21 months ago by Shane Allgeier

Patch looks good to me. Any chance we can get this backported to the older versions? At least Django 1.11 since that's the last version that supports Oracle 11.2.

Thanks for the patch, felixxm!

comment:5 Changed 21 months ago by Tim Graham

Triage Stage: AcceptedReady for checkin

This doesn't qualify for a backport based on our supported versions policy.

comment:6 Changed 21 months ago by GitHub <noreply@…>

Resolution: fixed
Status: assignedclosed

In acfc650f:

Fixed #29199 -- Fixed crash when database user password contains @ sign on Oracle.

Thanks Shane Allgeier for the report and Tim Graham for the review.

Note: See TracTickets for help on using tickets.
Back to Top