Django error ORA-01461 workaround?

江枫思渺然 提交于 2021-01-27 17:52:15

问题


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?


回答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.




回答2:


The workaround that worked for me is:

  • subclass unicode and set custom property input_size to cx_Oracle.CLOB
  • use new custom unicode to set django ORM model object CLOB property
  • input_size property will be used by oracle adapter when binding oracle vars (see https://github.com/django/django/blob/stable/1.6.x/django/db/backends/oracle/base.py#L796 - i.e. research usage of input_size property in the module)

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




回答3:


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.



来源:https://stackoverflow.com/questions/21197523/django-error-ora-01461-workaround

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!