Oracle mechanics

30.01.2015

DBA_AUDIT_TRAIL, ORA-64203 и китайская грамота

Filed under: commonplace,error,Oracle — Игорь Усольцев @ 01:00
Tags:

При выполнении запросов к DBA_AUDIT_TRAIL (например, для архивации данных) можно получить легковоспроизводимую ORA-64203:

SQL> select max(lengthb(sql_text)), max(lengthb(sql_bind)) from DBA_AUDIT_TRAIL;
 
select max(lengthb(sql_text)), max(lengthb(sql_bind)) from DBA_AUDIT_TRAIL
 
ORA-64203: Destination buffer too small to hold CLOB data after character set conversion.

Найти проблемные строки несложно:

SQL> declare
  2    v_entryid   number;
  3    v_sessionid number;
  4    g           number;
  5  begin
  6    for reco in (select entryid, sessionid from DBA_AUDIT_TRAIL) loop
  7      v_entryid   := reco.entryid;
  8      v_sessionid := reco.sessionid;
  9      select GREATEST(lengthb(sql_text), lengthb(sql_bind))
 10        into g
 11        from DBA_AUDIT_TRAIL
 12       where entryid = reco.entryid
 13         and sessionid = reco.sessionid;
 14    end loop;
 15  exception
 16    when others then
 17      dbms_output.put_line(v_sessionid || ' ' || v_entryid);
 18  --    raise;
 19  end;
 20  /
 
699257764 321
...

Естественно, эта строка вызывает ошибку и при простом запросе из обзора DBA_AUDIT_TRAIL:

SQL> select * from DBA_AUDIT_TRAIL where sessionid = 699257764 and entryid = 321;
 
select * from DBA_AUDIT_TRAIL where sessionid = 699257764 and entryid = 321
 
ORA-64203: Destination buffer too small to hold CLOB data after character set conversion.

, точнее, проблема вызвана содержимым одного из двух преобразованных в обзоре DBA_AUDIT_TRAIL CLOB полей — SQL_BIND:

SQL> select sql_bind from DBA_AUDIT_TRAIL where sessionid = 699257764 and entryid = 321;
 
select sql_bind from DBA_AUDIT_TRAIL where sessionid = 699257764 and entryid = 321
 
ORA-64203: Destination buffer too small to hold CLOB data after character set conversion.

, второе из CLOB полей AUD$.SQL_TEXT вызывает ошибку реже, поскольку не так часто заполняется иероглифами многобайтными символами:)

При этом сами данные в CLOB поле AUD$.SQL_BIND вполне адекватные:

SQL> select sqlbind from AUD$ where sessionid = 699257764 and entryid = 321;
 
SQLBIND
--------------------------------------------------------------------------------
 #1(1):2 #2(17):歩汬敤⁢礠卉䝔䕒䴀 ⌀㌀⠀㈀㔀㘀㜀⤀㨀吀爀愀挀攀戀愀挀欀 ⠀洀漀猀琀 爀攀挀攀渀琀 挀愀氀氀 氀愀猀琀⤀㨀਀  䘀椀氀攀 ∀⼀甀

Ошибка возникает в функции TO_NCHAR/TO_CHAR, используемой в DBA_AUDIT_TRAIL в виде:

SQL> select to_nchar(substr(sqlbind,1,2000)) from AUD$ where sessionid = 699257764 and entryid = 321;
 
select to_nchar(substr(sqlbind,1,2000)) from AUD$ where sessionid = 699257764 and entryid = 321
 
ORA-64203: Destination buffer too small to hold CLOB data after character set conversion.

, использование DBMS_LOB.SUBSTR в этом месте также завершается ошибкой, но другого уровня:

SQL> select to_nchar(dbms_lob.substr(sqlbind,2000,1)) from AUD$ where sessionid = 699257764 and entryid = 321;
 
select to_nchar(dbms_lob.substr(sqlbind,2000,1)) from AUD$ where sessionid = 699257764 and entryid = 321
 
ORA-06502: PL/SQL: numeric or value error: character string buffer too small
ORA-06512: at line 1

При этом собственно SUBSTR отрабатывает корректно:

SQL> select substr(sqlbind,1,2000) from AUD$ where sessionid = 699257764 and entryid = 321;
 
SUBSTR(SQLBIND,1,2000)
--------------------------------------------------------------------------------
 #1(1):2 #2(17):歩汬敤⁢礠卉䝔䕒䴀 ⌀㌀⠀㈀㔀㘀㜀⤀㨀吀爀愀挀攀戀愀挀欀 ⠀洀漀猀琀 爀攀挀攀渀琀 挀愀氀氀 氀愀猀琀⤀㨀਀  䘀椀氀攀 ∀⼀甀

, но в мультибайтовой/UTF бд:

SQL> select * from nls_database_parameters where parameter = 'NLS_CHARACTERSET';
 
PARAMETER                      VALUE
------------------------------ -----
NLS_CHARACTERSET               UTF8

есть естественная проблема при преобразовании обрезанного по кол-ву символов CLOB -> multibyte character в функции TO_CHAR:

SQL> select to_char(substr(sqlbind,1,2000)) from AUD$ where sessionid = 699257764 and entryid = 321;
 
select to_char(substr(sqlbind,1,2000)) from AUD$ where sessionid = 699257764 and entryid = 321
 
ORA-64203: Destination buffer too small to hold CLOB data after character set conversion.

— 2000 3-х байтных символов просто не помещаются в лимит VARCHAR2 4000 байт

Печально в этой ситуации то, что конструкция TO_NCHAR(SUBSTR(SQLBIND,1,2000)) hardcoded в системном обзоре DBA_AUDIT_TRAIL до версии 12.1.0.2 включительно

Понятно, что для решения проблемы достаточно каким-либо образом либо уменьшить кол-во выбираемых символов (создав, например, дополнительный исправленный обзор DBA_AUDIT_TRAIL2):

SQL> select to_nchar(substr(sqlbind,1,1000)) from AUD$ where sessionid = 699257764 and entryid = 321;
 
TO_NCHAR(SUBSTR(SQLBIND,1,1000
--------------------------------------------------------------------------------
 #1(1):2 #2(17):歩汬敤⁢礠卉䝔䕒䴀 ⌀㌀⠀㈀㔀㘀㜀⤀㨀吀爀愀挀攀戀愀挀欀 ⠀洀漀猀琀 爀攀挀攀渀琀 挀愀氀氀 氀愀猀琀⤀㨀਀  䘀椀氀攀 ∀⼀甀

, либо увеличить размер целевого VARCHAR2 поля, что доступно в 12c, либо архивировать данные AUD$ в таблицу с CLOB полями, ну и т.д…

Сокращение кол-ва выбираемых символов большого вреда при этом нанести не должно, ибо DBA_AUDIT_TRAIL и без того значительно обрезает реальные данные:

SQL> select max(dbms_lob.getlength(sqltext)), max(dbms_lob.getlength(sqlbind)) from AUD$; 
 
MAX(DBMS_LOB.GETLENGTH(SQLTEXT MAX(DBMS_LOB.GETLENGTH(SQLBIND
------------------------------ ------------------------------
                         30588                           8028

P.S. Интересно, что с мультибайтными символами из CLOB не дружит и другая функция LENGTHB, но по другой причине:

SQL> select length(substr(sqlbind,1,2000)) from AUD$ where sessionid = 699257764 and entryid = 321;
 
LENGTH(SUBSTR(SQLBIND,1,2000))
------------------------------
                          2000 -- LENGTH как и DBMS_LOB.GETLENGTH правильно считают unicode символы
 
SQL> select lengthb(substr(sqlbind,1,2000)) from AUD$ where sessionid = 699257764 and entryid = 321;
 
select lengthb(substr(sqlbind,1,2000)) from AUD$ where sessionid = 699257764 and entryid = 321
 
ORA-22998: CLOB or NCLOB in multibyte character set not supported -- , а байты LENGTHB считать не умеет, и честно в этом признаётся

Добавить комментарий »

Комментариев нет.

RSS feed for comments on this post. TrackBack URI

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s

Создайте бесплатный сайт или блог на WordPress.com.

%d такие блоггеры, как: