Skip Headers
Oracle® Streams Replication Administrator's Guide
11g Release 1 (11.1)

Part Number B28322-01
Go to Documentation Home
Home
Go to Book List
Book List
Go to Table of Contents
Contents
Go to Index
Index
Go to Master Index
Master Index
Go to Feedback page
Contact Us

Go to previous page
Previous
Go to next page
Next
View PDF

20 Single-Source Heterogeneous Replication Example

This chapter illustrates an example of a single-source heterogeneous replication environment that can be constructed using Oracle Streams, as well as the tasks required to add new objects and databases to such an environment.

This chapter contains these topics:

Overview of the Single-Source Heterogeneous Replication Example

This example illustrates using Oracle Streams to replicate data between four databases. The environment is heterogeneous because three of the databases are Oracle databases and one is a Sybase database. DML and DDL changes made to tables in the hr schema at the dbs1.net Oracle database are captured and propagated to the other two Oracle databases. Only DML changes are captured and propagated to the dbs4.net database, because an apply process cannot apply DDL changes to a non-Oracle database. Changes to the hr schema occur only at dbs1.net. The hr schema is read-only at the other databases in the environment.

Figure 20-1 provides an overview of the environment.

Figure 20-1 Sample Environment that Shares Data from a Single Source Database

Description of Figure 20-1 follows
Description of "Figure 20-1 Sample Environment that Shares Data from a Single Source Database"

As illustrated in Figure 20-1, dbs1.net contains the following tables in the hr schema:

This example uses directed networks, which means that captured changes at a source database are propagated to another database through one or more intermediate databases. Here, the dbs1.net database propagates changes to the dbs3.net database through the intermediate database dbs2.net. This configuration is an example of queue forwarding in a directed network. Also, the dbs1.net database propagates changes to the dbs2.net database, which applies the changes directly to the dbs4.net database through an Oracle Database Gateway.

Some of the databases in the environment do not have certain tables. If the database is not an intermediate database for a table and the database does not contain the table, then changes to the table do not need to be propagated to that database. For example, the departments, employees, job_history, and jobs tables do not exist at dbs3.net. Therefore, dbs2.net does not propagate changes to these tables to dbs3.net.

In this example, Oracle Streams is used to perform the following series of actions:

  1. The capture process captures DML and DDL changes for all of the tables in the hr schema and enqueues them at the dbs1.net database. In this example, changes to only four of the seven tables are propagated to destination databases, but in the example that illustrates "Add Objects to an Existing Oracle Streams Replication Environment", the remaining tables in the hr schema are added to a destination database.

  2. The dbs1.net database propagates these changes in the form of messages to a queue at dbs2.net.

  3. At dbs2.net, DML changes to the jobs table are transformed into DML changes for the assignments table (which is a direct mapping of jobs) and then applied. Changes to other tables in the hr schema are not applied at dbs2.net.

  4. Because the queue at dbs3.net receives changes from the queue at dbs2.net that originated in countries, locations, and regions tables at dbs1.net, these changes are propagated from dbs2.net to dbs3.net. This configuration is an example of directed networks.

  5. The apply process at dbs3.net applies changes to the countries, locations, and regions tables.

  6. Because dbs4.net, a Sybase database, receives changes from the queue at dbs2.net to the jobs table that originated at dbs1.net, these changes are applied remotely from dbs2.net using the dbs4.net database link through an Oracle Database Gateway. This configuration is an example of heterogeneous support.

Prerequisites

The following prerequisites must be completed before you begin the example in this chapter.

Create Queues and Database Links

Complete the following steps to create queues and database links for an Oracle Streams replication environment that includes three Oracle databases and one Sybase database:

  1. Show Output and Spool Results

  2. Create the ANYDATA Queue at dbs1.net

  3. Create the Database Link at dbs1.net

  4. Create the ANYDATA Queue at dbs2.net

  5. Create the Database Links at dbs2.net

  6. Create the hr.assignments Table at dbs2.net

  7. Create the ANYDATA Queue at dbs3.net

  8. Create the Database Links at dbs2.net

  9. Drop All of the Tables in the hr Schema at dbs3.net

  10. Check the Spool Results

Note:

If you are viewing this document online, then you can copy the text from the "BEGINNING OF SCRIPT" line after this note to the next "END OF SCRIPT" line into a text editor and then edit the text to create a script for your environment. Run the script with SQL*Plus on a computer that can connect to all of the databases in the environment.
/************************* BEGINNING OF SCRIPT ******************************

Step 1   Show Output and Spool Results

Run SET ECHO ON and specify the spool file for the script. Check the spool file for errors after you run this script.

*/

SET ECHO ON
SPOOL streams_setup_single.out

/*

Step 2   Create the ANYDATA Queue at dbs1.net

Connect as the Oracle Streams administrator at the database where you want to capture changes. In this example, that database is dbs1.net.

*/

CONNECT strmadmin/user-password@dbs1.net

/*

Run the SET_UP_QUEUE procedure to create a queue named streams_queue at dbs1.net. This queue will function as the ANYDATA queue by holding the captured changes that will be propagated to other databases.

Running the SET_UP_QUEUE procedure performs the following actions:

*/

EXEC DBMS_STREAMS_ADM.SET_UP_QUEUE();

/*

Step 3   Create the Database Link at dbs1.net

Create the database link from the database where changes are captured to the database where changes are propagated. In this example, the database where changes are captured is dbs1.net, and these changes are propagated to dbs2.net.

*/

CREATE DATABASE LINK dbs2.net CONNECT TO strmadmin IDENTIFIED BY user-password 
   USING 'dbs2.net';

/*

Step 4   Create the ANYDATA Queue at dbs2.net

Connect as the Oracle Streams administrator at dbs2.net.

*/

CONNECT strmadmin/user-password@dbs2.net

/*

Run the SET_UP_QUEUE procedure to create a queue named streams_queue at dbs2.net. This queue will function as the ANYDATA queue by holding the changes that will be applied at this database and the changes that will be propagated to other databases.

Running the SET_UP_QUEUE procedure performs the following actions:

*/

EXEC  DBMS_STREAMS_ADM.SET_UP_QUEUE();

/*

Step 5   Create the Database Links at dbs2.net

Create the database links to the databases where changes are propagated. In this example, database dbs2.net propagates changes to dbs3.net, which is another Oracle database, and to dbs4.net, which is a Sybase database. Notice that the database link to the Sybase database connects to the owner of the tables, not to the Oracle Streams administrator. This database link can connect to any user at dbs4.net that has privileges to change the hr.jobs table at that database.

Note:

On some non-Oracle databases, including Sybase, you must ensure that the characters in the user name and password are in the correct case. Therefore, double quotation marks are specified for the user name and password at the Sybase database.
*/

CREATE DATABASE LINK dbs3.net CONNECT TO strmadmin IDENTIFIED BY user-password
   USING 'dbs3.net';

CREATE DATABASE LINK dbs4.net CONNECT TO "hr" IDENTIFIED BY "user-password" 
   USING 'dbs4.net';

/*

Step 6   Create the hr.assignments Table at dbs2.net

This example illustrates a rule-based transformation in which changes to the hr.jobs table at dbs1.net are transformed into changes to the hr.assignments table at dbs2.net. You must create the hr.assignments table on dbs2.net for the transformation portion of this example to work properly.

Connect as hr at dbs2.net.

*/

CONNECT hr/user-password@dbs2.net

/*

Create the hr.assignments table in the dbs2.net database.

*/

CREATE TABLE hr.assignments AS SELECT * FROM hr.jobs;

ALTER TABLE hr.assignments ADD PRIMARY KEY (job_id);

/*

Step 7   Create the ANYDATA Queue at dbs3.net

Connect as the Oracle Streams administrator at dbs3.net.

*/

CONNECT strmadmin/user-password@dbs3.net

/*

Run the SET_UP_QUEUE procedure to create a queue named streams_queue at dbs3.net. This queue will function as the ANYDATA queue by holding the changes that will be applied at this database.

Running the SET_UP_QUEUE procedure performs the following actions:

*/

EXEC  DBMS_STREAMS_ADM.SET_UP_QUEUE();

/*

Step 8   Create a Database Link at dbs3.net to dbs1.net

Create a database link from dbs3.net to dbs1.net. Later in this example, this database link is used for the instantiation of some of the database objects that were dropped in Step 9. This example uses the DBMS_DATAPUMP package to perform a network import of these database objects directly from the dbs1.net database. Because this example performs a network import, no dump file is required.

Alternatively, you can perform an export at the source database dbs1.net, transfer the export dump file to the destination database dbs3.net, and then import the export dump file at the destination database. In this case, the database link created in this step is not required.

*/

CREATE DATABASE LINK dbs1.net CONNECT TO strmadmin IDENTIFIED BY user-password
   USING 'dbs1.net';

/*

Step 9   Drop All of the Tables in the hr Schema at dbs3.net

This example illustrates instantiating tables in the hr schema by importing them from dbs1.net into dbs3.net with Data Pump. You must delete these tables at dbs3.net for the instantiation portion of this example to work properly.

Connect as hr at dbs3.net.

*/

CONNECT hr/user-password@dbs3.net

/*

Drop all tables in the hr schema in the dbs3.net database.

Note:

If you complete this step and drop all of the tables in the hr schema, then you should complete the remaining sections of this example to reinstantiate the hr schema at dbs3.net. If the hr schema does not exist in an Oracle database, then some examples in the Oracle documentation set can fail.
*/

DROP TABLE hr.countries CASCADE CONSTRAINTS;
DROP TABLE hr.departments CASCADE CONSTRAINTS;
DROP TABLE hr.employees CASCADE CONSTRAINTS;
DROP TABLE hr.job_history CASCADE CONSTRAINTS;
DROP TABLE hr.jobs CASCADE CONSTRAINTS;
DROP TABLE hr.locations CASCADE CONSTRAINTS;
DROP TABLE hr.regions CASCADE CONSTRAINTS;

/*

Step 10   Check the Spool Results

Check the streams_setup_single.out spool file to ensure that all actions finished successfully after this script is completed.

*/

SET ECHO OFF
SPOOL OFF

/*************************** END OF SCRIPT ******************************/

Example Scripts for Sharing Data from One Database

This example illustrates two ways to accomplish the replication of the tables in the hr schema using Oracle Streams.

Simple Configuration for Sharing Data from a Single Database

Complete the following steps to specify the capture, propagation, and apply definitions using primarily the DBMS_STEAMS_ADM package.

  1. Show Output and Spool Results

  2. Configure Propagation at dbs1.net

  3. Configure the Capture Process at dbs1.net

  4. Set the Instantiation SCN for the Existing Tables at Other Databases

  5. Instantiate the dbs1.net Tables at dbs3.net

  6. Configure the Apply Process at dbs3.net

  7. Specify hr as the Apply User for the Apply Process at dbs3.net

  8. Grant the hr User Execute Privilege on the Apply Process Rule Set

  9. Start the Apply Process at dbs3.net

  10. Configure Propagation at dbs2.net

  11. Create the Rule-Based Transformation for Row LCRs at dbs2.net

  12. Configure the Apply Process for Local Apply at dbs2.net

  13. Specify hr as the Apply User for the Apply Process at dbs2.net

  14. Grant the hr User Execute Privilege on the Apply Process Rule Set

  15. Start the Apply Process at dbs2.net for Local Apply

  16. Configure the Apply Process at dbs2.net for Apply at dbs4.net

  17. Start the Apply Process at dbs2.net for Apply at dbs4.net

  18. Start the Capture Process at dbs1.net

  19. Check the Spool Results

Note:

If you are viewing this document online, then you can copy the text from the "BEGINNING OF SCRIPT" line after this note to the next "END OF SCRIPT" line into a text editor and then edit the text to create a script for your environment. Run the script with SQL*Plus on a computer that can connect to all of the databases in the environment.
/************************* BEGINNING OF SCRIPT ******************************

Step 1   Show Output and Spool Results

Run SET ECHO ON and specify the spool file for the script. Check the spool file for errors after you run this script.

*/

SET ECHO ON
SPOOL streams_share_schema1.out

/*

Step 2   Configure Propagation at dbs1.net

Connect to dbs1.net as the strmadmin user.

*/
 
CONNECT strmadmin/user-password@dbs1.net

/*

Configure and schedule propagation of DML and DDL changes in the hr schema from the queue at dbs1.net to the queue at dbs2.net.

*/

BEGIN
  DBMS_STREAMS_ADM.ADD_SCHEMA_PROPAGATION_RULES(
    schema_name             => 'hr', 
    streams_name            => 'dbs1_to_dbs2', 
    source_queue_name       => 'strmadmin.streams_queue',
    destination_queue_name  => 'strmadmin.streams_queue@dbs2.net',
    include_dml             => TRUE,
    include_ddl             => TRUE,
    source_database         => 'dbs1.net',
    inclusion_rule          => TRUE,
    queue_to_queue          => TRUE);
END;
/

/*

Step 3   Configure the Capture Process at dbs1.net

Configure the capture process to capture changes to the entire hr schema at dbs1.net. This step specifies that changes to the tables in the specified schema are captured by the capture process and enqueued into the specified queue.

This step also prepares the hr schema for instantiation and enables supplemental logging for any primary key, unique key, bitmap index, and foreign key columns in the tables in this schema. Supplemental logging places additional information in the redo log for changes made to tables. The apply process needs this extra information to perform certain operations, such as unique row identification and conflict resolution. Because dbs1.net is the only database where changes are captured in this environment, it is the only database where you must specify supplemental logging for the tables in the hr schema.

*/

BEGIN
  DBMS_STREAMS_ADM.ADD_SCHEMA_RULES(
    schema_name    => 'hr',   
    streams_type   => 'capture',
    streams_name   => 'capture',
    queue_name     => 'strmadmin.streams_queue',
    include_dml    => TRUE, 
    include_ddl    => TRUE,
    inclusion_rule => TRUE);
END;
/

/*

Step 4   Set the Instantiation SCN for the Existing Tables at Other Databases

In this example, the hr.jobs table already exists at dbs2.net and dbs4.net. At dbs2.net, this table is named assignments, but it has the same shape and data as the jobs table at dbs1.net. Also, in this example, dbs4.net is a Sybase database. All of the other tables in the Oracle Streams environment are instantiated at the other destination databases using Data Pump import.

Because the hr.jobs table already exists at dbs2.net and dbs4.net, this example uses the GET_SYSTEM_CHANGE_NUMBER function in the DBMS_FLASHBACK package at dbs1.net to obtain the current SCN for the database. This SCN is used at dbs2.net to run the SET_TABLE_INSTANTIATION_SCN procedure in the DBMS_APPLY_ADM package. Running this procedure twice sets the instantiation SCN for the hr.jobs table at dbs2.net and dbs4.net.

The SET_TABLE_INSTANTIATION_SCN procedure controls which LCRs for a table are ignored by an apply process and which LCRs for a table are applied by an apply process. If the commit SCN of an LCR for a table from a source database is less than or equal to the instantiation SCN for that table at a destination database, then the apply process at the destination database discards the LCR. Otherwise, the apply process applies the LCR.

In this example, both of the apply processes at dbs2.net will apply transactions to the hr.jobs table with SCNs that were committed after SCN obtained in this step.

Note:

This example assumes that the contents of the hr.jobs table at dbs1.net, dbs2.net (as hr.assignments), and dbs4.net are consistent when you complete this step. You might want to lock the table at each database while you complete this step to ensure consistency.
*/

DECLARE
  iscn  NUMBER;         -- Variable to hold instantiation SCN value
BEGIN
  iscn := DBMS_FLASHBACK.GET_SYSTEM_CHANGE_NUMBER();
  DBMS_APPLY_ADM.SET_TABLE_INSTANTIATION_SCN@DBS2.NET(
    source_object_name    => 'hr.jobs',
    source_database_name  => 'dbs1.net',
    instantiation_scn     => iscn);
  DBMS_APPLY_ADM.SET_TABLE_INSTANTIATION_SCN@DBS2.NET(
    source_object_name    => 'hr.jobs',
    source_database_name  => 'dbs1.net',
    instantiation_scn     => iscn,
    apply_database_link   => 'dbs4.net');
END;
/

/*

Step 5   Instantiate the dbs1.net Tables at dbs3.net

This example performs a network Data Pump import of the following tables:

  • hr.countries

  • hr.locations

  • hr.regions

A network import means that Data Pump imports these tables from dbs1.net without using an export dump file.

See Also:

Oracle Database Utilities for information about performing an import

Connect to dbs3.net as the strmadmin user.

*/
 
CONNECT strmadmin/user-password@dbs3.net

/*

This example will do a table import using the DBMS_DATAPUMP package. For simplicity, exceptions from any of the API calls will not be trapped. However, Oracle recommends that you define exception handlers and call GET_STATUS to retrieve more detailed error information if a failure occurs. If you want to monitor the import, then query the DBA_DATAPUMP_JOBS data dictionary view at the import database.

*/

SET SERVEROUTPUT ON
DECLARE
  h1        NUMBER;         -- Data Pump job handle
  sscn      NUMBER;         -- Variable to hold current source SCN
  job_state VARCHAR2(30);   -- To keep track of job state
  js        ku$_JobStatus;  -- The job status from GET_STATUS
  sts       ku$_Status;     -- The status object returned by GET_STATUS
  job_not_exist    exception;
  pragma exception_init(job_not_exist, -31626);
BEGIN
-- Create a (user-named) Data Pump job to do a table-level import.
  h1 := DBMS_DATAPUMP.OPEN(
          operation   => 'IMPORT',
          job_mode    => 'TABLE',
          remote_link => 'DBS1.NET',
          job_name    => 'dp_sing1');
-- A metadata filter is used to specify the schema that owns the tables 
-- that will be imported.
  DBMS_DATAPUMP.METADATA_FILTER(
    handle    => h1,
    name      => 'SCHEMA_EXPR',
    value     => '=''HR''');
-- A metadata filter is used to specify the tables that will be imported.
  DBMS_DATAPUMP.METADATA_FILTER(
    handle    => h1,
    name      => 'NAME_EXPR',
    value     => 'IN(''COUNTRIES'', ''REGIONS'', ''LOCATIONS'')');
-- Get the current SCN of the source database, and set the FLASHBACK_SCN 
-- parameter to this value to ensure consistency between all of the 
-- objects included in the import.
  sscn := DBMS_FLASHBACK.GET_SYSTEM_CHANGE_NUMBER@dbs1.net(); 
  DBMS_DATAPUMP.SET_PARAMETER(
    handle => h1,
    name   => 'FLASHBACK_SCN',
    value  => sscn); 
-- Start the job. 
  DBMS_DATAPUMP.START_JOB(h1);
-- The import job should be running. In the following loop, the job
-- is monitored until it completes.
  job_state := 'UNDEFINED';
  BEGIN
    WHILE (job_state != 'COMPLETED') AND (job_state != 'STOPPED') LOOP
      sts:=DBMS_DATAPUMP.GET_STATUS(
             handle  => h1,
             mask    => DBMS_DATAPUMP.KU$_STATUS_JOB_ERROR +
                        DBMS_DATAPUMP.KU$_STATUS_JOB_STATUS +
                        DBMS_DATAPUMP.KU$_STATUS_WIP,
             timeout => -1);
      js := sts.job_status;
      DBMS_LOCK.SLEEP(10);
      job_state := js.state;
    END LOOP;
  -- Gets an exception when job no longer exists
    EXCEPTION WHEN job_not_exist THEN
      DBMS_OUTPUT.PUT_LINE('Data Pump job has completed');
      DBMS_OUTPUT.PUT_LINE('Instantiation SCN: ' ||sscn);
  END;
END;
/

/*

Step 6   Configure the Apply Process at dbs3.net

Connect to dbs3.net as the strmadmin user.

*/
 
CONNECT strmadmin/user-password@dbs3.net

/*

Configure dbs3.net to apply changes to the countries table, locations table, and regions table.

*/

BEGIN
  DBMS_STREAMS_ADM.ADD_TABLE_RULES(
    table_name      => 'hr.countries',
    streams_type    => 'apply', 
    streams_name    => 'apply',
    queue_name      => 'strmadmin.streams_queue',
    include_dml     => TRUE,
    include_ddl     => TRUE,
    source_database => 'dbs1.net',
    inclusion_rule  => TRUE);
END;
/

BEGIN
  DBMS_STREAMS_ADM.ADD_TABLE_RULES(
    table_name      => 'hr.locations',
    streams_type    => 'apply', 
    streams_name    => 'apply',
    queue_name      => 'strmadmin.streams_queue',
    include_dml     => TRUE,
    include_ddl     => TRUE,
    source_database => 'dbs1.net',
    inclusion_rule  => TRUE);
END;
/

BEGIN
  DBMS_STREAMS_ADM.ADD_TABLE_RULES(
    table_name      => 'hr.regions',
    streams_type    => 'apply', 
    streams_name    => 'apply',
    queue_name      => 'strmadmin.streams_queue',
    include_dml     => TRUE,
    include_ddl     => TRUE,
    source_database => 'dbs1.net',
    inclusion_rule  => TRUE);
END;
/

/*

Step 7   Specify hr as the Apply User for the Apply Process at dbs3.net

In this example, the hr user owns all of the database objects for which changes are applied by the apply process at this database. Therefore, hr already has the necessary privileges to change these database objects, and it is convenient to make hr the apply user.

When the apply process was created in the previous step, the Oracle Streams administrator strmadmin was specified as the apply user by default, because strmadmin ran the procedure that created the apply process. Instead of specifying hr as the apply user, you could retain strmadmin as the apply user, but then you must grant strmadmin privileges on all of the database objects for which changes are applied and privileges to execute all user procedures used by the apply process. In an environment where an apply process applies changes to database objects in multiple schemas, it might be more convenient to use the Oracle Streams administrator as the apply user.

See Also:

Oracle Streams Concepts and Administration for more information about configuring an Oracle Streams administrator
*/

BEGIN
  DBMS_APPLY_ADM.ALTER_APPLY(
    apply_name => 'apply',
    apply_user => 'hr');
END;
/

/*

Step 8   Grant the hr User Execute Privilege on the Apply Process Rule Set

Because the hr user was specified as the apply user in the previous step, the hr user requires EXECUTE privilege on the positive rule set used by the apply process

*/

DECLARE
   rs_name  VARCHAR2(64);   -- Variable to hold rule set name
BEGIN
  SELECT RULE_SET_OWNER||'.'||RULE_SET_NAME 
    INTO rs_name 
    FROM DBA_APPLY 
    WHERE APPLY_NAME='APPLY';
  DBMS_RULE_ADM.GRANT_OBJECT_PRIVILEGE(
    privilege   => SYS.DBMS_RULE_ADM.EXECUTE_ON_RULE_SET,
    object_name => rs_name,
    grantee     => 'hr');
END;
/

/*

Step 9   Start the Apply Process at dbs3.net

Set the disable_on_error parameter to n so that the apply process will not be disabled if it encounters an error, and start the apply process at dbs3.net.

*/

BEGIN
  DBMS_APPLY_ADM.SET_PARAMETER(
    apply_name  => 'apply', 
    parameter   => 'disable_on_error', 
    value       => 'n');
END;
/
 
BEGIN
  DBMS_APPLY_ADM.START_APPLY(
    apply_name  => 'apply');
END;
/

/*

Step 10   Configure Propagation at dbs2.net

Connect to dbs2.net as the strmadmin user.

*/
 
CONNECT strmadmin/user-password@dbs2.net

/*

Configure and schedule propagation from the queue at dbs2.net to the queue at dbs3.net. You must specify this propagation for each table that will apply changes at dbs3.net. This configuration is an example of directed networks because the changes at dbs2.net originated at dbs1.net.

*/
 
BEGIN
  DBMS_STREAMS_ADM.ADD_TABLE_PROPAGATION_RULES(
    table_name               => 'hr.countries',
    streams_name             => 'dbs2_to_dbs3',
    source_queue_name        => 'strmadmin.streams_queue',
    destination_queue_name   => 'strmadmin.streams_queue@dbs3.net', 
    include_dml              => TRUE,
    include_ddl              => TRUE,
    source_database          => 'dbs1.net',
    inclusion_rule           => TRUE,
    queue_to_queue           => TRUE);
END;
/

BEGIN
  DBMS_STREAMS_ADM.ADD_TABLE_PROPAGATION_RULES(
    table_name              => 'hr.locations',
    streams_name            => 'dbs2_to_dbs3',
    source_queue_name       => 'strmadmin.streams_queue',
    destination_queue_name  => 'strmadmin.streams_queue@dbs3.net', 
    include_dml             => TRUE,
    include_ddl             => TRUE,
    source_database         => 'dbs1.net',
    inclusion_rule          => TRUE);
END;
/

BEGIN
  DBMS_STREAMS_ADM.ADD_TABLE_PROPAGATION_RULES(
    table_name              => 'hr.regions',
    streams_name            => 'dbs2_to_dbs3',
    source_queue_name       => 'strmadmin.streams_queue',
    destination_queue_name  => 'strmadmin.streams_queue@dbs3.net', 
    include_dml             => TRUE,
    include_ddl             => TRUE,
    source_database         => 'dbs1.net',
    inclusion_rule          => TRUE);
END;
/

/*

Step 11   Create the Rule-Based Transformation for Row LCRs at dbs2.net

Connect to dbs2.net as the hr user.

*/
 
CONNECT hr/user-password@dbs2.net

/*

Create the rule-based transformation function that transforms row changes resulting from DML statements to the jobs table from dbs1.net into row changes to the assignments table on dbs2.net.

The following function transforms every row LCR for the jobs table into a row LCR for the assignments table.

Note:

If DDL changes were also applied to the assignments table, then another transformation would be required for the DDL LCRs. This transformation would need to change the object name and the DDL text.
*/

CREATE OR REPLACE FUNCTION hr.to_assignments_trans_dml(
  p_in_data in ANYDATA) 
  RETURN ANYDATA IS out_data SYS.LCR$_ROW_RECORD;
  tc   pls_integer;
BEGIN
  -- Typecast AnyData to LCR$_ROW_RECORD
     tc := p_in_data.GetObject(out_data);
     IF out_data.GET_OBJECT_NAME() = 'JOBS'
     THEN
  -- Transform the in_data into the out_data
     out_data.SET_OBJECT_NAME('ASSIGNMENTS');
     END IF;
  -- Convert to AnyData
     RETURN ANYDATA.ConvertObject(out_data);
END;
/

/*

Step 12   Configure the Apply Process for Local Apply at dbs2.net

Connect to dbs2.net as the strmadmin user.

*/
 
CONNECT strmadmin/user-password@dbs2.net

/*

Configure dbs2.net to apply changes to the assignments table. Remember that the assignments table receives changes from the jobs table at dbs1.net.

*/

DECLARE
  to_assignments_rulename_dml   VARCHAR2(30);
  dummy_rule                    VARCHAR2(30);
BEGIN
--  DML changes to the jobs table from dbs1.net are applied to the assignments 
--  table. The to_assignments_rulename_dml variable is an out parameter 
--  in this call.
  DBMS_STREAMS_ADM.ADD_TABLE_RULES(
    table_name      => 'hr.jobs', -- jobs, not assignments, specified
    streams_type    => 'apply', 
    streams_name    => 'apply_dbs2',
    queue_name      => 'strmadmin.streams_queue',
    include_dml     => TRUE,
    include_ddl     => FALSE,
    source_database => 'dbs1.net',
    dml_rule_name   => to_assignments_rulename_dml,
    ddl_rule_name   => dummy_rule,
    inclusion_rule  => TRUE); 
--  Modify the rule for the hr.jobs table to use the transformation function.
  DBMS_STREAMS_ADM.SET_RULE_TRANSFORM_FUNCTION(
    rule_name          => to_assignments_rulename_dml,
    transform_function => 'hr.to_assignments_trans_dml');
END;
/

/*

Step 13   Specify hr as the Apply User for the Apply Process at dbs2.net

In this example, the hr user owns all of the database objects for which changes are applied by the apply process at this database. Therefore, hr already has the necessary privileges to change these database objects, and it is convenient to make hr the apply user.

When the apply process was created in the previous step, the Oracle Streams administrator strmadmin was specified as the apply user by default, because strmadmin ran the procedure that created the apply process. Instead of specifying hr as the apply user, you could retain strmadmin as the apply user, but then you must grant strmadmin privileges on all of the database objects for which changes are applied and privileges to execute all user procedures used by the apply process. In an environment where an apply process applies changes to database objects in multiple schemas, it might be more convenient to use the Oracle Streams administrator as the apply user.

See Also:

Oracle Streams Concepts and Administration for more information about configuring an Oracle Streams administrator
*/

BEGIN
  DBMS_APPLY_ADM.ALTER_APPLY(
    apply_name => 'apply_dbs2',
    apply_user => 'hr');
END;
/

/*

Step 14   Grant the hr User Execute Privilege on the Apply Process Rule Set

Because the hr user was specified as the apply user in the previous step, the hr user requires EXECUTE privilege on the positive rule set used by the apply process

*/

DECLARE
   rs_name  VARCHAR2(64);   -- Variable to hold rule set name
BEGIN
  SELECT RULE_SET_OWNER||'.'||RULE_SET_NAME 
    INTO rs_name 
    FROM DBA_APPLY 
    WHERE APPLY_NAME='APPLY_DBS2';
  DBMS_RULE_ADM.GRANT_OBJECT_PRIVILEGE(
    privilege   => SYS.DBMS_RULE_ADM.EXECUTE_ON_RULE_SET,
    object_name => rs_name,
    grantee     => 'hr');
END;
/

/*

Step 15   Start the Apply Process at dbs2.net for Local Apply

Set the disable_on_error parameter to n so that the apply process will not be disabled if it encounters an error, and start the apply process for local apply at dbs2.net.

*/

BEGIN
  DBMS_APPLY_ADM.SET_PARAMETER(
    apply_name  => 'apply_dbs2', 
    parameter   => 'disable_on_error', 
    value       => 'n');
END;
/
 
BEGIN
  DBMS_APPLY_ADM.START_APPLY(
    apply_name  => 'apply_dbs2');
END;
/

/*

Step 16   Configure the Apply Process at dbs2.net for Apply at dbs4.net

Configure the apply process for dbs4.net, which is a Sybase database. The dbs2.net database is acting as a gateway to dbs4.net. Therefore, the apply process for dbs4.net must be configured at dbs2.net. The apply process cannot apply DDL changes to non-Oracle databases. Therefore, the include_ddl parameter is set to FALSE when the ADD_TABLE_RULES procedure is run.

*/

BEGIN
  DBMS_APPLY_ADM.CREATE_APPLY(
    queue_name          => 'strmadmin.streams_queue',
    apply_name          => 'apply_dbs4',
    apply_database_link => 'dbs4.net',
    apply_captured      => TRUE);
END;
/

BEGIN
  DBMS_STREAMS_ADM.ADD_TABLE_RULES(
    table_name      => 'hr.jobs',
    streams_type    => 'apply', 
    streams_name    => 'apply_dbs4',
    queue_name      => 'strmadmin.streams_queue',
    include_dml     => TRUE,
    include_ddl     => FALSE,
    source_database => 'dbs1.net',
    inclusion_rule  => TRUE);
END;
/

/*

Step 17   Start the Apply Process at dbs2.net for Apply at dbs4.net

Set the disable_on_error parameter to n so that the apply process will not be disabled if it encounters an error, and start the remote apply for Sybase using database link dbs4.net.

*/

BEGIN
  DBMS_APPLY_ADM.SET_PARAMETER(
    apply_name  => 'apply_dbs4', 
    parameter   => 'disable_on_error', 
    value       => 'n');
END;
/
 
BEGIN
  DBMS_APPLY_ADM.START_APPLY(
    apply_name  => 'apply_dbs4');
END;
/

/*

Step 18   Start the Capture Process at dbs1.net

Connect to dbs1.net as the strmadmin user.

*/
 
CONNECT strmadmin/user-password@dbs1.net

/*

Start the capture process at dbs1.net.

*/

BEGIN
  DBMS_CAPTURE_ADM.START_CAPTURE(
    capture_name  => 'capture');
END;
/

/*

Step 19   Check the Spool Results

Check the streams_share_schema1.out spool file to ensure that all actions finished successfully after this script is completed.

*/

SET ECHO OFF
SPOOL OFF

/*

You can now make DML and DDL changes to specific tables at dbs1.net and see these changes replicated to the other databases in the environment based on the rules you configured for the Oracle Streams processes and propagations in this environment.

See Also:

"Make DML and DDL Changes to Tables in the hr Schema" for examples of changes that are replicated in this environment
/*************************** END OF SCRIPT ******************************/

Flexible Configuration for Sharing Data from a Single Database

Complete the following steps to use a more flexible approach for specifying the capture, propagation, and apply definitions. This approach does not use the DBMS_STREAMS_ADM package. Instead, it uses the following packages:

  • The DBMS_CAPTURE_ADM package to configure capture processes

  • The DBMS_PROPAGATION_ADM package to configure propagations

  • The DBMS_APPLY_ADM package to configure apply processes

  • The DBMS_RULES_ADM package to specify capture process, propagation, and apply process rules and rule sets

    Note:

    Neither the ALL_STREAMS_TABLE_RULES nor the DBA_STREAMS_TABLE_RULES data dictionary view is populated by the rules created in this example. To view the rules created in this example, you can query the ALL_STREAMS_RULES or DBA_STREAMS_RULES data dictionary view.

This example includes the following steps:

  1. Show Output and Spool Results

  2. Configure Propagation at dbs1.net

  3. Configure the Capture Process at dbs1.net

  4. Prepare the hr Schema at dbs1.net for Instantiation

  5. Set the Instantiation SCN for the Existing Tables at Other Databases

  6. Instantiate the dbs1.net Tables at dbs3.net

  7. Configure the Apply Process at dbs3.net

  8. Grant the hr User Execute Privilege on the Apply Process Rule Set

  9. Start the Apply Process at dbs3.net

  10. Configure Propagation at dbs2.net

  11. Create the Rule-Based Transformation for Row LCRs at dbs2.net

  12. Configure the Apply Process for Local Apply at dbs2.net

  13. Grant the hr User Execute Privilege on the Apply Process Rule Set

  14. Start the Apply Process at dbs2.net for Local Apply

  15. Configure the Apply Process at dbs2.net for Apply at dbs4.net

  16. Start the Apply Process at dbs2.net for Apply at dbs4.net

  17. Start the Capture Process at dbs1.net

  18. Check the Spool Results

Note:

If you are viewing this document online, then you can copy the text from the "BEGINNING OF SCRIPT" line after this note to the next "END OF SCRIPT" line into a text editor and then edit the text to create a script for your environment. Run the script with SQL*Plus on a computer that can connect to all of the databases in the environment.
/************************* BEGINNING OF SCRIPT ******************************

Step 1   Show Output and Spool Results

Run SET ECHO ON and specify the spool file for the script. Check the spool file for errors after you run this script.

*/

SET ECHO ON
SPOOL streams_share_schema2.out

/*

Step 2   Configure Propagation at dbs1.net

Connect to dbs1.net as the strmadmin user.

*/
 
CONNECT strmadmin/user-password@dbs1.net

/*

Configure and schedule propagation from the queue at dbs1.net to the queue at dbs2.net. This configuration specifies that the propagation propagates all changes to the hr schema. You have the option of omitting the rule set specification, but then everything in the queue will be propagated, which might not be desired if, in the future, multiple capture processes will use the streams_queue.

*/

BEGIN
  -- Create the rule set
  DBMS_RULE_ADM.CREATE_RULE_SET(
    rule_set_name       => 'strmadmin.propagation_dbs1_rules',
    evaluation_context  => 'SYS.STREAMS$_EVALUATION_CONTEXT');
  -- Create rules for all modifications to the hr schema
  DBMS_RULE_ADM.CREATE_RULE(
    rule_name  => 'strmadmin.all_hr_dml',
    condition  => ' :dml.get_object_owner() = ''HR'' AND ' || 
                  ' :dml.is_null_tag() = ''Y'' AND ' ||
                  ' :dml.get_source_database_name() = ''DBS1.NET'' ');
  DBMS_RULE_ADM.CREATE_RULE(
    rule_name  => 'strmadmin.all_hr_ddl',
    condition  => ' (:ddl.get_object_owner() = ''HR'' OR ' ||
                  ' :ddl.get_base_table_owner() =   ''HR'') AND ' || 
                  ' :ddl.is_null_tag() = ''Y'' AND ' ||
                  ' :ddl.get_source_database_name() = ''DBS1.NET'' ');
  -- Add rules to rule set
  DBMS_RULE_ADM.ADD_RULE(
    rule_name      => 'strmadmin.all_hr_dml', 
    rule_set_name  => 'strmadmin.propagation_dbs1_rules');
  DBMS_RULE_ADM.ADD_RULE(
    rule_name      => 'strmadmin.all_hr_ddl', 
    rule_set_name  => 'strmadmin.propagation_dbs1_rules');
  -- Create a propagation that uses the rule set as its positive rule set
  DBMS_PROPAGATION_ADM.CREATE_PROPAGATION(
    propagation_name    => 'dbs1_to_dbs2',
    source_queue        => 'strmadmin.streams_queue',
    destination_queue   => 'strmadmin.streams_queue',
    destination_dblink  => 'dbs2.net',
    rule_set_name       => 'strmadmin.propagation_dbs1_rules');
END;
/

/*

Step 3   Configure the Capture Process at dbs1.net

Create a capture process and rules to capture the entire hr schema at dbs1.net.

*/

BEGIN
  -- Create the rule set
  DBMS_RULE_ADM.CREATE_RULE_SET(
    rule_set_name       => 'strmadmin.demo_rules',
    evaluation_context  => 'SYS.STREAMS$_EVALUATION_CONTEXT');
  --   Create rules that specify the entire hr schema
  DBMS_RULE_ADM.CREATE_RULE(
    rule_name  => 'strmadmin.schema_hr_dml',
    condition  => ' :dml.get_object_owner() = ''HR''  AND ' || 
                  ' :dml.is_null_tag() = ''Y'' AND ' ||
                  ' :dml.get_source_database_name() = ''DBS1.NET'' ');
  DBMS_RULE_ADM.CREATE_RULE(
    rule_name  => 'strmadmin.schema_hr_ddl',
    condition  => ' (:ddl.get_object_owner() = ''HR'' OR ' ||
                  ' :ddl.get_base_table_owner() =   ''HR'') AND ' || 
                  ' :ddl.is_null_tag() = ''Y'' AND ' ||
                  ' :ddl.get_source_database_name() = ''DBS1.NET'' ');
  --  Add the rules to the rule set
  DBMS_RULE_ADM.ADD_RULE(
    rule_name      => 'strmadmin.schema_hr_dml', 
    rule_set_name  => 'strmadmin.demo_rules');
  DBMS_RULE_ADM.ADD_RULE(
    rule_name      => 'strmadmin.schema_hr_ddl', 
    rule_set_name  => 'strmadmin.demo_rules');
  --  Create a capture process that uses the rule set as its positive rule set
  DBMS_CAPTURE_ADM.CREATE_CAPTURE(
    queue_name    => 'strmadmin.streams_queue',
    capture_name  => 'capture',
    rule_set_name => 'strmadmin.demo_rules');
END;
/

/*

Step 4   Prepare the hr Schema at dbs1.net for Instantiation

While still connected as the Oracle Streams administrator at dbs1.net, prepare the hr schema at dbs1.net for instantiation at dbs3.net. This step marks the lowest SCN of the tables in the schema for instantiation. SCNs subsequent to the lowest SCN can be used for instantiation.

This step also enables supplemental logging for any primary key, unique key, bitmap index, and foreign key columns in the tables in the hr schema. Supplemental logging places additional information in the redo log for changes made to tables. The apply process needs this extra information to perform certain operations, such as unique row identification and conflict resolution. Because dbs1.net is the only database where changes are captured in this environment, it is the only database where you must specify supplemental logging for the tables in the hr schema.

Note:

This step is not required in the "Simple Configuration for Sharing Data from a Single Database". In that example, when the ADD_SCHEMA_RULES procedure in the DBMS_STREAMS_ADM package is run in Step 3, the PREPARE_SCHEMA_INSTANTIATION procedure in the DBMS_CAPTURE_ADM package is run automatically for the hr schema.
*/

BEGIN
  DBMS_CAPTURE_ADM.PREPARE_SCHEMA_INSTANTIATION(
    schema_name          => 'hr',
    supplemental_logging => 'keys');
END;
/

/*

Step 5   Set the Instantiation SCN for the Existing Tables at Other Databases

In this example, the hr.jobs table already exists at dbs2.net and dbs4.net. At dbs2.net, this table is named assignments, but it has the same shape and data as the jobs table at dbs1.net. Also, in this example, dbs4.net is a Sybase database. All of the other tables in the Oracle Streams environment are instantiated at the other destination databases using Data Pump import.

Because the hr.jobs table already exists at dbs2.net and dbs4.net, this example uses the GET_SYSTEM_CHANGE_NUMBER function in the DBMS_FLASHBACK package at dbs1.net to obtain the current SCN for the database. This SCN is used at dbs2.net to run the SET_TABLE_INSTANTIATION_SCN procedure in the DBMS_APPLY_ADM package. Running this procedure twice sets the instantiation SCN for the hr.jobs table at dbs2.net and dbs4.net.

The SET_TABLE_INSTANTIATION_SCN procedure controls which LCRs for a table are ignored by an apply process and which LCRs for a table are applied by an apply process. If the commit SCN of an LCR for a table from a source database is less than or equal to the instantiation SCN for that table at a destination database, then the apply process at the destination database discards the LCR. Otherwise, the apply process applies the LCR.

In this example, both of the apply processes at dbs2.net will apply transactions to the hr.jobs table with SCNs that were committed after SCN obtained in this step.

Note:

This example assumes that the contents of the hr.jobs table at dbs1.net, dbs2.net (as hr.assignments), and dbs4.net are consistent when you complete this step. You might want to lock the table at each database while you complete this step to ensure consistency.
*/

DECLARE
  iscn  NUMBER;         -- Variable to hold instantiation SCN value
BEGIN
  iscn := DBMS_FLASHBACK.GET_SYSTEM_CHANGE_NUMBER();
  DBMS_APPLY_ADM.SET_TABLE_INSTANTIATION_SCN@DBS2.NET(
    source_object_name    => 'hr.jobs',
    source_database_name  => 'dbs1.net',
    instantiation_scn     => iscn);
  DBMS_APPLY_ADM.SET_TABLE_INSTANTIATION_SCN@DBS2.NET(
    source_object_name    => 'hr.jobs',
    source_database_name  => 'dbs1.net',
    instantiation_scn     => iscn,
    apply_database_link   =>     'dbs4.net');
END;
/

/*

Step 6   Instantiate the dbs1.net Tables at dbs3.net

This example performs a network Data Pump import of the following tables:

  • hr.countries

  • hr.locations

  • hr.regions

A network import means that Data Pump imports these tables from dbs1.net without using an export dump file.

See Also:

Oracle Database Utilities for information about performing an import

Connect to dbs3.net as the strmadmin user.

*/
 
CONNECT strmadmin/user-password@dbs3.net

/*

This example will do a table import using the DBMS_DATAPUMP package. For simplicity, exceptions from any of the API calls will not be trapped. However, Oracle recommends that you define exception handlers and call GET_STATUS to retrieve more detailed error information if a failure occurs. If you want to monitor the import, then query the DBA_DATAPUMP_JOBS data dictionary view at the import database.

*/

SET SERVEROUTPUT ON
DECLARE
  h1        NUMBER;         -- Data Pump job handle
  sscn      NUMBER;         -- Variable to hold current source SCN
  job_state VARCHAR2(30);   -- To keep track of job state
  js        ku$_JobStatus;  -- The job status from GET_STATUS
  sts       ku$_Status;     -- The status object returned by GET_STATUS
  job_not_exist    exception;
  pragma exception_init(job_not_exist, -31626);
BEGIN
-- Create a (user-named) Data Pump job to do a table-level import.
  h1 := DBMS_DATAPUMP.OPEN(
          operation   => 'IMPORT',
          job_mode    => 'TABLE',
          remote_link => 'DBS1.NET',
          job_name    => 'dp_sing2');
-- A metadata filter is used to specify the schema that owns the tables 
-- that will be imported.
  DBMS_DATAPUMP.METADATA_FILTER(
    handle    => h1,
    name      => 'SCHEMA_EXPR',
    value     => '=''HR''');
-- A metadata filter is used to specify the tables that will be imported.
  DBMS_DATAPUMP.METADATA_FILTER(
    handle    => h1,
    name      => 'NAME_EXPR',
    value     => 'IN(''COUNTRIES'', ''REGIONS'', ''LOCATIONS'')');
-- Get the current SCN of the source database, and set the FLASHBACK_SCN 
-- parameter to this value to ensure consistency between all of the 
-- objects included in the import.
  sscn := DBMS_FLASHBACK.GET_SYSTEM_CHANGE_NUMBER@dbs1.net();
  DBMS_DATAPUMP.SET_PARAMETER(
    handle => h1,
    name   => 'FLASHBACK_SCN',
    value  => sscn); 
-- Start the job. 
  DBMS_DATAPUMP.START_JOB(h1);
-- The import job should be running. In the following loop, the job
-- is monitored until it completes.
  job_state := 'UNDEFINED';
  BEGIN
    WHILE (job_state != 'COMPLETED') AND (job_state != 'STOPPED') LOOP
      sts:=DBMS_DATAPUMP.GET_STATUS(
             handle  => h1,
             mask    => DBMS_DATAPUMP.KU$_STATUS_JOB_ERROR +
                        DBMS_DATAPUMP.KU$_STATUS_JOB_STATUS +
                        DBMS_DATAPUMP.KU$_STATUS_WIP,
             timeout => -1);
      js := sts.job_status;
      DBMS_LOCK.SLEEP(10);
      job_state := js.state;
    END LOOP;
  -- Gets an exception when job no longer exists
    EXCEPTION WHEN job_not_exist THEN
      DBMS_OUTPUT.PUT_LINE('Data Pump job has completed');
      DBMS_OUTPUT.PUT_LINE('Instantiation SCN: ' ||sscn);
  END;
END;
/

/*

Step 7   Configure the Apply Process at dbs3.net

Connect to dbs3.net as the strmadmin user.

*/
 
CONNECT strmadmin/user-password@dbs3.net

/*

Configure dbs3.net to apply DML and DDL changes to the countries table, locations table, and regions table.

*/

BEGIN
  -- Create the rule set
  DBMS_RULE_ADM.CREATE_RULE_SET(
    rule_set_name       => 'strmadmin.apply_rules',
    evaluation_context  => 'SYS.STREAMS$_EVALUATION_CONTEXT');
  -- Rules for hr.countries
  DBMS_RULE_ADM.CREATE_RULE(
    rule_name    => 'strmadmin.all_countries_dml',
    condition    => ' :dml.get_object_owner() = ''HR'' AND ' || 
                    ' :dml.get_object_name() = ''COUNTRIES''  AND ' || 
                    ' :dml.is_null_tag() = ''Y'' AND ' ||
                    ' :dml.get_source_database_name() = ''DBS1.NET'' ');
  DBMS_RULE_ADM.CREATE_RULE(
    rule_name    => 'strmadmin.all_countries_ddl',
    condition    => ' (:ddl.get_object_owner() = ''HR'' OR ' ||
                    ' :ddl.get_base_table_owner() =         ''HR'') AND ' || 
                    ' :ddl.get_object_name() = ''COUNTRIES'' AND ' || 
                    ' :ddl.is_null_tag() = ''Y'' AND ' ||
                    ' :ddl.get_source_database_name() = ''DBS1.NET'' ');
  -- Rules for hr.locations
  DBMS_RULE_ADM.CREATE_RULE(
    rule_name    => 'strmadmin.all_locations_dml',
    condition    => ' :dml.get_object_owner() = ''HR'' AND ' ||
                    ' :dml.get_object_name() = ''LOCATIONS'' AND ' || 
                    ' :dml.is_null_tag() = ''Y'' AND ' ||
                    ' :dml.get_source_database_name() = ''DBS1.NET'' ');
  DBMS_RULE_ADM.CREATE_RULE(
    rule_name    => 'strmadmin.all_locations_ddl',
    condition    => ' (:ddl.get_object_owner() = ''HR'' OR ' ||
                    ' :ddl.get_base_table_owner() =         ''HR'') AND ' ||
                    ' :ddl.get_object_name() = ''LOCATIONS'' AND ' || 
                    ' :ddl.is_null_tag() = ''Y'' AND ' ||
                    ' :ddl.get_source_database_name() = ''DBS1.NET'' ');
  -- Rules for hr.regions
  DBMS_RULE_ADM.CREATE_RULE(
    rule_name    => 'strmadmin.all_regions_dml',
    condition    => ' :dml.get_object_owner() = ''HR'' AND ' ||
                    ' :dml.get_object_name() = ''REGIONS'' AND ' || 
                    ' :dml.is_null_tag() = ''Y'' AND ' ||
                    ' :dml.get_source_database_name() = ''DBS1.NET'' ');
  DBMS_RULE_ADM.CREATE_RULE(
    rule_name    => 'strmadmin.all_regions_ddl',
    condition    => ' (:ddl.get_object_owner() = ''HR'' OR ' ||
                    ' :ddl.get_base_table_owner() =         ''HR'') AND ' ||
                    ' :ddl.get_object_name() = ''REGIONS'' AND ' || 
                    ' :ddl.is_null_tag() = ''Y'' AND ' ||
                    ' :ddl.get_source_database_name() = ''DBS1.NET'' ');
  -- Add rules to rule set
  DBMS_RULE_ADM.ADD_RULE(
    rule_name      => 'strmadmin.all_countries_dml', 
    rule_set_name  => 'strmadmin.apply_rules');
  DBMS_RULE_ADM.ADD_RULE(
    rule_name      => 'strmadmin.all_countries_ddl', 
    rule_set_name  => 'strmadmin.apply_rules');
  DBMS_RULE_ADM.ADD_RULE(
    rule_name      => 'strmadmin.all_locations_dml', 
    rule_set_name  => 'strmadmin.apply_rules');
  DBMS_RULE_ADM.ADD_RULE(
    rule_name      => 'strmadmin.all_locations_ddl', 
    rule_set_name  => 'strmadmin.apply_rules');
  DBMS_RULE_ADM.ADD_RULE(
    rule_name      => 'strmadmin.all_regions_dml', 
    rule_set_name  => 'strmadmin.apply_rules');
  DBMS_RULE_ADM.ADD_RULE(
    rule_name      => 'strmadmin.all_regions_ddl', 
    rule_set_name  => 'strmadmin.apply_rules');
  -- Create an apply process that uses the rule set as its positive rule set
  DBMS_APPLY_ADM.CREATE_APPLY(
    queue_name      => 'strmadmin.streams_queue',
    apply_name      => 'apply',
    rule_set_name   => 'strmadmin.apply_rules',
    apply_user      => 'hr',
    apply_captured  => TRUE,
    source_database => 'dbs1.net');
END;
/

/*

Step 8   Grant the hr User Execute Privilege on the Apply Process Rule Set

Because the hr user was specified as the apply user in the previous step, the hr user requires EXECUTE privilege on the positive rule set used by the apply process

*/

BEGIN
  DBMS_RULE_ADM.GRANT_OBJECT_PRIVILEGE(
    privilege   => SYS.DBMS_RULE_ADM.EXECUTE_ON_RULE_SET,
    object_name => 'strmadmin.apply_rules',
    grantee     => 'hr');
END;
/

/*

Step 9   Start the Apply Process at dbs3.net

Set the disable_on_error parameter to n so that the apply process will not be disabled if it encounters an error, and start the apply process at dbs3.net.

*/

BEGIN
  DBMS_APPLY_ADM.SET_PARAMETER(
    apply_name  => 'apply', 
    parameter   => 'disable_on_error', 
    value       => 'n');
END;
/
 
BEGIN
  DBMS_APPLY_ADM.START_APPLY(
    apply_name  => 'apply');
END;
/

/*

Step 10   Configure Propagation at dbs2.net

Connect to dbs2.net as the strmadmin user.

*/
 
CONNECT strmadmin/user-password@dbs2.net

/*

Configure and schedule propagation from the queue at dbs2.net to the queue at dbs3.net. This configuration is an example of directed networks because the changes at dbs2.net originated at dbs1.net.

*/

BEGIN
  -- Create the rule set
  DBMS_RULE_ADM.CREATE_RULE_SET(
    rule_set_name       => 'strmadmin.propagation_dbs3_rules',
    evaluation_context  => 'SYS.STREAMS$_EVALUATION_CONTEXT');
  -- Create rules for all modifications to the countries table
  DBMS_RULE_ADM.CREATE_RULE(
    rule_name  => 'strmadmin.all_countries_dml',
    condition  => ' :dml.get_object_owner() = ''HR'' AND ' || 
                  ' :dml.get_object_name() = ''COUNTRIES'' AND ' || 
                  ' :dml.is_null_tag() = ''Y'' AND ' ||
                  ' :dml.get_source_database_name() = ''DBS1.NET'' ');
  DBMS_RULE_ADM.CREATE_RULE(
    rule_name  => 'strmadmin.all_countries_ddl',
    condition  => ' (:ddl.get_object_owner() = ''HR'' OR ' ||
                  ' :ddl.get_base_table_owner() =   ''HR'') AND ' || 
                  ' :ddl.get_object_name() = ''COUNTRIES'' AND ' || 
                  ' :ddl.is_null_tag() = ''Y'' AND ' ||
                  ' :ddl.get_source_database_name() = ''DBS1.NET'' ');
  -- Create rules for all modifications to the locations table
  DBMS_RULE_ADM.CREATE_RULE(
    rule_name   => 'strmadmin.all_locations_dml',
    condition  => ' :dml.get_object_owner() = ''HR'' AND ' ||
                  ' :dml.get_object_name() = ''LOCATIONS'' AND ' || 
                  ' :dml.is_null_tag() = ''Y'' AND ' ||
                  ' :dml.get_source_database_name() = ''DBS1.NET'' ');
  DBMS_RULE_ADM.CREATE_RULE(
    rule_name   => 'strmadmin.all_locations_ddl',
    condition  => ' (:ddl.get_object_owner() = ''HR'' OR ' ||
                  ' :ddl.get_base_table_owner() =   ''HR'') AND ' ||
                  ' :ddl.get_object_name() = ''LOCATIONS'' AND ' || 
                  ' :ddl.is_null_tag() = ''Y'' AND ' ||
                  ' :ddl.get_source_database_name() = ''DBS1.NET'' ');
  -- Create rules for all modifications to the regions table
  DBMS_RULE_ADM.CREATE_RULE(
    rule_name   => 'strmadmin.all_regions_dml',
    condition  => ' :dml.get_object_owner() = ''HR'' AND ' ||
                  ' :dml.get_object_name() = ''REGIONS'' AND ' || 
                  ' :dml.is_null_tag() = ''Y'' AND ' ||
                  ' :dml.get_source_database_name() = ''DBS1.NET'' ');
  DBMS_RULE_ADM.CREATE_RULE(
    rule_name   => 'strmadmin.all_regions_ddl',
    condition  => ' (:ddl.get_object_owner() = ''HR'' OR ' ||
                  ' :ddl.get_base_table_owner() =   ''HR'') AND ' ||
                  ' :ddl.get_object_name() = ''REGIONS'' AND ' || 
                  ' :ddl.is_null_tag() = ''Y'' AND ' ||
                  ' :ddl.get_source_database_name() = ''DBS1.NET'' ');
  -- Add rules to rule set
  DBMS_RULE_ADM.ADD_RULE(
    rule_name      => 'strmadmin.all_countries_dml', 
    rule_set_name  => 'strmadmin.propagation_dbs3_rules');
  DBMS_RULE_ADM.ADD_RULE(
    rule_name      => 'strmadmin.all_countries_ddl', 
    rule_set_name  => 'strmadmin.propagation_dbs3_rules');
  DBMS_RULE_ADM.ADD_RULE(
    rule_name      => 'strmadmin.all_locations_dml',  
    rule_set_name  => 'strmadmin.propagation_dbs3_rules');
  DBMS_RULE_ADM.ADD_RULE(
    rule_name      => 'strmadmin.all_locations_ddl',  
    rule_set_name  => 'strmadmin.propagation_dbs3_rules');
  DBMS_RULE_ADM.ADD_RULE(
    rule_name      => 'strmadmin.all_regions_dml',  
    rule_set_name  => 'strmadmin.propagation_dbs3_rules');
  DBMS_RULE_ADM.ADD_RULE(
    rule_name      => 'strmadmin.all_regions_ddl',  
    rule_set_name  => 'strmadmin.propagation_dbs3_rules');
  -- Create a propagation that uses the rule set as its positive rule set
  DBMS_PROPAGATION_ADM.CREATE_PROPAGATION(
    propagation_name    => 'dbs2_to_dbs3',
    source_queue        => 'strmadmin.streams_queue',
    destination_queue   => 'strmadmin.streams_queue',
    destination_dblink  => 'dbs3.net',
    rule_set_name       => 'strmadmin.propagation_dbs3_rules');
END;
/

/*

Step 11   Create the Rule-Based Transformation for Row LCRs at dbs2.net

Connect to dbs2.net as the hr user.

*/
 
CONNECT hr/user-password@dbs2.net

/*

Create the rule-based transformation function that transforms row changes resulting from DML statements to the jobs table from dbs1.net into row changes to the assignments table on dbs2.net.

The following function transforms every row LCR for the jobs table into a row LCR for the assignments table.

Note:

If DDL changes were also applied to the assignments table, then another transformation would be required for the DDL LCRs. This transformation would need to change the object name and the DDL text.
*/

CREATE OR REPLACE FUNCTION hr.to_assignments_trans_dml(
  p_in_data in ANYDATA) 
  RETURN ANYDATA IS out_data SYS.LCR$_ROW_RECORD;
  tc   pls_integer;
BEGIN
  -- Typecast AnyData to LCR$_ROW_RECORD
     tc := p_in_data.GetObject(out_data);
     IF out_data.GET_OBJECT_NAME() = 'JOBS'
     THEN
  -- Transform the in_data into the out_data
     out_data.SET_OBJECT_NAME('ASSIGNMENTS');
     END IF;
  -- Convert to AnyData
     RETURN ANYDATA.ConvertObject(out_data);
END;
/

/*

Step 12   Configure the Apply Process for Local Apply at dbs2.net

Connect to dbs2.net as the strmadmin user.

*/
 
CONNECT strmadmin/user-password@dbs2.net

/*

Configure dbs2.net to apply changes to the local assignments table. Remember that the assignments table receives changes from the jobs table at dbs1.net. This step specifies a rule-based transformation without using the SET_RULE_TRANSFORM_FUNCTION procedure in the DBMS_STREAMS_ADM package. Instead, a name-value pair is added manually to the action context of the rule. The name-value pair specifies STREAMS$_TRANSFORM_FUNCTION for the name and hr.to_assignments_trans_dml for the value.

*/

DECLARE
  action_ctx_dml       SYS.RE$NV_LIST;
  action_ctx_ddl       SYS.RE$NV_LIST;
  ac_name              VARCHAR2(30) := 'STREAMS$_TRANSFORM_FUNCTION';
BEGIN
  -- Specify the name-value pair in the action context
  action_ctx_dml := SYS.RE$NV_LIST(SYS.RE$NV_ARRAY());
  action_ctx_dml.ADD_PAIR(
    ac_name, 
    ANYDATA.CONVERTVARCHAR2('hr.to_assignments_trans_dml'));
  --  Create the rule set strmadmin.apply_rules
  DBMS_RULE_ADM.CREATE_RULE_SET(
    rule_set_name       => 'strmadmin.apply_rules',
    evaluation_context  => 'SYS.STREAMS$_EVALUATION_CONTEXT');
  --  Create a rule that transforms all DML changes to the jobs table into 
  --  DML changes for assignments table
  DBMS_RULE_ADM.CREATE_RULE(
    rule_name       => 'strmadmin.all_jobs_dml',
    condition       => ' :dml.get_object_owner() = ''HR'' AND ' ||
                       ' :dml.get_object_name() = ''JOBS'' AND ' || 
                       ' :dml.is_null_tag() = ''Y'' AND ' ||
                       ' :dml.get_source_database_name() = ''DBS1.NET'' ',
    action_context  => action_ctx_dml);
  --  Add the rule to the rule set
  DBMS_RULE_ADM.ADD_RULE(
    rule_name      => 'strmadmin.all_jobs_dml', 
    rule_set_name  => 'strmadmin.apply_rules');
  -- Create an apply process that uses the rule set as its positive rule set
  DBMS_APPLY_ADM.CREATE_APPLY(
    queue_name      => 'strmadmin.streams_queue',
    apply_name      => 'apply_dbs2',
    rule_set_name   => 'strmadmin.apply_rules',
    apply_user      => 'hr',
    apply_captured  => TRUE,
    source_database => 'dbs1.net');
END;
/

/*

Step 13   Grant the hr User Execute Privilege on the Apply Process Rule Set

Because the hr user was specified as the apply user in the previous step, the hr user requires EXECUTE privilege on the positive rule set used by the apply process

*/

BEGIN
  DBMS_RULE_ADM.GRANT_OBJECT_PRIVILEGE(
    privilege   => SYS.DBMS_RULE_ADM.EXECUTE_ON_RULE_SET,
    object_name => 'strmadmin.apply_rules',
    grantee     => 'hr');
END;
/

/*

Step 14   Start the Apply Process at dbs2.net for Local Apply

Set the disable_on_error parameter to n so that the apply process will not be disabled if it encounters an error, and start the apply process for local apply at dbs2.net.

*/

BEGIN
  DBMS_APPLY_ADM.SET_PARAMETER(
    apply_name  => 'apply_dbs2', 
    parameter   => 'disable_on_error', 
    value       => 'n');
END;
/
 
BEGIN
  DBMS_APPLY_ADM.START_APPLY(
    apply_name  => 'apply_dbs2');
END;
/

/*

Step 15   Configure the Apply Process at dbs2.net for Apply at dbs4.net

Configure dbs2.net to apply DML changes to the jobs table at dbs4.net, which is a Sybase database. Remember that these changes originated at dbs1.net.

*/

BEGIN
  -- Create the rule set
  DBMS_RULE_ADM.CREATE_RULE_SET(
    rule_set_name      => 'strmadmin.apply_dbs4_rules',
    evaluation_context => 'SYS.STREAMS$_EVALUATION_CONTEXT');
  -- Create rule strmadmin.all_jobs_remote for all modifications 
  -- to the jobs table
  DBMS_RULE_ADM.CREATE_RULE(
    rule_name    => 'strmadmin.all_jobs_remote',
    condition    => ' :dml.get_object_owner() = ''HR'' AND ' ||
                    ' :dml.get_object_name() = ''JOBS'' AND ' || 
                    ' :dml.is_null_tag() = ''Y'' AND ' ||
                    ' :dml.get_source_database_name() = ''DBS1.NET'' ');
  -- Add the rule to the rule set
  DBMS_RULE_ADM.ADD_RULE(
    rule_name      => 'strmadmin.all_jobs_remote', 
    rule_set_name  => 'strmadmin.apply_dbs4_rules');
  -- Create an apply process that uses the rule set as its positive rule set
  DBMS_APPLY_ADM.CREATE_APPLY(
    queue_name          => 'strmadmin.streams_queue',
    apply_name          => 'apply_dbs4',
    rule_set_name       => 'strmadmin.apply_dbs4_rules',
    apply_database_link => 'dbs4.net',
    apply_captured      => TRUE,
    source_database     => 'dbs1.net');
END;
/

/*

Step 16   Start the Apply Process at dbs2.net for Apply at dbs4.net

Set the disable_on_error parameter to n so that the apply process will not be disabled if it encounters an error, and start the remote apply for Sybase using database link dbs4.net.

*/

BEGIN
  DBMS_APPLY_ADM.SET_PARAMETER(
    apply_name  => 'apply_dbs4', 
    parameter   => 'disable_on_error', 
    value       => 'n');
END;
/

BEGIN
  DBMS_APPLY_ADM.START_APPLY(
    apply_name  => 'apply_dbs4');
END;
/

/*

Step 17   Start the Capture Process at dbs1.net

Connect to dbs1.net as the strmadmin user.

*/
 
CONNECT strmadmin/user-password@dbs1.net

/*

Start the capture process at dbs1.net.

*/

BEGIN
  DBMS_CAPTURE_ADM.START_CAPTURE(
    capture_name  => 'capture');
END;
/

/*

Step 18   Check the Spool Results

Check the streams_share_schema2.out spool file to ensure that all actions finished successfully after this script is completed.

*/

SET ECHO OFF
SPOOL OFF

/*

You can now make DML and DDL changes to specific tables at dbs1.net and see these changes replicated to the other databases in the environment based on the rules you configured for the Oracle Streams processes and propagations in this environment.

See Also:

"Make DML and DDL Changes to Tables in the hr Schema" for examples of changes that are replicated in this environment
/*************************** END OF SCRIPT ******************************/

Make DML and DDL Changes to Tables in the hr Schema

After completing either of the examples described in "Example Scripts for Sharing Data from One Database", you can make DML and DDL changes to the tables in the hr schema at the dbs1.net database. These changes will be replicated to the other databases in the environment based on the rules you configured for Oracle Streams processes and propagations. You can check the other databases to see that the changes have been replicated.

For example, complete the following steps to make DML changes to the hr.jobs and hr.locations tables at dbs1.net. You can also make a DDL change to the hr.locations table at dbs1.net.

After you make these changes, you can query the hr.assignments table at dbs2.net to see that the DML change you made to this table at dbs1.net has been replicated. Remember that a rule-based transformation configured for the apply process at dbs2.net transforms DML changes to the hr.jobs table into DML changes to the hr.assignments table. You can also query the hr.locations table at dbs3.net to see that the DML and DDL changes you made to this table at dbs1.net have been replicated.


Step 1   Make DML and DDL Changes to Tables in the hr Schema

Make the following changes:

CONNECT hr/user-password@dbs1.net

UPDATE hr.jobs SET max_salary=10000 WHERE job_id='MK_REP';
COMMIT;

INSERT INTO hr.locations VALUES(
  3300, '521 Ralston Avenue', '94002', 'Belmont', 'CA', 'US');
COMMIT;

ALTER TABLE hr.locations RENAME COLUMN state_province TO state_or_province;

Step 2   Query the hr.assignments Table at dbs2.net

After some time passes to allow for capture, propagation, and apply of the changes performed the previous step, run the following query to confirm that the UPDATE change made to the hr.jobs table at dbs1.net has been applied to the hr.assignments table at dbs2.net.

CONNECT hr/user-password@dbs2.net

SELECT max_salary FROM hr.assignments WHERE job_id='MK_REP';

You should see 10000 for the value of the max_salary.

Step 3   Query and Describe the hr.locations Table at dbs3.net

Run the following query to confirm that the INSERT change made to the hr.locations table at dbs1.net has been applied at dbs3.net.

CONNECT hr/user-password@dbs3.net

SELECT * FROM hr.locations WHERE location_id=3300;

You should see the row inserted into the hr.locations table at dbs1.net in the previous step.

Next, describe the hr.locations table at to confirm that the ALTER TABLE change was propagated and applied correctly.

DESC hr.locations

The fifth column in the table should be state_or_province.

Add Objects to an Existing Oracle Streams Replication Environment

This example extends the Oracle Streams environment configured in the previous sections by adding replicated objects to an existing database. To complete this example, you must have completed the tasks in one of the previous examples in this chapter.

This example will add the following tables to the hr schema in the dbs3.net database:

When you complete this example, Oracle Streams processes changes to these tables with the following series of actions:

  1. The capture process captures changes at dbs1.net and enqueues them at dbs1.net.

  2. A propagation propagates changes from the queue at dbs1.net to the queue at dbs2.net.

  3. A propagation propagates changes from the queue at dbs2.net to the queue at dbs3.net.

  4. The apply process at dbs3.net applies the changes at dbs3.net.

When you complete this example, the hr schema at the dbs3.net database will have all of its original tables, because the countries, locations, and regions tables were instantiated at dbs3.net in the previous section.

Figure 20-2 provides an overview of the environment with the added tables.

Figure 20-2 Adding Objects to dbs3.net in the Environment

Description of Figure 20-2 follows
Description of "Figure 20-2 Adding Objects to dbs3.net in the Environment"

Complete the following steps to replicate these tables to the dbs3.net database.

  1. Show Output and Spool Results

  2. Stop the Apply Process at dbs3.net

  3. Configure the Apply Process for the Added Tables at dbs3.net

  4. Specify the Table Propagation Rules for the Added Tables at dbs2.net

  5. Prepare the Four Added Tables for Instantiation at dbs1.net

  6. Instantiate the dbs1.net Tables at dbs3.net

  7. Start the Apply Process at dbs3.net

  8. Check the Spool Results

Note:

If you are viewing this document online, then you can copy the text from the "BEGINNING OF SCRIPT" line after this note to the next "END OF SCRIPT" line into a text editor and then edit the text to create a script for your environment. Run the script with SQL*Plus on a computer that can connect to all of the databases in the environment.
/************************* BEGINNING OF SCRIPT ******************************

Step 1   Show Output and Spool Results

Run SET ECHO ON and specify the spool file for the script. Check the spool file for errors after you run this script.

*/

SET ECHO ON
SPOOL streams_addobjs.out

/*

Step 2    Stop the Apply Process at dbs3.net

Until you finish adding objects to dbs3.net, you must ensure that the apply process that will apply changes for the added objects does not try to apply changes for these objects. You can do this by stopping the capture process at the source database. Or, you can do this by stopping propagation of changes from dbs2.net to dbs3.net. Yet another alternative is to stop the apply process at dbs3.net. This example stops the apply process at dbs3.net.

Connect to dbs3.net as the strmadmin user.

*/
 
CONNECT strmadmin/user-password@dbs3.net

/*

Stop the apply process at dbs3.net.

*/

BEGIN
  DBMS_APPLY_ADM.STOP_APPLY(
    apply_name  => 'apply');
END;
/

/*

Step 3   Configure the Apply Process for the Added Tables at dbs3.net

Configure the apply process at dbs3.net to apply changes to the tables you are adding.

*/

BEGIN
  DBMS_STREAMS_ADM.ADD_TABLE_RULES(
    table_name      => 'hr.departments',
    streams_type    => 'apply', 
    streams_name    => 'apply',
    queue_name      => 'strmadmin.streams_queue',
    include_dml     => TRUE,
    include_ddl     => TRUE,
    source_database => 'dbs1.net',
    inclusion_rule  => TRUE);
END;
/

BEGIN
  DBMS_STREAMS_ADM.ADD_TABLE_RULES(
    table_name      => 'hr.employees',
    streams_type    => 'apply', 
    streams_name    => 'apply',
    queue_name      => 'strmadmin.streams_queue',
    include_dml     => TRUE,
    include_ddl     => TRUE,
    source_database => 'dbs1.net',
    inclusion_rule  => TRUE);
END;
/

BEGIN
  DBMS_STREAMS_ADM.ADD_TABLE_RULES(
    table_name      => 'hr.job_history',
    streams_type    => 'apply', 
    streams_name    => 'apply',
    queue_name      => 'strmadmin.streams_queue',
    include_dml     => TRUE,
    include_ddl     => TRUE,
    source_database => 'dbs1.net',
    inclusion_rule  => TRUE);
END;
/

BEGIN
  DBMS_STREAMS_ADM.ADD_TABLE_RULES(
    table_name      => 'hr.jobs',
    streams_type    => 'apply', 
    streams_name    => 'apply',
    queue_name      => 'strmadmin.streams_queue',
    include_dml     => TRUE,
    include_ddl     => TRUE,
    source_database => 'dbs1.net',
    inclusion_rule  => TRUE);
END;
/

/*

Step 4   Specify the Table Propagation Rules for the Added Tables at dbs2.net

Connect to dbs2.net as the strmadmin user.

*/
 
CONNECT strmadmin/user-password@dbs2.net

/*

Add the tables to the rules for propagation from the queue at dbs2.net to the queue at dbs3.net.

*/

BEGIN
  DBMS_STREAMS_ADM.ADD_TABLE_PROPAGATION_RULES(
    table_name               => 'hr.departments',
    streams_name             => 'dbs2_to_dbs3',
    source_queue_name        => 'strmadmin.streams_queue',
    destination_queue_name   => 'strmadmin.streams_queue@dbs3.net', 
    include_dml              => TRUE,
    include_ddl              => TRUE,
    source_database          => 'dbs1.net',
    inclusion_rule           => TRUE);
END;
/

BEGIN
  DBMS_STREAMS_ADM.ADD_TABLE_PROPAGATION_RULES(
    table_name              => 'hr.employees',
    streams_name            => 'dbs2_to_dbs3',
    source_queue_name       => 'strmadmin.streams_queue',
    destination_queue_name  => 'strmadmin.streams_queue@dbs3.net', 
    include_dml             => TRUE,
    include_ddl             => TRUE,
    source_database         => 'dbs1.net',
    inclusion_rule          => TRUE);
END;
/

BEGIN
  DBMS_STREAMS_ADM.ADD_TABLE_PROPAGATION_RULES(
    table_name              => 'hr.job_history',
    streams_name            => 'dbs2_to_dbs3',
    source_queue_name       => 'strmadmin.streams_queue',
    destination_queue_name  => 'strmadmin.streams_queue@dbs3.net', 
    include_dml             => TRUE,
    include_ddl             => TRUE,
    source_database         => 'dbs1.net',
    inclusion_rule          => TRUE);
END;
/

BEGIN
  DBMS_STREAMS_ADM.ADD_TABLE_PROPAGATION_RULES(
    table_name              => 'hr.jobs',
    streams_name            => 'dbs2_to_dbs3',
    source_queue_name       => 'strmadmin.streams_queue',
    destination_queue_name  => 'strmadmin.streams_queue@dbs3.net', 
    include_dml             => TRUE,
    include_ddl             => TRUE,
    source_database         => 'dbs1.net',
    inclusion_rule          => TRUE);
END;
/

/*

Step 5   Prepare the Four Added Tables for Instantiation at dbs1.net

Connect to dbs1.net as the strmadmin user.

*/
 
CONNECT strmadmin/user-password@dbs1.net

/*

Prepare the tables for instantiation. These tables will be instantiated at dbs3.net. This step marks the lowest SCN of the tables for instantiation. SCNs subsequent to the lowest SCN can be used for instantiation. Also, this preparation is necessary so that the Oracle Streams data dictionary for the relevant propagations and the apply process at dbs3.net contain information about these tables.

Note:

When the PREPARE_TABLE_INSTANTIATION procedure is run in this step, the supplemental_logging parameter is not specified. Therefore, the default value (keys) is used for this parameter. Supplemental logging already was enabled for any primary key, unique key, bitmap index, and foreign key columns in these tables in Step 3.
*/

BEGIN
  DBMS_CAPTURE_ADM.PREPARE_TABLE_INSTANTIATION(
    table_name  => 'hr.departments');
END;
/
 
BEGIN
  DBMS_CAPTURE_ADM.PREPARE_TABLE_INSTANTIATION(
    table_name  => 'hr.employees');
END;
/

BEGIN
  DBMS_CAPTURE_ADM.PREPARE_TABLE_INSTANTIATION(
    table_name  => 'hr.job_history');
END;
/

BEGIN
  DBMS_CAPTURE_ADM.PREPARE_TABLE_INSTANTIATION(
    table_name  => 'hr.jobs');
END;
/

/*

Step 6   Instantiate the dbs1.net Tables at dbs3.net

This example performs a network Data Pump import of the following tables:

A network import means that Data Pump imports these tables from dbs1.net without using an export dump file.

See Also:

Oracle Database Utilities for information about performing an import

Connect to dbs3.net as the strmadmin user.

*/
 
CONNECT strmadmin/user-password@dbs3.net

/*

This example will do a table import using the DBMS_DATAPUMP package. For simplicity, exceptions from any of the API calls will not be trapped. However, Oracle recommends that you define exception handlers and call GET_STATUS to retrieve more detailed error information if a failure occurs. If you want to monitor the import, then query the DBA_DATAPUMP_JOBS data dictionary view at the import database.

*/

SET SERVEROUTPUT ON
DECLARE
  h1        NUMBER;         -- Data Pump job handle
  sscn      NUMBER;         -- Variable to hold current source SCN
  job_state VARCHAR2(30);   -- To keep track of job state
  js        ku$_JobStatus;  -- The job status from GET_STATUS
  sts       ku$_Status;     -- The status object returned by GET_STATUS
  job_not_exist    exception;
  pragma exception_init(job_not_exist, -31626);
BEGIN
-- Create a (user-named) Data Pump job to do a table-level import.
  h1 := DBMS_DATAPUMP.OPEN(
          operation   => 'IMPORT',
          job_mode    => 'TABLE',
          remote_link => 'DBS1.NET',
          job_name    => 'dp_sing3');
-- A metadata filter is used to specify the schema that owns the tables 
-- that will be imported.
  DBMS_DATAPUMP.METADATA_FILTER(
    handle    => h1,
    name      => 'SCHEMA_EXPR',
    value     => '=''HR''');
-- A metadata filter is used to specify the tables that will be imported.
  DBMS_DATAPUMP.METADATA_FILTER(
    handle    => h1,
    name      => 'NAME_EXPR',
    value     => 'IN(''DEPARTMENTS'', ''EMPLOYEES'', 
                     ''JOB_HISTORY'', ''JOBS'')');
-- Get the current SCN of the source database, and set the FLASHBACK_SCN 
-- parameter to this value to ensure consistency between all of the 
-- objects included in the import.
  sscn := DBMS_FLASHBACK.GET_SYSTEM_CHANGE_NUMBER@dbs1.net();
  DBMS_DATAPUMP.SET_PARAMETER(
    handle => h1,
    name   => 'FLASHBACK_SCN',
    value  => sscn); 
-- Start the job. 
  DBMS_DATAPUMP.START_JOB(h1);
-- The import job should be running. In the following loop, the job
-- is monitored until it completes.
  job_state := 'UNDEFINED';
  BEGIN
    WHILE (job_state != 'COMPLETED') AND (job_state != 'STOPPED') LOOP
      sts:=DBMS_DATAPUMP.GET_STATUS(
             handle  => h1,
             mask    => DBMS_DATAPUMP.KU$_STATUS_JOB_ERROR +
                        DBMS_DATAPUMP.KU$_STATUS_JOB_STATUS +
                        DBMS_DATAPUMP.KU$_STATUS_WIP,
             timeout => -1);
      js := sts.job_status;
      DBMS_LOCK.SLEEP(10);
      job_state := js.state;
    END LOOP;
  -- Gets an exception when job no longer exists
    EXCEPTION WHEN job_not_exist THEN
      DBMS_OUTPUT.PUT_LINE('Data Pump job has completed');
      DBMS_OUTPUT.PUT_LINE('Instantiation SCN: ' ||sscn);
  END;
END;
/

/*

Step 7    Start the Apply Process at dbs3.net

Start the apply process at dbs3.net. This apply process was stopped in Step 2.

Connect to dbs3.net as the strmadmin user.

*/
 
CONNECT strmadmin/user-password@dbs3.net

/*

Start the apply process at dbs3.net.

*/

BEGIN
  DBMS_APPLY_ADM.START_APPLY(
    apply_name  => 'apply');
END;
/

/*

Step 8   Check the Spool Results

Check the streams_addobjs.out spool file to ensure that all actions finished successfully after this script is completed.

*/

SET ECHO OFF
SPOOL OFF

/*************************** END OF SCRIPT ******************************/

Make a DML Change to the hr.employees Table

After completing the examples described in the "Add Objects to an Existing Oracle Streams Replication Environment" section, you can make DML and DDL changes to the tables in the hr schema at the dbs1.net database. These changes will be replicated to dbs3.net. You can check these tables at dbs3.net to see that the changes have been replicated.

For example, complete the following steps to make a DML change to the hr.employees table at dbs1.net. Next, query the hr.employees table at dbs3.net to see that the change has been replicated.


Step 1   Make a DML Change to the hr.employees Table

Make the following change:

CONNECT hr/user-password@dbs1.net

UPDATE hr.employees SET job_id='ST_MAN' WHERE employee_id=143;
COMMIT;

Step 2   Query the hr.employees Table at dbs3.net

After some time passes to allow for capture, propagation, and apply of the change performed in the previous step, run the following query to confirm that the UPDATE change made to the hr.employees table at dbs1.net has been applied to the hr.employees table at dbs3.net.

CONNECT hr/user-password@dbs3.net

SELECT job_id FROM hr.employees WHERE employee_id=143;

You should see ST_MAN for the value of the job_id.

Add a Database to an Existing Oracle Streams Replication Environment

This example extends the Oracle Streams environment configured in the previous sections by adding an additional database to the existing configuration. In this example, an existing Oracle database named dbs5.net is added to receive changes to the entire hr schema from the queue at dbs2.net.

Figure 20-3 provides an overview of the environment with the added database.

Figure 20-3 Adding the dbs5.net Oracle Database to the Environment

Description of Figure 20-3 follows
Description of "Figure 20-3 Adding the dbs5.net Oracle Database to the Environment"

To complete this example, you must meet the following prerequisites:

Complete the following steps to add dbs5.net to the Oracle Streams environment.

  1. Show Output and Spool Results

  2. Drop All of the Tables in the hr Schema at dbs5.net

  3. Set Up Users at dbs5.net

  4. Create the ANYDATA Queue at dbs5.net

  5. Create a Database Link at dbs5.net to dbs1.net

  6. Configure the Apply Process at dbs5.net

  7. Specify hr as the Apply User for the Apply Process at dbs5.net

  8. Grant the hr User Execute Privilege on the Apply Process Rule Set

  9. Create the Database Link Between dbs2.net and dbs5.net

  10. Configure Propagation Between dbs2.net and dbs5.net

  11. Prepare the hr Schema for Instantiation at dbs1.net

  12. Instantiate the dbs1.net Tables at dbs5.net

  13. Start the Apply Process at dbs5.net

  14. Check the Spool Results

Note:

If you are viewing this document online, then you can copy the text from the "BEGINNING OF SCRIPT" line after this note to the next "END OF SCRIPT" line into a text editor and then edit the text to create a script for your environment. Run the script with SQL*Plus on a computer that can connect to all of the databases in the environment.
/************************* BEGINNING OF SCRIPT ******************************

Step 1   Show Output and Spool Results

Run SET ECHO ON and specify the spool file for the script. Check the spool file for errors after you run this script.

*/

SET ECHO ON
SPOOL streams_adddb.out

/*

Step 2   Drop All of the Tables in the hr Schema at dbs5.net

This example illustrates instantiating the tables in the hr schema by importing them from dbs1.net into dbs5.net using Data Pump. You must delete these tables at dbs5.net for the instantiation portion of this example to work properly.

Connect as hr at dbs5.net.

*/

CONNECT hr/user-password@dbs5.net

/*

Drop all tables in the hr schema in the dbs5.net database.

Note:

If you complete this step and drop all of the tables in the hr schema, then you should complete the remaining sections of this example to reinstantiate the hr schema at dbs5.net. If the hr schema does not exist in an Oracle database, then some examples in the Oracle documentation set can fail.
*/

DROP TABLE hr.countries CASCADE CONSTRAINTS;
DROP TABLE hr.departments CASCADE CONSTRAINTS;
DROP TABLE hr.employees CASCADE CONSTRAINTS;
DROP TABLE hr.job_history CASCADE CONSTRAINTS;
DROP TABLE hr.jobs CASCADE CONSTRAINTS;
DROP TABLE hr.locations CASCADE CONSTRAINTS;
DROP TABLE hr.regions CASCADE CONSTRAINTS;

/*

Step 3   Set Up Users at dbs5.net

Connect to dbs5.net as SYSTEM user.

*/
 
CONNECT system/user-password@dbs5.net

/*

Create the Oracle Streams administrator named strmadmin and grant this user the necessary privileges. These privileges enable the user to manage queues, execute subprograms in packages related to Oracle Streams, create rule sets, create rules, and monitor the Oracle Streams environment by querying data dictionary views and queue tables. You can choose a different name for this user.

Note:

The ACCEPT command must appear on a single line in the script.

See Also:

Oracle Streams Concepts and Administration for more information about configuring an Oracle Streams administrator
*/

GRANT DBA TO strmadmin IDENTIFIED BY user-password;

ACCEPT streams_tbs PROMPT 'Enter Oracle Streams administrator tablespace on dbs5.net: '

ALTER USER strmadmin DEFAULT TABLESPACE &streams_tbs
                     QUOTA UNLIMITED ON &streams_tbs;

/*

Step 4   Create the ANYDATA Queue at dbs5.net

Connect as the Oracle Streams administrator at the database you are adding. In this example, that database is dbs5.net.

*/

CONNECT strmadmin/user-password@dbs5.net

/*

Run the SET_UP_QUEUE procedure to create a queue named streams_queue at dbs5.net. This queue will function as the ANYDATA queue by holding the changes that will be applied at this database.

Running the SET_UP_QUEUE procedure performs the following actions:

*/

EXEC  DBMS_STREAMS_ADM.SET_UP_QUEUE();

/*

Step 5   Create a Database Link at dbs5.net to dbs1.net

Create a database link from dbs5.net to dbs1.net. Later in this example, this database link is used for the instantiation of the database objects that were dropped in Step 2. This example uses the DBMS_DATAPUMP package to perform a network import of these database objects directly from the dbs1.net database. Because this example performs a network import, no dump file is required.

Alternatively, you can perform an export at the source database dbs1.net, transfer the export dump file to the destination database dbs5.net, and then import the export dump file at the destination database. In this case, the database link created in this step is not required.

*/

CREATE DATABASE LINK dbs1.net CONNECT TO strmadmin IDENTIFIED BY user-password
   USING 'dbs1.net';

/*

Step 6   Configure the Apply Process at dbs5.net

While still connected as the Oracle Streams administrator at dbs5.net, configure the apply process to apply changes to the hr schema.

*/

BEGIN
  DBMS_STREAMS_ADM.ADD_SCHEMA_RULES(
    schema_name     => 'hr',   
    streams_type    => 'apply',
    streams_name    => 'apply',
    queue_name      => 'strmadmin.streams_queue',
    include_dml     => TRUE,
    include_ddl     => TRUE,
    source_database => 'dbs1.net',
    inclusion_rule  => TRUE);
END;
/

/*

Step 7   Specify hr as the Apply User for the Apply Process at dbs5.net

In this example, the hr user owns all of the database objects for which changes are applied by the apply process at this database. Therefore, hr already has the necessary privileges to change these database objects, and it is convenient to make hr the apply user.

When the apply process was created in the previous step, the Oracle Streams administrator strmadmin was specified as the apply user by default, because strmadmin ran the procedure that created the apply process. Instead of specifying hr as the apply user, you could retain strmadmin as the apply user, but then you must grant strmadmin privileges on all of the database objects for which changes are applied and privileges to execute all user procedures used by the apply process. In an environment where an apply process applies changes to database objects in multiple schemas, it might be more convenient to use the Oracle Streams administrator as the apply user.

See Also:

Oracle Streams Concepts and Administration for more information about configuring an Oracle Streams administrator
*/

BEGIN
  DBMS_APPLY_ADM.ALTER_APPLY(
    apply_name => 'apply',
    apply_user => 'hr');
END;
/

/*

Step 8   Grant the hr User Execute Privilege on the Apply Process Rule Set

Because the hr user was specified as the apply user in the previous step, the hr user requires EXECUTE privilege on the positive rule set used by the apply process

*/

DECLARE
   rs_name  VARCHAR2(64);   -- Variable to hold rule set name
BEGIN
  SELECT RULE_SET_OWNER||'.'||RULE_SET_NAME 
    INTO rs_name 
    FROM DBA_APPLY 
    WHERE APPLY_NAME='APPLY';
  DBMS_RULE_ADM.GRANT_OBJECT_PRIVILEGE(
    privilege   => SYS.DBMS_RULE_ADM.EXECUTE_ON_RULE_SET,
    object_name => rs_name,
    grantee     => 'hr');
END;
/

/*

Step 9   Create the Database Link Between dbs2.net and dbs5.net

Connect to dbs2.net as the strmadmin user.

*/
 
CONNECT strmadmin/user-password@dbs2.net

/*

Create the database links to the databases where changes are propagated. In this example, database dbs2.net propagates changes to dbs5.net.

*/

CREATE DATABASE LINK dbs5.net CONNECT TO strmadmin IDENTIFIED BY user-password
   USING 'dbs5.net';

/*

Step 10   Configure Propagation Between dbs2.net and dbs5.net

While still connected as the Oracle Streams administrator at dbs2.net, configure and schedule propagation from the queue at dbs2.net to the queue at dbs5.net. Remember, changes to the hr schema originated at dbs1.net.

*/

BEGIN
  DBMS_STREAMS_ADM.ADD_SCHEMA_PROPAGATION_RULES(
    schema_name             => 'hr', 
    streams_name            => 'dbs2_to_dbs5', 
    source_queue_name       => 'strmadmin.streams_queue',
    destination_queue_name  => 'strmadmin.streams_queue@dbs5.net',
    include_dml             => TRUE,
    include_ddl             => TRUE,
    source_database         => 'dbs1.net',
    inclusion_rule          => TRUE,
    queue_to_queue          => TRUE);
END;
/

/*

Step 11   Prepare the hr Schema for Instantiation at dbs1.net

Connect to dbs1.net as the strmadmin user.

*/
 
CONNECT strmadmin/user-password@dbs1.net

/*

Prepare the hr schema for instantiation. These tables in this schema will be instantiated at dbs5.net. This preparation is necessary so that the Oracle Streams data dictionary for the relevant propagations and the apply process at dbs5.net contain information about the hr schema and the objects in the schema.

*/

BEGIN
  DBMS_CAPTURE_ADM.PREPARE_SCHEMA_INSTANTIATION(
    schema_name          => 'hr',
    supplemental_logging => 'keys');
END;
/

/*

Step 12   Instantiate the dbs1.net Tables at dbs5.net

This example performs a network Data Pump import of the following tables:

A network import means that Data Pump imports these tables from dbs1.net without using an export dump file.

See Also:

Oracle Database Utilities for information about performing an import

Connect to dbs5.net as the strmadmin user.

*/
 
CONNECT strmadmin/user-password@dbs5.net

/*

This example will do a table import using the DBMS_DATAPUMP package. For simplicity, exceptions from any of the API calls will not be trapped. However, Oracle recommends that you define exception handlers and call GET_STATUS to retrieve more detailed error information if a failure occurs. If you want to monitor the import, then query the DBA_DATAPUMP_JOBS data dictionary view at the import database.

*/

SET SERVEROUTPUT ON
DECLARE
  h1        NUMBER;         -- Data Pump job handle
  sscn      NUMBER;         -- Variable to hold current source SCN
  job_state VARCHAR2(30);   -- To keep track of job state
  js        ku$_JobStatus;  -- The job status from GET_STATUS
  sts       ku$_Status;     -- The status object returned by GET_STATUS
  job_not_exist    exception;
  pragma exception_init(job_not_exist, -31626);
BEGIN
-- Create a (user-named) Data Pump job to do a table-level import.
  h1 := DBMS_DATAPUMP.OPEN(
          operation   => 'IMPORT',
          job_mode    => 'TABLE',
          remote_link => 'DBS1.NET',
          job_name    => 'dp_sing4');
-- A metadata filter is used to specify the schema that owns the tables 
-- that will be imported.
  DBMS_DATAPUMP.METADATA_FILTER(
    handle    => h1,
    name      => 'SCHEMA_EXPR',
    value     => '=''HR''');
-- Get the current SCN of the source database, and set the FLASHBACK_SCN 
-- parameter to this value to ensure consistency between all of the 
-- objects included in the import.
  sscn := DBMS_FLASHBACK.GET_SYSTEM_CHANGE_NUMBER@dbs1.net();
  DBMS_DATAPUMP.SET_PARAMETER(
    handle => h1,
    name   => 'FLASHBACK_SCN',
    value  => sscn); 
-- Start the job. 
  DBMS_DATAPUMP.START_JOB(h1);
-- The import job should be running. In the following loop, the job
-- is monitored until it completes.
  job_state := 'UNDEFINED';
  BEGIN
    WHILE (job_state != 'COMPLETED') AND (job_state != 'STOPPED') LOOP
      sts:=DBMS_DATAPUMP.GET_STATUS(
             handle  => h1,
             mask    => DBMS_DATAPUMP.KU$_STATUS_JOB_ERROR +
                        DBMS_DATAPUMP.KU$_STATUS_JOB_STATUS +
                        DBMS_DATAPUMP.KU$_STATUS_WIP,
             timeout => -1);
      js := sts.job_status;
      DBMS_LOCK.SLEEP(10);
      job_state := js.state;
    END LOOP;
  -- Gets an exception when job no longer exists
    EXCEPTION WHEN job_not_exist THEN
      DBMS_OUTPUT.PUT_LINE('Data Pump job has completed');
      DBMS_OUTPUT.PUT_LINE('Instantiation SCN: ' ||sscn);
  END;
END;
/

/*

Step 13   Start the Apply Process at dbs5.net

Connect as the Oracle Streams administrator at dbs5.net.

*/

CONNECT strmadmin/user-password@dbs5.net

/*

Set the disable_on_error parameter to n so that the apply process will not be disabled if it encounters an error, and start apply process at dbs5.net.

*/

BEGIN
  DBMS_APPLY_ADM.SET_PARAMETER(
    apply_name  => 'apply', 
    parameter   => 'disable_on_error', 
    value       => 'n');
END;
/

BEGIN
  DBMS_APPLY_ADM.START_APPLY(
    apply_name  => 'apply');
END;
/

/*

Step 14   Check the Spool Results

Check the streams_adddb.out spool file to ensure that all actions finished successfully after this script is completed.

*/

SET ECHO OFF
SPOOL OFF

/*************************** END OF SCRIPT ******************************/

Make a DML Change to the hr.departments Table

After completing the examples described in the "Add a Database to an Existing Oracle Streams Replication Environment" section, you can make DML and DDL changes to the tables in the hr schema at the dbs1.net database. These changes will be replicated to dbs5.net. You can check these tables at dbs5.net to see that the changes have been replicated.

For example, complete the following steps to make a DML change to the hr.departments table at dbs1.net. Next, query the hr.departments table at dbs5.net to see that the change has been replicated.


Step 1   Make a DML Change to the hr.departments Table

Make the following change:

CONNECT hr/user-password@dbs1.net

UPDATE hr.departments SET location_id=2400 WHERE department_id=270;
COMMIT;

Step 2   Query the hr.departments Table at dbs5.net

After some time passes to allow for capture, propagation, and apply of the change performed in the previous step, run the following query to confirm that the UPDATE change made to the hr.departments table at dbs1.net has been applied to the hr.departments table at dbs5.net.

CONNECT hr/user-password@dbs5.net

SELECT location_id FROM hr.departments WHERE department_id=270;

You should see 2400 for the value of the location_id.