DevX - Use An Oracle API To Extract and Store Database Objects' DDL
DevX - Use An Oracle API To Extract and Store Database Objects' DDL
DevX: Use an Oracle API to Extract and Store Database Objects' DDL
http://www.devx.com/dbzone/10MinuteSolution/21558
Though there are plenty of scripts available extract database objects' metadata from the various
Oracle dictionaries, most of them are either incomplete or out-of-date.
Learn how to perform the above task in the most optimal, error-free, and maintenance-free way
using the DBMS_METADATApackage.Using Oracle Native APIs: The DBMS_METADATA Package
Oracle databases provide a rich set of prepackaged APIs in the form of Supplied PL/SQL
Packages. One of the packages introduced in Oracle 9.2 version is exactly what you need: the
www.devx.com/print/dbzone/10MinuteSolution/21558/0/page/2
1/6
10/09/13
DevX: Use an Oracle API to Extract and Store Database Objects' DDL
DBMS_METADATA package. It contains APIs for retrieving database objects' definitions. Click
here for the reference page for DBMS_METADATA.
The main API you'll be using is the DBMS_METADATA.GET_DDLfunction. This function returns an
object definition SQL string as a CLOB. It takes the following INparameters:
object_type VARCHAR2
name VARCHAR2
schema VARCHAR2 DEFAULT NULL
version VARCHAR2 DEFAULT 'COMPATIBLE'
model VARCHAR2 DEFAULT 'ORACLE',
transform VARCHAR2 DEFAULT 'DDL'
The code below creates a table EmpTestwith indexes and constraints that you'll be using to test
the code:
create table EmpTest
(
empNo
integer
not null,
lastName varchar2(30) not null,
firstName varchar2(20) not null,
job
varchar2(9)
'
hireDate date
'
isActive number(1)
constraint EmpTest_CK1
check (isActive in (0,1))
,
salary
number(9,2)
,
commision number(9,2)
,
deptNo
number(2)
,
constraint EmpTest_PK
primary key (empNo),
constraint EmpTest_AK1
unique (lastName, firstName)
);
create index EmpTest_HireDate_Salary
on EmpTest
(
salary,
hireDate
);
After running the above script, EmpTesttable has been created with three indexes (two unique
and one nonunique):
select index_name,
index_type,
uniqueness
from user_indexes
where table_name = EMPTEST;
INDEX_NAME
INDEX_TYPE
UNIQUENESS
EMPTEST_AK1
NORMAL
UNIQUE
EMPTEST_HIREDATE_SALARY NORMAL
NONUNIQUE
EMPTEST_PK
NORMAL
UNIQUE
2/6
10/09/13
DevX: Use an Oracle API to Extract and Store Database Objects' DDL
CONSTRAINT_TYPE INDEX_NAME
SYS_C002144065
SYS_C002144066
SYS_C002144067
EMPTEST_CK1
EMPTEST_PK
EMPTEST_AK1
C
C
C
C
P
U
EMPTEST_PK
EMPTEST_AK1
Now, execute an anonymous PL/SQL code block that calls the DBMS_METADATA.GET_DDLfunction
to retrieve EmpTesttable definition.
The DBMS_OUTPUTpackage can only output strings up to 255 characters long, which is a problem
because it's easy to exceed such a limit while working with tables' DDL strings. To overcome this
limitation, the local procedure Show()is used (Listing 1).
Listing 1 produces the following output:
DDL length: 461
CREATE TABLE "BORIS"."EMPTEST"
(
"EMPNO" NUMBER(*,0) NOT NULL ENABLE,
"LASTNAME" VARCHAR2(30) NOT NULL ENABLE,
"FIRSTNAME" VARCHAR2(20) NOT NULL ENABLE,
"JOB" VARCHAR2(9),
"HIREDATE" DATE,
"ISACTIVE" NUMBER(1,0),
"SALARY" NUMBER(9,2),
"COMMISION" NUMBER(9,2),
"DEPTNO" NUMBER(2,0),
CONSTRAINT "EMPTEST_CK1" CHECK (isActive in (0,1)) ENABLE,
CONSTRAINT "EMPTEST_PK" PRIMARY KEY ("EMPNO")
USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
TABLESPACE "TOOLS" ENABLE,
CONSTRAINT "EMPTEST_AK1" UNIQUE ("LASTNAME", "FIRSTNAME")
USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1
MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL
DEFAULT)
TABLESPACE "TOOLS" ENABLE
) PCTFREE 10 PCTUSED 40 INITRANS 1
MAXTRANS 255 NOCOMPRESS LOGGING
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
TABLESPACE "TOOLS"
So far so good, though it returned the table's DDL string with the primary key EmpTest_PK, the
alternate key EmpTest_AK1, and checked constraint EmpTest_CK1altogether. Two unique
indexes are created to support the primary and alternate key constraints. This isn't exactly what
you want: you want a table without constraints and indexes to speed up the data load. Only
when the data load is completed do you create indexes and constraints. Another reason to keep
each object definition separate is flexibility: you may need to change the order of the objects'
creation.
Now it's time to design a structure for storing all of your objects' metadata.
The Metadata Memory Repository: MetaDataPkg Package Specification
First, you need to create a record type to store all of the necessary information for individual
www.devx.com/print/dbzone/10MinuteSolution/21558/0/page/2
3/6
10/09/13
DevX: Use an Oracle API to Extract and Store Database Objects' DDL
Next, you need to create a record type that consists of one tMetaObjectattribute for the table
itself (aTable) and three tArrayMetaObjectattributes: one for indexes (aIndexes), one for
constraints (aConstraints), and one for triggers (aTriggers):
type tFullMetaObject is record
(
aTable
tMetaObject,
aIndexes
tArrayMetaObject,
aConstraints tArrayMetaObject,
aTriggers
tArrayMetaObject
);
The object of type tFullMetaObjectholds all the objects' metadata for a single table. Finally,
the top level type is the associative array of tFullMetaObject type
tArrayFullMetaObjectByString is table of tFullMetaObject index by varchar2(30); The above
type's object serves as a metadata memory repository and holds the complete set
of metadata information for multiple tables.
All of the above types are contained in the MetaDataPkg package specification
(Listing 2). I've also introduced the following API's:
www.devx.com/print/dbzone/10MinuteSolution/21558/0/page/2
4/6
10/09/13
DevX: Use an Oracle API to Extract and Store Database Objects' DDL
The next three lines emit segment attributes (physical attributes, storage
attributes, tablespace, logging, etc.), storage and tablespace clauses for
tables, and indexes' object definitions:
dbms_metadata.set_transform_param(
dbms_metadata.session_transform, 'SEGMENT_ATTRIBUTES', true);
dbms_metadata.set_transform_param(
dbms_metadata.session_transform, 'STORAGE', true);
dbms_metadata.set_transform_param(
dbms_metadata.session_transform, 'TABLESPACE', true);
It's important to specify all of the physical, storage, and logging attributes
explicitlyotherwise, they'll be set to defaults that may differ from the
originally set values.
The last three lines of the SetEnvironment() procedure prevent all of the nonreferential and referential constraints from being included in the table's DDL.
It also suppresses emitting table constraints as separate ALTER TABLE (and, if
necessary, CREATE INDEX) statements:
dbms_metadata.set_transform_param(
dbms_metadata.session_transform, 'CONSTRAINTS', false);
dbms_metadata.set_transform_param(
dbms_metadata.session_transform, 'REF_CONSTRAINTS', false);
dbms_metadata.set_transform_param(
dbms_metadata.session_transform, 'CONSTRAINTS_AS_ALTER', false);
For maximum flexibility, extract object definitions for tables, indexes, and
constraints separately and keep them separate. That way you control the order in
which they are created.
MetaDataPkg package's main workhorse is the MetaDataPkg.GetDDL() function.
MetaDataPkg.GetDDL() contains an extended version of the code from the Listing 1.
www.devx.com/print/dbzone/10MinuteSolution/21558/0/page/2
5/6
10/09/13
DevX: Use an Oracle API to Extract and Store Database Objects' DDL
What's been added is the ability to extract DDL strings longer than 32767
characters. This helps to handle partitioned table definitionswhich can get
rather long when the number of partitions grows. That's why the GetDDL() code
parses and loads the DDL string into the vLongStrings array of strings up to
32767 characters each. The current code version only returns the first array's
element, so you need to modify the code and make the array an attribute of
tMetaObject record type. This allows it to handle DDL strings longer than 32767
characters, which is rather rare.
Use the MetaDataPkg.GetMeta() API to get complete metadata objects per specified
table. This API accepts two parameters: pTable, which is the table name, and
pForce, the Boolean flag. When the pForce flag is set to TRUE, it forces metadata
retrieval from Oracle dictionaries and then loads the metadata into the memory
repositorywhether or not it's there already. However, the default value is
FALSE, so the first call loads metadata into the memory repository and returns
the object of tFullMetaObject type. Any subsequent GetMeta() calls simply
retrieve the metadata from the repository.
Using the MetaDataPkg Package
To illustrate how to use the MetaDataPkg package, I created a small anonymous
block code. It loads the medatada of EmpTest table in to the metadata repository
and then prints it's content.
This is the anonymous PL/SQL block:
declare
vTable
MetaDataPkg.tString := 'EmpTest';
vRunStartTime number;
begin
vRunStartTime := dbms_utility.get_time;
MetaDataPkg.Load(vTable, true);
MetaDataPkg.Show();
dbms_output.put_line('Time Elapsed: ' ||
to_char((dbms_utility.get_time - vRunStartTime) / 100) || ' sec.');
end;
/
www.devx.com/print/dbzone/10MinuteSolution/21558/0/page/2
6/6