10.5 Extracting DDL and DataOracle supplies the Export (exp) and Import (imp) utilities to export Oracle database objects and then import them back into Oracle databases. The Export utility extracts not only the data for these objects but the DDL (Data Definition Language) to create them. These venerable utilities work well enough, but sometimes fall short in a number of important ways:
We've included two Perl/DBI scripts in the toolkit to help fill these gaps. Using sqlunldr.pl and ddl_oracle.pl you can dump all schema data to comma-delimited files, generate SQL*Loader control and parameter files, and then generate DDL for all user tablespaces and schemas. 10.5.1 Extracting Data With sqlunldr.plSometimes you need raw portable data — to populate another database, build a customer's spreadsheet, or perform some other data operation. Unfortunately, Oracle's Export utility is the wrong mousetrap. One popular solution to such problems is to build SQL*Plus dump scripts. This approach works for single tables, but grows cumbersome when dumping entire schemas or even a handful of selected tables. What's needed is a single dump utility that creates portable output. It would also be nice if the data field separators were configurable and if enclosed quote characters were both configurable and optional. The sqlunldr.pl script fits the bill on all counts. Here are some of its main features. In the following sections we'll include several examples that show you to use this script.
The script's command-line options are summarized in Table 10-8.
10.5.1.1 Dumping and reloading SCOTT's schemaIn this first example, we'll use sqlunldr.pl to dump the entire scott schema. This time we can ignore the -dateformat option, as we'll simply reload the data straight back into the same database. However, we'd need this option if the data were to be loaded into a database with a different NLS_DATE_FORMAT. Here's the command to dump the scott schema: $ sqlunldr.pl -machine watson -database ts99 -username system \ -owner scott -noheader -schemadump Once this entirely portable command completes, you'll find all the output in the scott.dump directory. Example 10-19 displays the output on a Unix system. Example 10-19. Dumping the SCOTT schema with sqlunldr.pl%oramon> sqlunldr.pl -machine watson -database ts99 -username system \ -owner scott -noheader -schemadump -rw-r--r-- 1 jkstill dba 56 May 20 01:17 bincol_test.txt -rw-r--r-- 1 jkstill dba 129 May 20 01:17 bonus.ctl -rw-r--r-- 1 jkstill dba 67 May 20 01:17 bonus.par -rw-r--r-- 1 jkstill dba 0 May 20 01:17 bonus.txt -rw-r--r-- 1 jkstill dba 123 May 20 01:17 dept.ctl -rw-r--r-- 1 jkstill dba 64 May 20 01:17 dept.par -rw-r--r-- 1 jkstill dba 104 May 20 01:17 dept.txt -rw-r--r-- 1 jkstill dba 167 May 20 01:17 emp.ctl -rw-r--r-- 1 jkstill dba 61 May 20 01:17 emp.par -rw-r--r-- 1 jkstill dba 661709 May 20 01:17 emp.txt -rw-r--r-- 1 jkstill dba 132 May 20 01:17 salgrade.ctl -rw-r--r-- 1 jkstill dba 76 May 20 01:17 salgrade.par -rw-r--r-- 1 jkstill dba 89 May 20 01:17 salgrade.txt %oramon> Let's see what's going on in this code:
Example 10-20. SQL*Loader log file — dept.logTable DEPT, loaded from every logical record. Column Name Position Len Term Encl Datatype ------------------------------ ---------- ----- ---- ---- ---------------- DNAME NEXT * , O(") CHARACTER LOC NEXT * , O(") CHARACTER Table DEPT: 4 Rows successfully loaded. 0 Rows not loaded due to data errors. 0 Rows not loaded because all WHEN clauses were failed. 0 Rows not loaded because all fields were null. Space allocated for bind array: 49536 bytes(64 rows) Space allocated for memory besides bind array: 0 bytes Total logical records skipped: 0 Total logical records read: 4 Total logical records rejected: 0 Total logical records discarded: 0 Run began on Sun Feb 24 17:18:17 2002 Run ended on Sun Feb 24 17:18:24 2002 Elapsed time was: 00:00:07.40 CPU time was: 00:00:00.10 10.5.1.2 Dumping binary dataNow we'll look at a more complex example. We'll create a table with one column of plain text, convert it to unreadable binary form, and then dump the table with sqlunldr.pl. We'll then delete all of the data from the table, reload it with the generated SQL*Loader scripts, and then validate it. Example 10-21 shows a test of this operation. Example 10-21. Binary data test1 DROP TABLE BINCOL_TEST; 2 CREATE TABLE BINCOL_TEST 3 (CLEAR_TEXT VARCHAR2(10), BINARY_DATA VARCHAR2(10)); 4 5 INSERT INTO BINCOL_TEST(CLEAR_TEXT) VALUES('Post-Dated'); 6 INSERT INTO BINCOL_TEST(CLEAR_TEXT) VALUES('Check'); 7 INSERT INTO BINCOL_TEST(CLEAR_TEXT) VALUES('Loan'); 8 COMMIT; 9 10 VAR xorstr VARCHAR2(10) 11 12 BEGIN 13 :xorstr := RPAD(CHR(127),10,CHR(127)); 14 END; 15 / 16 17 UPDATE BINCOL_TEST 18 SET BINARY_DATA = 19 UTL_RAW.CAST_TO_VARCHAR2( 20 UTL_RAW.BIT_XOR( 21 UTL_RAW.CAST_TO_RAW(CLEAR_TEXT), 22 UTL_RAW.CAST_TO_RAW(SUBSTR(:xorstr,1,LENGTH(CLEAR_TEXT))) 23 ) 24 ); 25 COMMIT; 26 SET TERM OFF 27 SPOOL BINCOL_TEST.LOG 28 SELECT * FROM BINCOL_TEST; 29 SPOOL OFF 30 SET TERM ON 31 32 ED bincol_test.log 33 SELECT UTL_RAW.CAST_TO_VARCHAR2( 34 UTL_RAW.BIT_XOR( 35 UTL_RAW.CAST_TO_RAW(BINARY_DATA), 36 UTL_RAW.CAST_TO_RAW(:XORSTR) 37 ) 38 ) 39 FROM BINCOL_TEST; You'll find the example scripts bincol_test.sql and bincol_test2.sql in the following locations, so you can run these tests yourself if you wish:
Let's see what's going on in this example:
10.5.2 Extracting DDL with ddl_oracle.plAlthough Perl-based Oracle tools like Orac and Oracletool supply options to regenerate DDL, and although the DDL::Oracle module was designed to explicitly perform this operation (we describe all of these applications in Chapter 3 and Chapter 4), it would sometimes be convenient to extract DDL from a database in one easy operation. We could then recreate all the objects in user and application schemas. Oracle's Export utility does extract all of this information, but often in unusable form. It is possible to use the indexfile=myddl.sql construct with the Export utility to extract the DDL for tables, indexes, and constraints from an Oracle export file, but this utility fails to cover packages, procedures, functions, and triggers. To fill the gap, we've developed the ddl_oracle.pl script to generate the DDL to recreate the following schema database elements; in the list we've noted any exceptions to what can be generated:
Before going any further, let's examine the configurationfile exp_exclude.conf. This file is simply a list of user accounts and roles that we don't care to preserve in DDL files, often because they are created for us when creating a new database. The file included in the PDBA distribution is shown in Example 10-23. Most of the generic Oracle database users and roles are included. Example 10-23. exp_exclude.confpackage expexclude; use vars qw{ @users }; @users = qw{ SYS SYSTEM OUTLN DBSNMP TRACESVR ORDSYS ORDPLUGINS MDSYS AURORA$JIS$UTILITY$ OSE$HTTP$ADMIN AURORA$ORB$UNAUTHENTICATED }; @roles = qw { CONNECT RESOURCE DBA SELECT_ CATALOG_ROLE EXECUTE_CATALOG_ROLE DELETE_CATALOG_ROLE EXP_FULL_DATABASE IMP_FULL_DATABASE RECOVERY_CATALOG_OWNER AQ_ADMINISTRATOR_ROLE AQ_USER_ROLE SNMPAGENT OEM_MONITOR HS_ADMIN_ROLE JAVAUSERPRIV JAVAIDPRIV JAVASYSPRIV JAVADEBUGPRIV JAVA_ADMIN JAVA_DEPLOY PLUSTRACE TIMESERIES_DEVELOPER TIMESERIES_DBA CTXAPP }; 1; Make sure that the exp_exclude.conf file is in your PDBA_HOME directory, as with other configuration files described earlier in this chapter. There's no need to edit exp_exclude.conf unless you need to edit the roles or user lists. For instance, if you want OUTLN included in DDL generation, remove it from @users. Most of the DDL generation is accomplished via Richard Sutherland's DDL::Oracle module, which we discussed in Chapter 3. If that application is not available on your system, you will need to install it (making sure to use at least Version 1.10). Here's a Perl one-liner to determine what version of DDL::Oracle you are using: perl -e "use DDL::Oracle 1.10; print qq{OK!\n}" If the proper version is installed, you'll see OK! printed on the screen; otherwise, you'll need to install the latest version. This is easier for Win32 users. Simply start the ActiveState PPM package manager and install directly as follows: C:\> ppm PPM> install DDL::Oracle Unix users will need to download the latest version and install it. If you want to do a manual install (as we described in Chapter 2), you can get the file at http://search.cpan.org/search?dist=DDL-Oracle. For a direct CPAN install, do the following: $ perl -MCPAN -e "shell" cpan> install DDL::Oracle ... Running make for R/RV/RVSUTHERL/DDL-Oracle-1.10.tar.gz ... Although we make use of DDL::Oracle to generate much of the DDL output from ddl_oracle.pl, we don't use it for all of the DDL. Let's see why. One of the goals of the script was to be able to generate a single file of all object grants made on a schema's objects from the perspective of the grantor, or owner, of the objects. If the SCOTT schema owns 10 tables and SCOTT has issued SELECT grants on all of his tables to the JONES, ADAMS, and CLARK accounts, we wanted a single script to contain all of those grants. This way, the DBA needs to issue only a single Oracle logon to the SCOTT account so that the DDL script containing those grants can be run. The DDL::Oracle module generates grants from the perspective of the grantee, or recipient of the granted privileges. In the case of generating the DDL required to grant SELECT privileges to the JONES, ADAMS, and CLARK accounts, this would require three separate logons by the SCOTT account to create those grants. In our script, we crafted our own DDL generation for certain aspects to get just the output we wanted. For example, we did this in generating the DDL to create users and roles, and in generating the GRANT statements by grantor rather than grantee.[9]
The command line for ddl_oracle.pl is rather basic, with the usual command-line options summarized in Table 10-9.
Running ddl_oracle.pl is very simple, as shown here: $ ddl_oracle.pl -machine watson -database ts99 -username system Building List working on profiles working on public database links working on public synonyms working on rollback segments working on tablespaces working on users working on SCOTT working on COMMON working on JKSTILL working on PDBA_ROLE ... The output consists of a number of SQL scripts; each script name is prefixed with a number, which indicates the order in which the scripts need to be run (assuming that all are to be used). An abbreviated version of the 1_create.sql script is shown in Example 10-24. See Table 10-10 for all of the script names.
The output is designed so it could be run sequentially by running 1_create.sql, but you'll rarely do things this way. By grouping objects and privileges by owner, it's a bit easier for you to recreate a single schema. This approach also reduces the number of files you must deal with. Example 10-24. The 1_create.sql script@@2_tbs_ddl.sql @@3_rbs_ddl.sql @@4_pub_db_link.sql @@5_pub_synonyms.sql @@6_user_profiles.sql @@7_role_ddl.sql @@8_user_ddl.sql PROMPT connecting to SCOTT - please enter the password CONNECT SCOTT @@9_schema_scott.sql @@10_grant_scott.sql PROMPT connecting to PDBAREP - please enter the password CONNECT PDBAREP @@9_schema_pdbarep.sql @@10_grant_pdbarep.sql ... |