четверг, 20 ноября 2014 г.

Как обойти ошибку SP2-0027 при запуске скрипта в SQL*Plus

Я экспортирую таблицу с данными в формате insert из Oracle SQL Developer, получаю скрипт export.sql. На другом сервере баз данных, который доступен только локально, выполняю этот скрипт из под SQL*Plus: SQL>@/data/script.sql. Все команды insert в скрипте записаны отдельными строками. Некоторые строки очень длинные. В таких случаях при выполнении возникает ошибка: SP2-0027: Input is too long (> 2499 characters) – line ignored. Это ограничение SQLPlus: он не может прочитать больше 2500 символов в строке. В командах insert вставляются полигоны с большим количеством точек, используется тип MDSYS.SDO_ORDINATE_ARRAY с перечислением координат, разделённых запятыми.
Моя задача состояла в том, чтобы вставить переносы строк после последней запятой до колонки 2500. Строк может быть очень много, вручную это делать утомительно.
Для решения я использую Notepad++. После открытия файла выполняю команду "Замена" (Ctrl+H), выбираю режим поиска "Регуляр. выражен." и "Зациклить поиск", в поле "Найти" ввожу (^.{2470,2490}\d,) (найти фрагменты строк длиной от 2470 до 2490 символов, за которыми следует цифра и запятая), в поле "Заменить на" ввожу "\1\r\n" (добавить к найденному фрагменту символы <CR><LF>, как принято в Windows) или "\1\n", если требуется UNIX-формат. Выполняю "Заменить всё" и переносы строк вставляются в нужных местах!

среда, 15 октября 2014 г.

Неодинаковое поведение Date и TimeZone в разных браузерах (IE, Firefox, Chrome)

Я решил проверить, как браузеры учитывают переход на летнее время.
В сентябре 2014 года Microsoft выпустила обновление часовых поясов для России KB 2998527.
В Линуксе для Москвы учтены переходы 28 марта 2010, 31 октября 2010, 27 марта 2011 и 26 октября 2014:

/usr/share/zoneinfo/Europe/Moscow  Sat Mar 27 22:59:59 2010 UTC = Sun Mar 28 01:59:59 2010 MSK isdst=0 gmtoff=10800
/usr/share/zoneinfo/Europe/Moscow  Sat Mar 27 23:00:00 2010 UTC = Sun Mar 28 03:00:00 2010 MSD isdst=1 gmtoff=14400
/usr/share/zoneinfo/Europe/Moscow  Sat Oct 30 22:59:59 2010 UTC = Sun Oct 31 02:59:59 2010 MSD isdst=1 gmtoff=14400
/usr/share/zoneinfo/Europe/Moscow  Sat Oct 30 23:00:00 2010 UTC = Sun Oct 31 02:00:00 2010 MSK isdst=0 gmtoff=10800
/usr/share/zoneinfo/Europe/Moscow  Sat Mar 26 22:59:59 2011 UTC = Sun Mar 27 01:59:59 2011 MSK isdst=0 gmtoff=10800
/usr/share/zoneinfo/Europe/Moscow  Sat Mar 26 23:00:00 2011 UTC = Sun Mar 27 03:00:00 2011 MSK isdst=0 gmtoff=14400
/usr/share/zoneinfo/Europe/Moscow  Sat Oct 25 21:59:59 2014 UTC = Sun Oct 26 01:59:59 2014 MSK isdst=0 gmtoff=14400
/usr/share/zoneinfo/Europe/Moscow  Sat Oct 25 22:00:00 2014 UTC = Sun Oct 26 01:00:00 2014 MSK isdst=0 gmtoff=10800
Написал скрипт, который выводит даты после перевода часов, начиная с марта 2010 года в течение 6 лет.

<!DOCTYPE html>
<html>
<head>
 <script type="text/javascript">
  function f1() {
   var gmt = "GMT+0300";
   var day = 24*60*60*1000;
   var date = new Date(2010, 2, 1);
   var ms = date.getTime();
   var s = date.toString() + " " + date.toLocaleDateString();
   for (var i=0; i<6*365; i++) {
     ms += day;
     date = new Date(ms);
     var gmt_ = date.toString().match(/GMT.\d+/).toString();
     if (gmt_ != gmt) {
       s += "<br/>" + date.toString() + " " + date.toLocaleDateString();
    gmt = gmt_;
     }
   }
   document.getElementById("main").innerHTML  = s;
  }
 </script>
</head>
<body onload="f1();">
  <div id="main"></div>
</body>
</html>
Mozilla Firefox 31.1.0 (Oracle Linux 6.5) работает безупречно:

Mon Mar 01 2010 00:00:00 GMT+0300 (MSK) 01.03.2010
Mon Mar 29 2010 01:00:00 GMT+0400 (MSD) 29.03.2010
Mon Nov 01 2010 00:00:00 GMT+0300 (MSK) 01.11.2010
Mon Mar 28 2011 01:00:00 GMT+0400 (MSK) 28.03.2011
Mon Oct 27 2014 00:00:00 GMT+0300 (MSK) 27.10.2014
Internet Explorer 11 (Windows 7) тоже работает безупречно:

Mon Mar 01 2010 00:00:00 GMT+0300 (RTZ 2 (зима)) ‎01‎.‎03‎.‎2010
Mon Mar 29 2010 01:00:00 GMT+0400 (RTZ 2 (лето)) ‎29‎.‎03‎.‎2010
Mon Nov 01 2010 00:00:00 GMT+0300 (RTZ 2 (зима)) ‎01‎.‎11‎.‎2010
Mon Mar 28 2011 01:00:00 GMT+0400 (RTZ 2 (лето)) ‎28‎.‎03‎.‎2011
Mon Oct 27 2014 00:00:00 GMT+0300 (RTZ 2 (зима)) ‎27‎.‎10‎.‎2014
Google Chrome 33.0.1750.154 (Windows 7) не знает, что с 2011 года перевод времени отменён (за исключением 2014 года). И на летнее время переходит в январе:

Mon Mar 01 2010 00:00:00 GMT+0400 (RTZ 2 (лето)) 28.2.2010
Tue Mar 02 2010 00:00:00 GMT+0400 (RTZ 2 (лето)) 1.3.2010
Sun Oct 31 2010 23:00:00 GMT+0300 (RTZ 2 (зима)) 31.10.2010
Thu Jan 06 2011 00:00:00 GMT+0400 (RTZ 2 (лето)) 5.1.2011
Sun Oct 30 2011 23:00:00 GMT+0300 (RTZ 2 (зима)) 31.10.2011
Thu Jan 05 2012 00:00:00 GMT+0400 (RTZ 2 (лето)) 5.1.2012
Sun Oct 28 2012 23:00:00 GMT+0300 (RTZ 2 (зима)) 29.10.2012
Thu Jan 03 2013 00:00:00 GMT+0400 (RTZ 2 (лето)) 3.1.2013
Sun Oct 27 2013 23:00:00 GMT+0300 (RTZ 2 (зима)) 28.10.2013
Thu Jan 02 2014 00:00:00 GMT+0400 (RTZ 2 (лето)) 2.1.2014
Sun Oct 26 2014 23:00:00 GMT+0300 (RTZ 2 (зима)) 27.10.2014
Thu Jan 08 2015 00:00:00 GMT+0400 (RTZ 2 (лето)) 8.1.2015
Sun Oct 25 2015 23:00:00 GMT+0300 (RTZ 2 (зима)) 26.10.2015
Thu Jan 07 2016 00:00:00 GMT+0400 (RTZ 2 (лето)) 7.1.2016
Mozilla Firefox 26.0 (Windows 7) тоже сильно ошибается:

Mon Mar 01 2010 00:00:00 GMT+0400 1 Март 2010 г.
Tue Mar 02 2010 00:00:00 GMT+0400 2 Март 2010 г.
Sun Oct 31 2010 23:00:00 GMT+0300 31 Октябрь 2010 г.
Thu Jan 06 2011 00:00:00 GMT+0400 6 Январь 2011 г.
Sun Oct 30 2011 23:00:00 GMT+0300 30 Октябрь 2011 г.
Thu Jan 05 2012 00:00:00 GMT+0400 5 Январь 2012 г.
Sun Oct 28 2012 23:00:00 GMT+0300 28 Октябрь 2012 г.
Thu Jan 03 2013 00:00:00 GMT+0400 3 Январь 2013 г.
Sun Oct 27 2013 23:00:00 GMT+0300 27 Октябрь 2013 г.
Thu Jan 02 2014 00:00:00 GMT+0400 2 Январь 2014 г.
Sun Oct 26 2014 23:00:00 GMT+0300 26 Октябрь 2014 г.
Thu Jan 08 2015 00:00:00 GMT+0400 8 Январь 2015 г.
Sun Oct 25 2015 23:00:00 GMT+0300 25 Октябрь 2015 г.
Thu Jan 07 2016 00:00:00 GMT+0400 7 Январь 2016 г.
Так что объект Date ведет себя неодинаково на разных платформах. Им нужно пользоваться с большой осторожностью, а лучше найти ему замену.

четверг, 7 августа 2014 г.

Универсальное журналирование изменений данных в Oracle DB 11g в формате JSON с использованием DML хандлера и Streams

Приложение соединяется с БД через единственную учётную запись. Пользователь проходит процедуру аутентификации через внешнюю службу. В сессию БД должны быть записаны имя пользователя и IP клиента. При изменении данных должны сохраняться сведения о пользователе, о времени изменения, в какой схеме, таблице и колонке изменились данные, тип данных, предыдущие и новые значения.
Для решения этой задачи я использовал PL/JSON.
Для начала я перешёл в папку с инсталлятором PL/JSON, запустил sqlplus, создал пользователя, перешёл в его схему и установил PL/JSON:
sqlplus system/password@ORABASE
CREATE USER MONITORING_SCHEMA IDENTIFIED BY tiger ACCOUNT LOCK;
ALTER SESSION SET CURRENT_SCHEMA = MONITORING_SCHEMA;
@install.sql
Создал три таблицы: две для тестирования и последняя — для журналирования. И последовательности с триггерами для каждой таблицы.
--DROP TABLE MONITORING_SCHEMA.TPROGRAM CASCADE CONSTRAINTS PURGE;
--DROP SEQUENCE MONITORING_SCHEMA.SQ_TPROGRAM;
CREATE SEQUENCE MONITORING_SCHEMA.SQ_TPROGRAM MINVALUE 1000;
CREATE TABLE MONITORING_SCHEMA.TPROGRAM (
 IDPROGRAM INTEGER NOT NULL,
 CVAL      CHAR(1 BYTE),
 VCVAL     VARCHAR2(254 CHAR),
 NUMVAL    NUMBER,
 DATEVAL   DATE, 
 TSVAL     TIMESTAMP, 
 TSTZVAL   TIMESTAMP WITH TIME ZONE,
 TSLTZVAL  TIMESTAMP WITH LOCAL TIME ZONE,
 IYVAL     INTERVAL YEAR TO MONTH,
 IDVAL     INTERVAL DAY TO SECOND,
 CONSTRAINT PK_TPROGRAMM PRIMARY KEY (IDPROGRAM)
);
CREATE OR REPLACE TRIGGER MONITORING_SCHEMA.PK_TPROGRAM  
   BEFORE INSERT ON MONITORING_SCHEMA.TPROGRAM 
   FOR EACH ROW 
BEGIN  
   IF :NEW.IDPROGRAM IS NULL THEN 
      SELECT MONITORING_SCHEMA.SQ_TPROGRAM.NEXTVAL INTO :NEW.IDPROGRAM FROM DUAL; 
   END IF; 
END;
/

--DROP TABLE MONITORING_SCHEMA.TSUBPROGRAM CASCADE CONSTRAINTS PURGE;
--DROP SEQUENCE MONITORING_SCHEMA.SQ_TSUBPROGRAM;
CREATE SEQUENCE MONITORING_SCHEMA.SQ_TSUBPROGRAM MINVALUE 1000;
CREATE TABLE MONITORING_SCHEMA.TSUBPROGRAM (
 IDSUBPROGRAM INTEGER NOT NULL,
 NCVAL   NCHAR(2),
 NVCVAL  NVARCHAR2(2000),
 CLOBVAL CLOB,
 NCLOBVAL NCLOB,
 BLOBVAL BLOB,
 CONSTRAINT PK_TSUBPROGRAMM PRIMARY KEY (IDSUBPROGRAM)
);
CREATE OR REPLACE TRIGGER MONITORING_SCHEMA.PK_TSUBPROGRAM 
   BEFORE INSERT ON MONITORING_SCHEMA.TSUBPROGRAM 
   FOR EACH ROW 
BEGIN  
   IF :NEW.IDSUBPROGRAM IS NULL THEN 
      SELECT MONITORING_SCHEMA.SQ_TSUBPROGRAM.NEXTVAL INTO :NEW.IDSUBPROGRAM FROM DUAL; 
   END IF; 
END;
/

--DROP TABLE MONITORING_SCHEMA.TAUDIT_LOG CASCADE CONSTRAINTS PURGE;
--DROP SEQUENCE MONITORING_SCHEMA.SQ_TAUDIT_LOG;
CREATE SEQUENCE MONITORING_SCHEMA.SQ_TAUDIT_LOG MINVALUE 1000;
CREATE TABLE MONITORING_SCHEMA.TAUDIT_LOG (
   IDAUDIT_LOG    INTEGER NOT NULL,
   TABLE_OWNER    VARCHAR2(30 BYTE) NOT NULL,
   TABLE_NAME     VARCHAR2(30 BYTE) NOT NULL,
   SESSION_USER   VARCHAR2(100 CHAR) NOT NULL,
   LOG_WHEN       TIMESTAMP WITH LOCAL TIME ZONE DEFAULT LOCALTIMESTAMP NOT NULL,
   OPERATION      INTEGER NOT NULL,
   LOG_VALUES     CLOB,
   CONSTRAINT PK_TAUDIT_LOG PRIMARY KEY (IDAUDIT_LOG)
);
CREATE OR REPLACE TRIGGER MONITORING_SCHEMA.PK_TAUDIT_LOG
   BEFORE INSERT ON MONITORING_SCHEMA.TAUDIT_LOG 
   FOR EACH ROW 
BEGIN  
   IF :NEW.IDAUDIT_LOG IS NULL THEN 
      SELECT MONITORING_SCHEMA.SQ_TAUDIT_LOG.NEXTVAL INTO :NEW.IDAUDIT_LOG FROM DUAL; 
   END IF; 
END;
/
Предоставил разрешения для компиляции пакета журналирования:
CONN SYS / AS SYSDBA
GRANT EXECUTE ON SYS.DBMS_FLASHBACK TO MONITORING_SCHEMA;
GRANT EXECUTE ON SYS.DBMS_STREAMS_AUTH TO MONITORING_SCHEMA;
GRANT EXECUTE ON SYS.DBMS_STREAMS_ADM TO MONITORING_SCHEMA;
GRANT EXECUTE ON SYS.DBMS_APPLY_ADM TO MONITORING_SCHEMA;
GRANT EXECUTE ON SYS.DBMS_CAPTURE_ADM TO MONITORING_SCHEMA;
GRANT EXECUTE ON SYS.DBMS_STREAMS TO MONITORING_SCHEMA;
GRANT SELECT ON SYS.DBA_APPLY_INSTANTIATED_OBJECTS TO MONITORING_SCHEMA;
EXEC DBMS_STREAMS_AUTH.GRANT_ADMIN_PRIVILEGE(grantee => 'system', grant_privileges => TRUE);
CONN SYSTEM
Скомппилировал пакет:
CREATE OR REPLACE PACKAGE MONITORING_SCHEMA.PAK AUTHID CURRENT_USER AS
  TYPE TABLELIST IS VARRAY(100) OF VARCHAR2(30);
  PROCEDURE CREATE_LOG(schema IN VARCHAR2, tables IN TABLELIST);
  PROCEDURE DROP_ALL_LOGS;
  PROCEDURE DML_HANDLER(in_any IN ANYDATA);
END;
/

CREATE OR REPLACE PACKAGE BODY MONITORING_SCHEMA.PAK AS
  PROCEDURE CREATE_LOG(schema IN VARCHAR2, tables IN TABLELIST) AS
    database_name global_name.global_name%TYPE;
 obj_name VARCHAR2(65);
 scn NUMBER;
  BEGIN
 SELECT GLOBAL_NAME INTO database_name FROM GLOBAL_NAME;
 scn := DBMS_FLASHBACK.GET_SYSTEM_CHANGE_NUMBER();
 DBMS_STREAMS_ADM.SET_UP_QUEUE();
 FOR i IN tables.FIRST..tables.LAST LOOP
  obj_name := schema||'.'||tables(i);
  DBMS_STREAMS_ADM.ADD_TABLE_RULES(table_name => obj_name, streams_type => 'capture', 
   streams_name => 'capture_stream', include_tagged_lcr => TRUE);
  DBMS_STREAMS_ADM.ADD_TABLE_RULES(table_name => obj_name, streams_type => 'apply', 
   streams_name => 'apply_stream', include_tagged_lcr => TRUE);
  DBMS_APPLY_ADM.SET_DML_HANDLER(object_name => obj_name, object_type => 'TABLE', operation_name => 'DEFAULT', 
   user_procedure => 'MONITORING_SCHEMA.PAK.DML_HANDLER', apply_name => 'apply_stream', assemble_lobs => FALSE);
  DBMS_APPLY_ADM.SET_DML_HANDLER(object_name => obj_name, object_type => 'TABLE', operation_name => 'LOB_UPDATE', 
   user_procedure => 'MONITORING_SCHEMA.PAK.DML_HANDLER', apply_name => 'apply_stream', assemble_lobs => FALSE);
  DBMS_APPLY_ADM.SET_TABLE_INSTANTIATION_SCN(source_object_name => obj_name, 
   source_database_name => database_name, instantiation_scn => scn);
 END LOOP;
 DBMS_CAPTURE_ADM.START_CAPTURE(capture_name => 'capture_stream');
 DBMS_APPLY_ADM.SET_PARAMETER(apply_name => 'apply_stream', parameter => 'DISABLE_ON_ERROR', value => 'N');
 DBMS_APPLY_ADM.START_APPLY(apply_name => 'apply_stream');
  END;
  
  PROCEDURE DROP_ALL_LOGS AS
    database_name global_name.global_name%TYPE;
 obj_name VARCHAR2(65);
  BEGIN
 SELECT GLOBAL_NAME INTO database_name FROM GLOBAL_NAME;
 DBMS_APPLY_ADM.ALTER_APPLY(apply_name => 'apply_stream', remove_apply_tag => TRUE);
 DBMS_APPLY_ADM.STOP_APPLY(apply_name => 'apply_stream');
 DBMS_APPLY_ADM.DELETE_ALL_ERRORS('apply_stream');
 DBMS_APPLY_ADM.DROP_APPLY('apply_stream');
 DBMS_CAPTURE_ADM.STOP_CAPTURE(capture_name => 'capture_stream');
 DBMS_CAPTURE_ADM.DROP_CAPTURE('capture_stream');
 FOR rec IN (SELECT SOURCE_OBJECT_OWNER, SOURCE_OBJECT_NAME 
   FROM DBA_APPLY_INSTANTIATED_OBJECTS 
   WHERE SOURCE_DATABASE=database_name AND SOURCE_OBJECT_TYPE='TABLE') LOOP
  obj_name := rec.SOURCE_OBJECT_OWNER||'.'||rec.SOURCE_OBJECT_NAME;
  DBMS_APPLY_ADM.SET_DML_HANDLER(object_name => obj_name, object_type => 'TABLE', operation_name => 'DEFAULT', 
   error_handler  => FALSE, user_procedure => NULL, apply_name => 'apply_stream');
  DBMS_APPLY_ADM.SET_DML_HANDLER(object_name => obj_name, object_type => 'TABLE', operation_name => 'LOB_UPDATE', 
   error_handler  => FALSE, user_procedure => NULL, apply_name => 'apply_stream');
  DBMS_CAPTURE_ADM.ABORT_TABLE_INSTANTIATION(table_name  => obj_name);
  DBMS_APPLY_ADM.SET_TABLE_INSTANTIATION_SCN(source_object_name => obj_name, 
   source_database_name => database_name, instantiation_scn => NULL);
 END LOOP;
 DBMS_STREAMS_ADM.REMOVE_QUEUE('streams_queue', true, true);
  END;
  
  FUNCTION getValues(xml IN XMLType, kind IN VARCHAR2, lcr IN SYS.LCR$_ROW_RECORD) RETURN json_list AS
 res json_list := json_list();
 item json;
 doc DBMS_XMLDOM.DOMDocument;
 nl1 DBMS_XMLDOM.DOMNodeList;
 nl2 DBMS_XMLDOM.DOMNodeList;
 n1  DBMS_XMLDOM.DOMNode;
 n2  DBMS_XMLDOM.DOMNode;
 column_name VARCHAR2(30);
 data_type VARCHAR2(30);
 ch VARCHAR2(2000 CHAR);
 ret PLS_INTEGER;
 nc NCHAR(1000);
 nvc NVARCHAR2(2000);
  BEGIN
 doc := DBMS_XMLDOM.newDOMDocument(xml.EXTRACT('/ROW_LCR/'||kind||'_values'));
 nl1 := DBMS_XMLDOM.getElementsByTagName(DBMS_XMLDOM.getDocumentElement(doc), kind||'_value');
 FOR i IN 0 .. DBMS_XMLDOM.getLength(nl1) - 1 LOOP
  n1 := DBMS_XMLDOM.item(nl1, i);
  nl2 := DBMS_XMLDOM.getChildNodes(n1);
  item := json();
  FOR j IN 0 .. DBMS_XMLDOM.getLength(nl2) - 1 LOOP
   n2 := DBMS_XMLDOM.item(nl2, j);
   IF DBMS_XMLDOM.getNodeName(n2) = 'column_name' THEN
    column_name := DBMS_XMLDOM.getNodeValue(DBMS_XMLDOM.getFirstChild(n2));
    item.put('column_name', column_name);
   ELSIF DBMS_XMLDOM.getNodeName(n2) = 'data' THEN
    data_type := DBMS_XMLDOM.getNodeName(DBMS_XMLDOM.getFirstChild(n2));
    IF data_type = 'number' THEN
     ch := DBMS_XMLDOM.getNodeValue(DBMS_XMLDOM.getFirstChild(DBMS_XMLDOM.getFirstChild(n2)));
     item.put(data_type, cast(ch as number));
    ELSIF data_type IN ('date', 'timestamp', 'timestamp_tz', 'timestamp_ltz') THEN
     ch := DBMS_XMLDOM.getNodeValue(DBMS_XMLDOM.getFirstChild(
      DBMS_XMLDOM.getFirstChild(DBMS_XMLDOM.getFirstChild(n2))));
     item.put(data_type, ch);
    ELSIF data_type = 'nchar' THEN
     ret := lcr.GET_VALUE(kind, column_name).GETNCHAR(nc);
     item.put(data_type, TRIM(TO_CHAR(nc)));
    ELSIF data_type = 'nvarchar2' THEN
     ret := lcr.GET_VALUE(kind, column_name).GETNVARCHAR2(nvc);
     item.put(data_type, TO_CHAR(nvc));
    ELSE
     ch := SUBSTR(DBMS_XMLDOM.getNodeValue(DBMS_XMLDOM.getFirstChild(DBMS_XMLDOM.getFirstChild(n2))), 1, 2000);
     item.put(data_type, ch);
   END IF;
   res.append(item.to_json_value);
    END IF;
  END LOOP;
   END LOOP;
 RETURN res;
  END;
  
  PROCEDURE DML_HANDLER(in_any IN ANYDATA) AS
   lcr SYS.LCR$_ROW_RECORD;
   rc PLS_INTEGER;
   command VARCHAR2(32);
   oper PLS_INTEGER;
   towner VARCHAR2(30);
   tname VARCHAR2(30);
   login VARCHAR2(100);
   xml XMLTYPE;
   log_val json := json();
   clb clob;
   msg VARCHAR2(500);
 BEGIN
   JSON_PRINTER.ascii_output := FALSE;
   rc := in_any.GETOBJECT(lcr);
   command := lcr.GET_COMMAND_TYPE();
   SELECT ACTION INTO oper FROM AUDIT_ACTIONS WHERE NAME=command;
   towner := lcr.GET_OBJECT_OWNER();
   tname := lcr.GET_OBJECT_NAME();
   login := NVL(UTL_RAW.CAST_TO_VARCHAR2(lcr.GET_TAG()), 'UNKNOWN');
   xml := DBMS_STREAMS.CONVERT_LCR_TO_XML(in_any);
   SELECT DELETEXML(xml, '//data/*[name()=''nchar'' or name()=''nvarchar2'']/node()') INTO xml FROM DUAL;
   IF command IN ('DELETE', 'UPDATE') THEN
     log_val.put('old_values', getValues(xml, 'old', lcr));
   END IF;
   IF command IN ('INSERT', 'UPDATE') THEN
     log_val.put('new_values', getValues(xml, 'new', lcr));
   END IF;
   clb := empty_clob();
   DBMS_LOB.CREATETEMPORARY(clb, TRUE);
   log_val.to_clob(clb);
   INSERT INTO MONITORING_SCHEMA.TAUDIT_LOG(IDAUDIT_LOG, TABLE_OWNER, TABLE_NAME, SESSION_USER, LOG_WHEN, OPERATION, LOG_VALUES)
   VALUES (MONITORING_SCHEMA.SQ_TAUDIT_LOG.NEXTVAL, towner, tname, login, LOCALTIMESTAMP, oper, clb);
   DBMS_LOB.FREETEMPORARY(clb);
 EXCEPTION WHEN OTHERS THEN
   msg := SUBSTR(SQLERRM, 1, 500);
   INSERT INTO MONITORING_SCHEMA.TAUDIT_LOG(IDAUDIT_LOG, TABLE_OWNER, TABLE_NAME, SESSION_USER, LOG_WHEN, OPERATION, LOG_VALUES)
   VALUES (MONITORING_SCHEMA.SQ_TAUDIT_LOG.NEXTVAL, towner, tname, login, LOCALTIMESTAMP, oper, TO_CLOB(msg));
 END;
END;
/
Запустил журналирование изменений для двух таблиц:
EXEC MONITORING_SCHEMA.PAK.CREATE_LOG('MONITORING_SCHEMA', MONITORING_SCHEMA.PAK.TABLELIST('TPROGRAM', 'TSUBPROGRAM'));
Можно проверить, что журналирование запущено:
SELECT * FROM DBA_STREAMS_ADMINISTRATOR;
SELECT * FROM DBA_STREAMS_TABLE_RULES;
SELECT * FROM DBA_CAPTURE;
SELECT * FROM DBA_CAPTURE_PREPARED_TABLES;
SELECT * FROM DBA_APPLY;
SELECT * FROM DBA_APPLY_INSTANTIATED_OBJECTS;
SELECT * FROM DBA_APPLY_DML_HANDLERS;
Для тестирования выполнил некоторые изменения данных, предварительно сохранив в сессии данные пользователя:
EXEC DBMS_STREAMS.SET_TAG(tag => UTL_RAW.CAST_TO_RAW('Иванов: 192.168.0.35'));

INSERT INTO MONITORING_SCHEMA.TPROGRAM (CVAL, VCVAL, NUMVAL, DATEVAL, TSVAL, TSTZVAL, TSLTZVAL, IYVAL, IDVAL) VALUES 
  ('A', 'Двадцать', 20, SYSDATE, SYSTIMESTAMP, CURRENT_TIMESTAMP, LOCALTIMESTAMP, TO_YMINTERVAL('01-02'), TO_DSINTERVAL('2 10:20:30.456'));
COMMIT;
UPDATE MONITORING_SCHEMA.TPROGRAM SET VCVAL='Тридцать', NUMVAL=30;
COMMIT;
DELETE FROM MONITORING_SCHEMA.TPROGRAM;
COMMIT;

CREATE OR REPLACE DIRECTORY IMG AS '/u01/app/oracle/databases/img';
DECLARE
 l_blob   BLOB;
 l_bfile  BFILE := BFILENAME('IMG', 'img01.png');
BEGIN
 INSERT INTO MONITORING_SCHEMA.TSUBPROGRAM (CLOBVAL, NCLOBVAL, BLOBVAL) VALUES 
  ('Текст CLOB', N'Текст NCLOB', empty_blob()) RETURNING BLOBVAL INTO l_blob;
 DBMS_LOB.OPEN(l_bfile, DBMS_LOB.LOB_READONLY);
 DBMS_LOB.LOADFROMFILE(l_blob, l_bfile, DBMS_LOB.GETLENGTH(l_bfile));
 DBMS_LOB.CLOSE(l_bfile);
 COMMIT;
END;
/
DROP DIRECTORY IMG;
UPDATE MONITORING_SCHEMA.TSUBPROGRAM SET NCVAL=N'Ру', NVCVAL=N'Текст NVARCHAR2';
COMMIT;
DELETE FROM MONITORING_SCHEMA.TSUBPROGRAM;
COMMIT;
В таблицу TAUDIT_LOG добавились строки:
IDAUDIT_LOG TABLE_OWNER TABLE_NAME SESSION_USER LOG_WHEN OPERATION LOG_VALUES
1000 MONITORING_SCHEMA TSUBPROGRAM Иванов: 192.168.0.35 08.08.14 08:39:13,761547000 2 {"new_values":[{"column_name":"IDSUBPROGRAM","number":1000},{"column_name":"NCVAL","nchar":""},{"column_name":"NVCVAL","nvarchar2":""},{"column_name":"CLOBVAL","varchar2":""},{"column_name":"NCLOBVAL","raw":""},{"column_name":"BLOBVAL","raw":""}]}
1001 MONITORING_SCHEMA TPROGRAM Иванов: 192.168.0.35 08.08.14 08:39:13,762111000 2 {"new_values":[{"column_name":"IDPROGRAM","number":1000},{"column_name":"CVAL","char":"A"},{"column_name":"VCVAL","varchar2":"Двадцать"},{"column_name":"NUMVAL","number":20},{"column_name":"DATEVAL","date":" 2014/08/08 08:39:13"},{"column_name":"TSVAL","timestamp":" 2014/08/08 08:39:13,463334000"},{"column_name":"TSTZVAL","timestamp_tz":" 2014/08/08 08:39:13,463336000 EUROPE/MOSCOW"},{"column_name":"TSLTZVAL","timestamp_ltz":" 2014/08/08 04:39:13,463336000"},{"column_name":"IYVAL","interval_ym":"+000000001-02"},{"column_name":"IDVAL","interval_ds":"+000000002 10:20:30.456000000"}]}
1002 MONITORING_SCHEMA TSUBPROGRAM Иванов: 192.168.0.35 08.08.14 08:39:13,766396000 6 {"old_values":[{"column_name":"IDSUBPROGRAM","number":1000},{"column_name":"NCVAL","nchar":""},{"column_name":"NVCVAL","nvarchar2":""}],"new_values":[{"column_name":"CLOBVAL","varchar2":"Текст CLOB"},{"column_name":"NCLOBVAL","nvarchar2":"Текст NCLOB"}]}
1003 MONITORING_SCHEMA TPROGRAM Иванов: 192.168.0.35 08.08.14 08:39:13,767247000 6 {"old_values":[{"column_name":"IDPROGRAM","number":1000},{"column_name":"VCVAL","varchar2":"Двадцать"},{"column_name":"NUMVAL","number":20}],"new_values":[{"column_name":"VCVAL","varchar2":"Тридцать"},{"column_name":"NUMVAL","number":30}]}
1004 MONITORING_SCHEMA TSUBPROGRAM Иванов: 192.168.0.35 08.08.14 08:39:13,770783000 6 {"old_values":[{"column_name":"IDSUBPROGRAM","number":1000}],"new_values":[{"column_name":"BLOBVAL","raw":"89504E470D0A1A0A0000000D494844520000001C0000001C080300000045D32FA60000001974455874536F6674776172650041646F626520496D616765526561647971C9653C00000300504C54458C6244895D3B331919FFFFA5491D13FFFF97FFFF8732120AACA2A190623E3432197C63313C080B968541EEE672FFFF8D5A5A5A3D32322A0000D6C964FFFFFF62322150211241150C3B2B2BB1A355DBD6D52018188E623EBBA552C6B65A927D3D7E5133A18C46AD954B250502968141735A2DAF994D74472D4924137E5336130101B7A955C6BEBD5148485F2E21784C30A9974BFFFF9EFFFF9C865A394A3D3C2A2121634626300003694D27C3BBBA746B6B6E542BB5ACAB7C562E4A40402C0C07080D152C0C0D8E7A42855939522D118A5E3C544A4A332929352E2E0B0101602F215325171B1B244239395050510000003B060A815536875B3AFFFF94FFFF7D5A2F1E8256365D371EFFFF80FFFF7EFFFF91835737FFFF7FFFFF93FFFF84FFFF8F43180EFFFF85FFFF99FFFF92FFFF8EFFFF95FFFF8B825637FFFF8AFFFF88A28E47D2CCCBE9E6E58153345E3320FFFF81FFFFACDDD0698458375939258B81818A5E3BFFFF90FFFF985D371C97863DD1BB5EFFFB80E1DDDCC3B65BFFFF838F674A3D43265E512A3E0D0BE9E26E8F84845E5E5EECE578FFFF9A8453338A7A446060604F2E22B3A35C120B15C5B75B00000D201010663823C0B7B7463A20755A31A994474C5254A38B789F8B4B1A1111A1806BC2B060CBB6685B6163C3BCBA9C8775856331856F37A38642A48E49FFFDEF3A3A3D584E4EF6EE803E0900917D42C7B95C7A7070C9B95D43494DCFBC684B4B4BB9B0AFAA9048AD974CF1EEEEFAF679DFD770D0CAC8B1B665D3CDCCD4CECE8C5D3BA599998C5F3CCCC5C4634027654D25C5CC63403535845737E6E2E1FDFA84968246F0E97D524745BDB4B4434321DED9D8A58773DFE076A98C795B301EFFFFA1FDFDFDDAD26E78783B9E949452585A9A8D8E86723583703C8B7239B29F5961381E582C18C1B8B8C2BAB8572A19A08B456F666637170C3A1D0D4C413FF6F280B3A9A8CFCF6BEAE174F5F07542221E906748966A43C5B55B93803FD4C763867C7C5D4124070D08545428E7DF75785E307F5930453B3A463D3DA49048A6954AF5EF78545454EEE67BE8E177F7F780FFFFFFC9049A930000010074524E53FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"}]}
1005 MONITORING_SCHEMA TPROGRAM Иванов: 192.168.0.35 08.08.14 08:39:13,775046000 7 {"old_values":[{"column_name":"IDPROGRAM","number":1000},{"column_name":"CVAL","char":"A"},{"column_name":"VCVAL","varchar2":"Тридцать"},{"column_name":"NUMVAL","number":30},{"column_name":"DATEVAL","date":" 2014/08/08 08:39:13"},{"column_name":"TSVAL","timestamp":" 2014/08/08 08:39:13,463334000"},{"column_name":"TSTZVAL","timestamp_tz":" 2014/08/08 08:39:13,463336000 EUROPE/MOSCOW"},{"column_name":"TSLTZVAL","timestamp_ltz":" 2014/08/08 04:39:13,463336000"},{"column_name":"IYVAL","interval_ym":"+000000001-02"},{"column_name":"IDVAL","interval_ds":"+000000002 10:20:30.456000000"}]}
1006 MONITORING_SCHEMA TSUBPROGRAM Иванов: 192.168.0.35 08.08.14 08:39:13,776091000 6 {"old_values":[{"column_name":"IDSUBPROGRAM","number":1000},{"column_name":"NCVAL","nchar":""},{"column_name":"NVCVAL","nvarchar2":""}],"new_values":[{"column_name":"NCVAL","nchar":"Ру"},{"column_name":"NVCVAL","nvarchar2":"Текст NVARCHAR2"}]}
1007 MONITORING_SCHEMA TSUBPROGRAM Иванов: 192.168.0.35 08.08.14 08:39:13,779515000 7 {"old_values":[{"column_name":"IDSUBPROGRAM","number":1000},{"column_name":"NCVAL","nchar":"Ру"},{"column_name":"NVCVAL","nvarchar2":"Текст NVARCHAR2"}]}
Здесь код операции соответствует таблице AUDIT_ACTIONS.
Для остановки журналирования достаточно выполнить:
EXEC MONITORING_SCHEMA.PAK.DROP_ALL_LOGS;
В журнал можно записывать данные любого типа и размера, но целесообразно длинные строки обрезать до 2000 символов.