Showing posts with label streams. Show all posts
Showing posts with label streams. Show all posts

Friday, August 8, 2014

oracle streams for auditing/logging using custom apply handler

Let say you have a table in your database and you want to log any dml on this table (source_table).

We have already created our streams administrator stradmin, now we connect with the owner of the source_table to grant him necessary privileges.

conn owner/pass
grant all on owner.source_table to stradmin;

we create a second table (source_table_audit) the same as the source table to insert all changes made in the source table, we create this table with 3 extra columns

  • update_date  =====> date of the dml
  • username      =====>  the user who made the modification
  • action           =====>  dml (delete, update or insert)

We grant all necessary privileges to the streams administrator on this table

grant all on owner.source_table_audit to stradmin;

We create a queue for this purpose

BEGIN
   DBMS_STREAMS_ADM.SET_UP_QUEUE (
      queue_table   => 'strmadmin.streams_queue_table',
      queue_name    => 'strmadmin.streams_queue');
END;
/

We create a capture rule to include only dml on our source_table

BEGIN
   DBMS_STREAMS_ADM.ADD_TABLE_RULES (
      table_name       => 'hr.employees',
      streams_type     => 'capture',
      streams_name     => 'capture_emp',
      queue_name       => 'strmadmin.streams_queue',
      include_dml      => TRUE,
      include_ddl      => FALSE,
      inclusion_rule   => TRUE);
END;
/

We add an extra attribute to capture the user who made the change

BEGIN
   DBMS_CAPTURE_ADM.INCLUDE_EXTRA_ATTRIBUTE (
      capture_name     => 'capture_emp',
      attribute_name   => 'username',
      include          => TRUE);
END;
/

CREATE OR REPLACE PROCEDURE dml_handler (in_any IN ANYDATA)
IS
   lcr          SYS.LCR$_ROW_RECORD;
   rc           PLS_INTEGER;
   command      VARCHAR2 (30);
   old_values   SYS.LCR$_ROW_LIST;
BEGIN
   -- Access the LCR
   rc := in_any.GETOBJECT (lcr);
   -- Get the object command
   typecommand := lcr.GET_COMMAND_TYPE ();

   -- Set the command_type in the row LCR to INSERT
   lcr.SET_COMMAND_TYPE ('INSERT');
   -- Set the object_name in the row LCR
   lcr.SET_OBJECT_NAME ('SOURCE_TABLE_AUDIT');

   -- Set the new values to the old values for update and delete
   IF command IN ('DELETE', 'UPDATE')
   THEN
      -- Get the old values in the row LCR
      old_values := lcr.GET_VALUES ('old');
      -- Set the old values in the row LCR to the new values in the row LCR
      lcr.SET_VALUES ('new', old_values);
      -- Set the old values in the row LCR to NULL
      lcr.SET_VALUES ('old', NULL);
   END IF;

   -- Add a SYSDATE for update_date
   lcr.ADD_COLUMN ('new', 'UPDATE_DATE', ANYDATA.ConvertDate (SYSDATE));
   -- Add a user column
   lcr.ADD_COLUMN ('new', 'USERNAME', lcr.GET_EXTRA_ATTRIBUTE ('USERNAME'));
   -- Add an action column
   lcr.ADD_COLUMN ('new', 'ACTION', ANYDATA.ConvertVarChar2 (command));
   -- Make the changes
   lcr.EXECUTE (TRUE);
   COMMIT;
END;

/

Now we can set the apply dml handler for this table

BEGIN
   DBMS_APPLY_ADM.SET_DML_HANDLER (
      object_name           => 'owner.source_table',
      object_type           => 'TABLE',
      operation_name        => 'INSERT',
      error_handler         => FALSE,
      user_procedure        => 'strmadmin.dml_handler',
      apply_database_link   => NULL,
      apply_name            => NULL);
END;
/

BEGIN
   DBMS_APPLY_ADM.SET_DML_HANDLER (
      object_name           => 'owner.source_table',
      object_type           => 'TABLE',
      operation_name        => 'UPDATE',
      error_handler         => FALSE,
      user_procedure        => 'strmadmin.dml_handler',
      apply_database_link   => NULL,
      apply_name            => NULL);
END;
/

BEGIN
   DBMS_APPLY_ADM.SET_DML_HANDLER (
      object_name           => 'owner.source_table',
      object_type           => 'TABLE',
      operation_name        => 'DELETE',
      error_handler         => FALSE,
      user_procedure        => 'strmadmin.dml_handler',
      apply_database_link   => NULL,
      apply_name            => NULL);
END;
/



Monday, June 10, 2013

Oracle Streams vs Advanced Replication

Streams is a much more efficient architecture. Rather than synchronously capturing changes in materialized view logs which adds overhead to the transaction, Streams mines the redo logs that are generated by the change in order to generate the change vector to send to the target system. That also allows the change to be replicated to the other system(s) much more quickly.

Streams is a much more flexible architecture. You can write your own custom apply processes which makes it relatively easy to inject additional processing or logging, to ignore certain changes, etc. You can send a Streams logical change record (LCR) to a JMS queue or to a non-Oracle system (though, of course, you'd need to write code to apply the LCR on the non-Oracle system).

Oracle Streams does not support the following:
  • Replication of changes to tables with columns of the following data types: BFILE, ROWID, and user-defined types (including object types, REFs, varrays, and nested tables)
  • Synchronous replication
If your current Advanced Replication environment uses these features, then these elements of the environment cannot be migrated to Oracle Streams. In this case, you might decide not to migrate the environment to Oracle Streams now, or you might decide to modify the environment so that it can be migrated to Oracle Streams.