2

I have a Django (version 1.5) application that is getting this Oracle error:

ORA-01461: can bind a LONG value only for insert into a LONG column

After doing some database debugging, it appears that the problem is caused by doing an INSERT on the DJANGO_SESSION table with a long (roughly 2.5k characters) Unicode string value for SESSION_DATA (data type NCLOB).

I have been told by a coworker that the problem does not occur on UPDATEs but only on INSERTs, because the Django database code for UPDATEs contains logic to (I'm paraphrasing here) split the database write into manageable chunks, but for some reason that logic is missing for INSERTs.

Armed with this information, I figured I could work around it by writing a small piece of custom middleware that does an immediate request.session.save() with a (presumably) empty value for SESSION_DATA and then later on when the session data is set, that would cause an UPDATE instead of an INSERT.

However, it appears not to be that simple. If I insert my custom middleware in settings.MIDDLEWARE_CLASSES above the entry for 'django.contrib.sessions.middleware.SessionMiddleware', the request object doesn't have the session attribute yet, and I get this error:

AttributeError: 'WSGIRequest' object has no attribute 'session'

And if I insert my middleware after the SessionMiddleware entry, it's too late in the process and I get the original ORA-01461 error.

So, is there a way to work around the ORA-01461 error using middleware? Or at all?

John Gordon
  • 29,573
  • 7
  • 33
  • 58

3 Answers3

1

Check this Django bug: https://code.djangoproject.com/ticket/11487

I've also hit this issue (on Django 1.6) and verified it by changing the 4000 character check to say 1000 here https://github.com/django/django/blob/stable/1.6.x/django/db/backends/oracle/base.py#L699. Unfortunately as it is Django core code this is a difficult thing to fix locally.

I have not tested, but AFAICT the issue at least has been attempted to be fixed in Django 1.7 - https://github.com/django/django/blob/stable/1.7.x/django/db/backends/oracle/base.py#L781

I guess if you cannot upgrade to 1.7 (if it indeed fixes the issue), you could find the places where the session data grows too large and work around by storing it elsewhere - if that is an option.

jaywink
  • 280
  • 3
  • 14
1

The workaround that worked for me is:

This should work:

class clob_unicode(unicode):
  def __new__(cls, *args, **kwargs):
    ret = unicode.__new__(cls, *args, **kwargs)
    import cx_Oracle 
    ret.input_size = cx_Oracle.CLOB # to set proper bind var type
    return ret

Usage:

django_object.clob_variable = clob_unicode(u"some very long string")
django_object.save() # will use .input_size to set oracle bind var type

See also this answer to see discussion on how to properly subclass and init string/unicode subclass object (initialized in __new__ method and not in __init__).

Community
  • 1
  • 1
Robert Lujo
  • 15,383
  • 5
  • 56
  • 73
1

what worked for me was creating and saving a session object before the session data gets very big:

from django.contrib.sessions.backends.db import SessionStore
def view(request): 
    s = SessionStore()
    s.create()
    request.session = s
    s.save()
    # rest of the code here

subsequent queries are executed as update commands and not as inserts by oracle.

C4d
  • 3,183
  • 4
  • 29
  • 50