You are on page 1of 139

1. Write a query to eliminate duplicate rows in a table?

Delete from emp x where rowid <= ( select max(rowid) from emp y where
x.rowid = y.rowid)

2. Write a query to find max 5 salaries of employees ?

Select * from (select * from emp order by sal desc) where rownum < 6

Show all data for Clerks hired after the year 1997

Select * from employees where job_id=’ST_CLERK’ and hire_date >’31-dec-


1997’;

2) show the last name,job,salary and commission of those employees


who earn commission sort the data by the salary in descending order

SELECT LAST_NAME,JOB_ID,SALARY,COMMISSION_PCT FROM


EMPLOYEES WHERE COMMISSION_PCT IS NOT NULL ORDER BY
SALARY DESC;

3) Show the employees who have no commission not have a 10% raise in
salary (round off salaries)

Select ‘the salary of’|| last_name||’after a 10% raise is’ from employees
Where commission_pct is null;

What is SQL and where does it come from?

Structured Query Language (SQL) is a language that provides an interface to relational


database systems. SQL was developed by IBM in the 1970s for use in System R, and is a
de facto standard, as well as an ISO and ANSI standard. SQL is often pronounced
SEQUEL.

In common usage SQL also encompasses DML (Data Manipulation Language), for
INSERTs, UPDATEs, DELETEs and DDL (Data Definition Language), used for creating
and modifying tables and other database structures.

The development of SQL is governed by standards. A major revision to the SQL standard
was completed in 1992, called SQL2. SQL3 support object extensions and are (partially?)
implemented in Oracle8 and 9.

Back to top of file


What are the difference between DDL, DML and DCL commands?

DDL is Data Definition Language statements. Some examples:


 CREATE - to create objects in the database
 ALTER - alters the structure of the database
 DROP - delete objects from the database
 TRUNCATE - remove all records from a table, including all spaces allocated for
the records are removed
 COMMENT - add comments to the data dictionary
 GRANT - gives user's access privileges to database
 REVOKE - withdraw access privileges given with the GRANT command

DML is Data Manipulation Language statements. Some examples:

 SELECT - retrieve data from the a database


 INSERT - insert data into a table
 UPDATE - updates existing data within a table
 DELETE - deletes all records from a table, the space for the records remain
 CALL - call a PL/SQL or Java subprogram
 EXPLAIN PLAN - explain access path to data
 LOCK TABLE - control concurrency

DCL is Data Control Language statements. Some examples:

 COMMIT - save work done


 SAVEPOINT - identify a point in a transaction to which you can later roll back
 ROLLBACK - restore database to original since the last COMMIT
 SET TRANSACTION - Change transaction options like what rollback segment to
use

Back to top of file

How does one escape special characters when building SQL queries?

The LIKE keyword allows for string searches. The '_' wild card character is used to
match exactly one character, '%' is used to match zero or more occurrences of any
characters. These characters can be escaped in SQL. Example:
SELECT name FROM emp WHERE id LIKE '%\_%' ESCAPE '\';

Use two quotes for every one displayed. Example:

SELECT 'Franks''s Oracle site' FROM DUAL;


SELECT 'A ''quoted'' word.' FROM DUAL;
SELECT 'A ''''double quoted'''' word.' FROM DUAL;
Back to top of file

How does one eliminate duplicates rows from a table?

Choose one of the following queries to identify or remove duplicate rows from a table
leaving only unique records in the table:

Method 1:

SQL> DELETE FROM table_name A WHERE ROWID > (


2 SELECT min(rowid) FROM table_name B
3 WHERE A.key_values = B.key_values);
Method 2:
SQL> create table table_name2 as select distinct * from table_name1;
SQL> drop table_name1;
SQL> rename table_name2 to table_name1;
SQL> -- Remember to recreate all indexes, constraints, triggers, etc
on table...
Method 3: (thanks to Dennis Gurnick)
SQL> delete from my_table t1
SQL> where exists (select 'x' from my_table t2
SQL> where t2.key_value1 = t1.key_value1
SQL> and t2.key_value2 = t1.key_value2
SQL> and t2.rowid > t1.rowid);
Note: One can eliminate N^2 unnecessary operations by creating an index on the joined
fields in the inner loop (no need to loop through the entire table on each pass by a
record). This will speed-up the deletion process.

Note 2: If you are comparing NOT-NULL columns, use the NVL function. Remember
that NULL is not equal to NULL. This should not be a problem as all key columns should
be NOT NULL by definition.

Back to top of file

How does one generate primary key values for a table?

Create your table with a NOT NULL column (say SEQNO). This column can now be
populated with unique values:
SQL> UPDATE table_name SET seqno = ROWNUM;

or use a sequences generator:

SQL> CREATE SEQUENCE sequence_name START WITH 1 INCREMENT BY 1;


SQL> UPDATE table_name SET seqno = sequence_name.NEXTVAL;
Finally, create a unique index on this column.
Back to top of file
How does one get the time difference between two date columns?

Look at this example query:


select floor(((date1-date2)*24*60*60)/3600)
|| ' HOURS ' ||
floor((((date1-date2)*24*60*60) -
floor(((date1-date2)*24*60*60)/3600)*3600)/60)
|| ' MINUTES ' ||
round((((date1-date2)*24*60*60) -
floor(((date1-date2)*24*60*60)/3600)*3600 -
(floor((((date1-date2)*24*60*60) -
floor(((date1-date2)*24*60*60)/3600)*3600)/60)*60)))
|| ' SECS ' time_difference
from ...

If you don't want to go through the floor and ceiling math, try this method (contributed by
Erik Wile):

select to_char(to_date('00:00:00','HH24:MI:SS') +
(date1 - date2), 'HH24:MI:SS') time_difference
from ...

Note that this query only uses the time portion of the date and ignores the date itself. It
will thus never return a value bigger than 23:59:59.

Back to top of file

How does one add a day/hour/minute/second to a date value?

The SYSDATE pseudo-column shows the current system date and time. Adding 1 to
SYSDATE will advance the date by 1 day. Use fractions to add hours, minutes or
seconds to the date. Look at these examples:
SQL> select sysdate, sysdate+1/24, sysdate +1/1440, sysdate +
1/86400 from dual;

SYSDATE SYSDATE+1/24 SYSDATE+1/1440


SYSDATE+1/86400
-------------------- -------------------- --------------------
--------------------
03-Jul-2002 08:32:12 03-Jul-2002 09:32:12 03-Jul-2002 08:33:12
03-Jul-2002 08:32:13
The following format is frequently used with Oracle Replication:
select sysdate NOW, sysdate+30/(24*60*60) NOW_PLUS_30_SECS from
dual;

NOW NOW_PLUS_30_SECS
-------------------- --------------------
03-JUL-2002 16:47:23 03-JUL-2002 16:47:53
Here are a couple of examples:
Description Date Expression
Now SYSDATE
Tomorow/ next day SYSDATE + 1
Seven days from now SYSDATE + 7
One hour from now SYSDATE + 1/24
Three hours from now SYSDATE + 3/24
An half hour from now SYSDATE + 1/48
10 minutes from now SYSDATE + 10/1440
30 seconds from now SYSDATE + 30/86400
Tomorrow at 12 midnight TRUNC(SYSDATE + 1)
Tomorrow at 8 AM TRUNC(SYSDATE + 1) + 8/24
Next Monday at 12:00 noon NEXT_DAY(TRUNC(SYSDATE), 'MONDAY') + 12/24
First day of next month at 12
TRUNC(LAST_DAY(SYSDATE ) + 1)
midnight
First day of the current month TRUNC(LAST_DAY(ADD_MONTHS(SYSDATE,-1))) + 1
The next Monday, Wednesday TRUNC(LEAST(NEXT_DAY(sysdate,''MONDAY'' ),NEXT_DAY(sysdate,''WEDNESDAY''),
or Friday at 9 a.m NEXT_DAY(sysdate,''FRIDAY'' ))) + (9/24)

Back to top of file

How does one count different data values in a column?

Use this simple query to count the number of data values in a column:
select my_table_column, count(*)
from my_table
group by my_table_column;

A more sophisticated example...


select dept, sum( decode(sex,'M',1,0)) MALE,
sum( decode(sex,'F',1,0)) FEMALE,
count(decode(sex,'M',1,'F',1)) TOTAL
from my_emp_table
group by dept;
Back to top of file

How does one count/sum RANGES of data values in a column?

A value x will be between values y and z if GREATEST(x, y) = LEAST(x, z). Look at


this example:
select f2,
sum(decode(greatest(f1,59), least(f1,100), 1, 0)) "Range
60-100",
sum(decode(greatest(f1,30), least(f1, 59), 1, 0)) "Range
30-59",
sum(decode(greatest(f1, 0), least(f1, 29), 1, 0)) "Range
00-29"
from my_table
group by f2;
For equal size ranges it might be easier to calculate it with
DECODE(TRUNC(value/range), 0, rate_0, 1, rate_1, ...). Eg.
select ename "Name", sal "Salary",
decode( trunc(f2/1000, 0), 0, 0.0,
1, 0.1,
2, 0.2,
3, 0.31) "Tax rate"
from my_table;
Back to top of file

Can one retrieve only the Nth row from a table?

Rupak Mohan provided this solution to select the Nth row from a table:
SELECT * FROM t1 a
WHERE n = (SELECT COUNT(rowid)
FROM t1 b
WHERE a.rowid >= b.rowid);

Shaik Khaleel provided this solution:

SELECT * FROM (
SELECT ENAME,ROWNUM RN FROM EMP WHERE ROWNUM < 101 )
WHERE RN = 100;
Note: In this first query we select one more than the required row number, then we select
the required one. Its far better than using a MINUS operation.

Ravi Pachalla provided these solutions:

SELECT f1 FROM t1
WHERE rowid = (
SELECT rowid FROM t1
WHERE rownum <= 10
MINUS
SELECT rowid FROM t1
WHERE rownum < 10);
SELECT rownum,empno FROM scott.emp a
GROUP BY rownum,empno HAVING rownum = 4;

Alternatively...
SELECT * FROM emp WHERE rownum=1 AND rowid NOT IN
(SELECT rowid FROM emp WHERE rownum < 10);
Please note, there is no explicit row order in a relational database. However, this query is
quite fun and may even help in the odd situation.
Back to top of file
Can one retrieve only rows X to Y from a table?

Shaik Khaleel provided this solution to the problem:


SELECT * FROM (
SELECT ename, rownum rn
FROM emp WHERE rownum < 101
) WHERE RN between 91 and 100 ;
Note: the 101 is just one greater than the maximum row of the required rows (means x=
90, y=100, so the inner values is y+1).

Ravi Pachalla provided this solution:

SELECT rownum, f1 FROM t1


GROUP BY rownum, f1 HAVING rownum BETWEEN 2 AND 4;

Another solution is to use the MINUS operation. For example, to display rows 5 to 7,
construct a query like this:
SELECT *
FROM tableX
WHERE rowid in (
SELECT rowid FROM tableX
WHERE rownum <= 7
MINUS
SELECT rowid FROM tableX
WHERE rownum < 5);
Youssef Youssef provided this soluton: "this one was faster for me and allowed for
sorting before filtering by rownum. The inner query (table A) can be a series of tables
joined together with any operation before the filtering by rownum is applied."

SELECT *
FROM (SELECT a.*, rownum RN
FROM (SELECT *
FROM t1 ORDER BY key_column) a
WHERE rownum <=7)
WHERE rn >=5
Please note, there is no explicit row order in a relational database. However, this query is
quite fun and may even help in the odd situation.
Back to top of file

How does one select EVERY Nth row from a table?

One can easily select all even, odd, or Nth rows from a table using SQL queries like this:

Method 1: Using a subquery

SELECT *
FROM emp
WHERE (ROWID,0) IN (SELECT ROWID, MOD(ROWNUM,4)
FROM emp);
Method 2: Use dynamic views (available from Oracle7.2):
SELECT *
FROM ( SELECT rownum rn, empno, ename
FROM emp
) temp
WHERE MOD(temp.ROWNUM,4) = 0;
Method 3: Using GROUP BY and HAVING - provided by Ravi Pachalla
SELECT rownum, f1
FROM t1
GROUP BY rownum, f1 HAVING MOD(rownum,n) = 0 OR rownum = 2-n
Please note, there is no explicit row order in a relational database. However, these queries
are quite fun and may even help in the odd situation.
Back to top of file

How does one select the TOP N rows from a table?

Form Oracle8i one can have an inner-query with an ORDER BY clause. Look at this
example:
SELECT *
FROM (SELECT * FROM my_table ORDER BY col_name_1 DESC)
WHERE ROWNUM < 10;
Use this workaround with prior releases:
SELECT *
FROM my_table a
WHERE 10 >= (SELECT COUNT(DISTINCT maxcol)
FROM my_table b
WHERE b.maxcol >= a.maxcol)
ORDER BY maxcol DESC;
Back to top of file

How does one code a tree-structured query?

Tree-structured queries are definitely non-relational (enough to kill Codd and make him
roll in his grave). Also, this feature is not often found in other database offerings.

The SCOTT/TIGER database schema contains a table EMP with a self-referencing


relation (EMPNO and MGR columns). This table is perfect for tesing and demonstrating
tree-structured queries as the MGR column contains the employee number of the
"current" employee's boss.

The LEVEL pseudo-column is an indication of how deep in the tree one is. Oracle can
handle queries with a depth of up to 255 levels. Look at this example:

select LEVEL, EMPNO, ENAME, MGR


from EMP
connect by prior EMPNO = MGR
start with MGR is NULL;
One can produce an indented report by using the level number to substring or lpad() a
series of spaces, and concatenate that to the string. Look at this example:
select lpad(' ', LEVEL * 2) || ENAME ........
One uses the "start with" clause to specify the start of the tree. More than one record can
match the starting condition. One disadvantage of having a "connect by prior" clause is
that you cannot perform a join to other tables. The "connect by prior" clause is rarely
implemented in the other database offerings. Trying to do this programmatically is
difficult as one has to do the top level query first, then, for each of the records open a
cursor to look for child nodes.

One way of working around this is to use PL/SQL, open the driving cursor with the
"connect by prior" statement, and the select matching records from other tables on a row-
by-row basis, inserting the results into a temporary table for later retrieval.

Back to top of file

How does one code a matrix report in SQL?

Look at this example query with sample output:


SELECT *
FROM (SELECT job,
sum(decode(deptno,10,sal)) DEPT10,
sum(decode(deptno,20,sal)) DEPT20,
sum(decode(deptno,30,sal)) DEPT30,
sum(decode(deptno,40,sal)) DEPT40
FROM scott.emp
GROUP BY job)
ORDER BY 1;

JOB DEPT10 DEPT20 DEPT30 DEPT40


--------- ---------- ---------- ---------- ----------
ANALYST 6000
CLERK 1300 1900 950
MANAGER 2450 2975 2850
PRESIDENT 5000
SALESMAN 5600
Back to top of file

How does one implement IF-THEN-ELSE in a select statement?

The Oracle decode function acts like a procedural statement inside an SQL statement to
return different values or columns based on the values of other columns in the select
statement.

Some examples:
select decode(sex, 'M', 'Male', 'F', 'Female', 'Unknown')
from employees;

select a, b, decode( abs(a-b), a-b, 'a > b',


0, 'a = b',
'a < b') from tableX;

select decode( GREATEST(A,B), A, 'A is greater OR EQUAL than B',


'B is greater than A')...

select decode( GREATEST(A,B),


A, decode(A, B, 'A NOT GREATER THAN B', 'A GREATER
THAN B'),
'A NOT GREATER THAN B')...
Note: The decode function is not ANSI SQL and is rarely implemented in other RDBMS
offerings. It is one of the good things about Oracle, but use it sparingly if portability is
required.

From Oracle 8i one can also use CASE statements in SQL. Look at this example:

SELECT ename, CASE WHEN sal>1000 THEN 'Over paid' ELSE 'Under
paid' END
FROM emp;
Back to top of file

How can one dump/ examine the exact content of a database column?
SELECT DUMP(col1)
FROM tab1
WHERE cond1 = val1;

DUMP(COL1)
-------------------------------------
Typ=96 Len=4: 65,66,67,32
For this example the type is 96, indicating CHAR, and the last byte in the column is 32,
which is the ASCII code for a space. This tells us that this column is blank-padded.
Back to top of file

Can one drop a column from a table?

From Oracle8i one can DROP a column from a table. Look at this sample script,
demonstrating the ALTER TABLE table_name DROP COLUMN column_name;
command.

Other workarounds:
1. SQL> update t1 set column_to_drop = NULL;
SQL> rename t1 to t1_base;
SQL> create view t1 as select <specific columns> from t1_base;

2. SQL> create table t2 as select <specific columns> from t1;


SQL> drop table t1;
SQL> rename t2 to t1;
Back to top of file

Can one rename a column in a table?

From Oracle9i one can RENAME a column from a table. Look at this example:
ALTER TABLE tablename RENAME COLUMN oldcolumn TO newcolumn;

Other workarounds:

1. -- Use a view with correct column names...


rename t1 to t1_base;
create view t1 <column list with new name> as select * from t1_base;

2. -- Recreate the table with correct column names...


create table t2 <column list with new name> as select * from t1;
drop table t1;
rename t2 to t1;

3. -- Add a column with a new name and drop an old column...


alter table t1 add ( newcolame datatype );
update t1 set newcolname=oldcolname;
alter table t1 drop column oldcolname;
Back to top of file

How can I change my Oracle password?

Issue the following SQL command: ALTER USER <username> IDENTIFIED BY


<new_password>
/

From Oracle8 you can just type "password" from SQL*Plus, or if you need to change
another user's password, type "password user_name".

Back to top of file

How does one find the next value of a sequence?

Perform an "ALTER SEQUENCE ... NOCACHE" to unload the unused cached sequence
numbers from the Oracle library cache. This way, no cached numbers will be lost. If you
then select from the USER_SEQUENCES dictionary view, you will see the correct high
water mark value that would be returned for the next NEXTVALL call. Afterwards,
perform an "ALTER SEQUENCE ... CACHE" to restore caching.

You can use the above technique to prevent sequence number loss before a SHUTDOWN
ABORT, or any other operation that would cause gaps in sequence values.

Back to top of file

Workaround for snapshots on tables with LONG columns

You can use the SQL*Plus COPY command instead of snapshots if you need to copy
LONG and LONG RAW variables from one location to another. Eg:
COPY TO SCOTT/TIGER@REMOTE -
CREATE IMAGE_TABLE USING -
SELECT IMAGE_NO, IMAGE -
FROM IMAGES;
Note: If you run Oracle8, convert your LONGs to LOBs, as it can be replicated.
Back to top of file

o
o Sample SQL matrix report

SELECT job,
sum(decode(deptno,10,sal)) DEPT10,
sum(decode(deptno,20,sal)) DEPT20,
sum(decode(deptno,30,sal)) DEPT30,
sum(decode(deptno,40,sal)) DEPT40
FROM scott.emp
GROUP BY job
/

-- Sample output:
--
-- JOB DEPT10 DEPT20 DEPT30 DEPT40
-- --------- ---------- ---------- ---------- ----------
-- ANALYST 6000
-- CLERK 1300 1900 950
-- MANAGER 2450 2975 2850
-- PRESIDENT 5000
-- SALESMAN 5600
--
o
o Lookup Oracle error messages

set serveroutput on
set veri off feed off

prompt Lookup Oracle error messages:


prompt
prompt Please enter error numbers as negatives. E.g. -1
prompt

exec dbms_output.put_line('==> '||sqlerrm( &errno ) );

set veri on feed on


undef errno
o
o Display Database version, installed options and port string

set head off feed off pages 0 serveroutput on

col banner format a72 wrap

select banner
from sys.v_$version;

select ' With the '||parameter||' option'


from sys.v_$option
where value = 'TRUE';

select ' The '||parameter||' option is not installed'


from sys.v_$option
where value <> 'TRUE';

begin
dbms_output.put_line('Port String: '||dbms_utility.port_string);
end;
/

set head on feed on


o
o "Who am I" script

set termout off


store set store rep
set head off
set pause off
set termout on

select 'User: '|| user || ' on database ' || global_name,


' (term='||USERENV('TERMINAL')||
', audsid='||USERENV('SESSIONID')||')' as MYCONTEXT
from global_name;

@store
set termout on
o
o Select the Nth highest value from a table

select level, max('col_name') from my_table


where level = '&n'
connect by prior ('col_name') > 'col_name')
group by level;

-- Example :
--
-- Given a table called emp with the following columns:
-- id number
-- name varchar2(20)
-- sal number
--
-- For the second highest salary:
--
select level, max(sal) from emp
where level=2
connect by prior sal > sal
group by level
--
o
o Select the Nth lowest value from a table

select level, min('col_name') from my_table


where level = '&n'
connect by prior ('col_name') < 'col_name')
group by level;

-- Example:
--
-- Given a table called emp with the following columns:
-- id number
-- name varchar2(20)
-- sal number
--
-- For the second lowest salary:
--
-- select level, min(sal) from emp
-- where level=2
-- connect by prior sal < sal
-- group by level
--
o
o Demonstrate default column values

drop table x
-- /

create table x (a char, b number default 99999, c date, d varchar2(6))


/

alter table x modify (c date default sysdate)


/

insert into x(a, d) values ('a', 'qwerty')


/

select * from x
/

--
-- Expected output:
--
-- A B C D
-- - ---------- ----------- ------
-- a 99999 25-APR-2001 qwerty
-
o
o Display table and column comments

set pages 50000


set null 'No Comments'

tti 'Table Comments'


col comments format a29 wrap word

select * from user_tab_comments;

tti 'Column Comments'


col comments format a18 wrap word
break on table_name skip 1
select * from user_col_comments;
clear break

set null ''


set pages 23
o
o Pass application info through to the Oracle RDBMS

The following code tells the database what the application is up to:

begin
dbms_application_info.set_client_info('BANCS application info');
dbms_application_info.set_module('BANCS XYZ module', 'BANCS action
name');
end;
/

-- Retrieve application info from the database:

select module, action, client_info


from sys.v_$session where audsid = USERENV('SESSIONID')
/

select sql_text
from sys.v_$sqlarea
where module = 'BANCS XYZ module'
and action = 'BANCS action name'
/
o
o SQL*Plus Help script
select info
from system.help
where upper(topic)=upper('&1')
o
o Test for Leap Years

select year,
decode( mod(year, 4), 0,
decode( mod(year, 400), 0, 'Leap Year',
decode( mod(year, 100), 0, 'Not a Leap Year', 'Leap Year')
), 'Not a Leap Year'
) as leap_year_indicator
from my_table

o Spell out numbers to words

select decode( sign( &num ), -1, 'Negative ', 0, 'Zero', NULL ) ||


decode( sign( abs(&num) ), +1,
to_char( to_date( abs(&num),'J'),'Jsp') )
from dual
o
o Demonstrate simple encoding and decoding of messages

SELECT TRANSLATE(
'HELLO WORLD', -- Message to encode
'ABCDEFGHIJKLMNOPQRSTUVWXYZ ',
'1234567890!@#$%^&*()-=_+;,.') ENCODED_MESSAGE
FROM DUAL
/

SELECT TRANSLATE(
'85@@%._%*@4', -- Message to decode
'1234567890!@#$%^&*()-=_+;,.',
'ABCDEFGHIJKLMNOPQRSTUVWXYZ ') DECODED_MESSAGE
FROM DUAL
o
o Count the number of rows for ALL tables in current schema

set termout off echo off feed off trimspool on head off pages 0

spool countall.tmp
select 'SELECT count(*), '''||table_name||''' from '||table_name||';'
from user_tables
/
spool off

set termout on
@@countall.tmp
set head on feed on

o
o Demonstrate Oracle database types and object tables

drop type employee_typ;

create type employee_typ as object (


empno NUMBER,
emp_name varchar2(30),
hiredate date,
member function days_at_company return NUMBER,
pragma restrict_references(days_at_company, WNDS)
)
/

create type body employee_tye is


begin
member function days_at_company return number is
begin
return (SYSDATE-hiredate);
end;
end;
o
o Demonstrate VARRAY database types

CREATE OR REPLACE TYPE vcarray AS VARRAY(10) OF VARCHAR2(128);


/
CREATE TABLE varray_table (id number, col1 vcarray);

INSERT INTO varray_table VALUES (1, vcarray('A'));


INSERT INTO varray_table VALUES (2, vcarray('B', 'C'));
INSERT INTO varray_table VALUES (3, vcarray('D', 'E', 'F'));

SELECT * FROM varray_table;


SELECT * FROM USER_VARRAYS;
-- SELECT * FROM USER_SEGMENTS;

-- Unnesting the collection:


select t1.id, t2.COLUMN_VALUE
from varray_table t1, TABLE(t1.col1) t2
/

-- Use PL/SQL to access the varray...


set serveroutput on
declare
v_vcarray vcarray;
begin
for c1 in (select * from varray_table) loop
dbms_output.put_line('Row fetched...');
FOR i IN c1.col1.FIRST..c1.col1.LAST LOOP
dbms_output.put_line('...property fetched: '|| c1.col1(i));
END LOOP;
end loop;
end;
/

-- Clean-up...
DROP TABLE varray_table;
o DROP TYPE vcarray
o Demonstrate Oracle temporary tables

drop table x
/

create global temporary table x (a date)


on commit delete rows -- Delete rows after commit
-- on commit preserve rows -- Delete rows after exit session
/

select table_name, temporary, duration


from user_tables
where table_name = 'X'
/

insert into x values (sysdate);

select * from x;

commit;

-- Inserted rows are missing after commit


select * from x;
o
o Convert LONG data types to LOBs

create table old_long_table(c1 number, c2 long);


insert into old_long_table values (1, 'LONG data to convert to CLOB');

create table new_lob_table(c1 number, c2 clob);

-- Use TO_LOB function to convert LONG to LOB...


insert into new_lob_table
select c1, to_lob(c2) from old_long_table;

-- Note: the same procdure can be used to convert LONG RAW datatypes to
BLOBs.
o
o Delete duplicate values from a table

DELETE FROM my_table


WHERE ROWID NOT IN (SELECT MIN(ROWID)
FROM my_table
GROUP BY delete_col_name);

-- Example :
--
-- Given a table called emp with the following columns:
-- id number
-- name varchar2(20)
-- sal number
--
-- To delete the duplicate values:
--
-- DELETE FROM emp
-- WHERE ROWID NOT IN (SELECT MIN(ROWID) FROM emp GROUP BY id);
--
-- COMMIT;
--
o

General PL/SQL: Scripts

o Update/ delete from a huge table with intermittent commits

loop
update tab1 set col1 = 'value2'
where rowid = c1.rowid;

i := i + 1; -- Commit after every X records


if i > 10000 then
commit;
i := 0;
end if;

end loop;
commit;
end;
/

-- Note: More advanced users can use the mod() function to commit every
N rows.
-- No counter variable required:
--
-- if mod(i, 10000)
-- commit;
-- dbms_output.put_line('Commit issued for rows up to: '||
c1%rowcount);
-- end if;
--
o
o Simple program to demonstrate BULK COLLECT and BULK BIND operations

set serveroutput on size 50000

DECLARE
CURSOR emp_cur IS SELECT * FROM EMP;

TYPE emp_tab_t IS TABLE OF emp%ROWTYPE INDEX BY BINARY_INTEGER;


emp_tab emp_tab_t; -- In-memory table
rows NATURAL := 10000; -- Number of rows to process at a time
i BINARY_INTEGER := 0;
BEGIN
OPEN emp_cur;
LOOP
-- Bulk collect data into memory table - X rows at a time
FETCH emp_cur BULK COLLECT INTO emp_tab LIMIT rows;
EXIT WHEN emp_tab.COUNT = 0;

DBMS_OUTPUT.PUT_LINE( TO_CHAR(emp_tab.COUNT)|| ' rows bulk


fetched.');

FOR i IN emp_tab.FIRST .. emp_tab.LAST loop


-- Manipumate data in the memory table...
dbms_output.put_line('i = '||i||', EmpName='||emp_tab(i).ename);
END LOOP;

-- Bulk bind of data in memory table...


FORALL i in emp_tab.FIRST..emp_tab.LAST
INSERT /*+APPEND*/ INTO emp2 VALUES emp_tab(i);

END LOOP;
CLOSE emp_cur;
END;
/
o
o Profile PL/SQL code for execution statistics

conn / as sysdba

-- Install the profiler...


@?/rdbms/admin/proftab
@?/rdbms/admin/profload
@?/plsql/demo/profrep.sql

-- Create a test procedure to time...


CREATE OR REPLACE PROCEDURE proc1 IS
v_dummy CHAR;
BEGIN
FOR i IN 1..100 LOOP
SELECT dummy INTO v_dummy FROM dual;
END LOOP;
END;
/
SHOW ERRORS

-- Do the profilling and print the report...


set line 5000 serveroutput on size 1000000
DECLARE
v_run NUMBER;
BEGIN
DBMS_PROFILER.START_PROFILER('test','test1',v_run);
proc1;
DBMS_PROFILER.STOP_PROFILER;
DBMS_PROFILER.ROLLUP_RUN(v_run);
PROF_REPORT_UTILITIES.PRINT_RUN(v_run);
END;
/
o
o
o
o
o Select records from a cursor into PL/SQL table

set serveroutput on

declare
-- Declare the PL/SQL table
type deptarr is table of dept%rowtype
index by binary_integer;
d_arr deptarr;

-- Declare cursor
type d_cur is ref cursor return dept%rowtype;
c1 d_cur;

i number := 1;
begin
-- Populate the PL/SQL table from the cursor
open c1 for select * from dept;
loop
exit when c1%NOTFOUND;
fetch c1 into d_arr(i);
i := i+1;
end loop;
close c1;

-- Display the entire PL/SQL table on screen


for i in 1..d_arr.last loop
dbms_output.put_line('DEPTNO : '||d_arr(i).deptno );
dbms_output.put_line('DNAME : '||d_arr(i).dname );
dbms_output.put_line('LOC : '||d_arr(i).loc );
dbms_output.put_line('---------------------------');
end loop;
end;
/
o
o Password encrypt/decrypt using DBMS Obfuscation Toolkit

CREATE OR REPLACE PACKAGE PASSWORD AS


function encrypt(i_password varchar2) return varchar2;
function decrypt(i_password varchar2) return varchar2;
END PASSWORD;
/
show errors

CREATE OR REPLACE PACKAGE BODY PASSWORD AS


-- key must be exactly 8 bytes long
c_encrypt_key varchar2(8) := 'key45678';

function encrypt (i_password varchar2) return varchar2 is


v_encrypted_val varchar2(38);
v_data varchar2(38);
begin
-- Input data must have a length divisible by eight
v_data := RPAD(i_password,
(TRUNC(LENGTH(i_password)/8)+1)*8,CHR(0));

DBMS_OBFUSCATION_TOOLKIT.DESENCRYPT(
input_string => v_data,
key_string => c_encrypt_key,
encrypted_string => v_encrypted_val);
return v_encrypted_val;
end encrypt;

function decrypt (i_password varchar2) return varchar2 is


v_decrypted_val varchar2(38);
begin
DBMS_OBFUSCATION_TOOLKIT.DESDECRYPT(
input_string => i_password,
key_string => c_encrypt_key,
decrypted_string => v_decrypted_val);
return v_decrypted_val;
end decrypt;

end PASSWORD;
/
show errors

-- Test if it is working...
select password.encrypt('PASSWORD1') from dual;
select password.decrypt(app_password.encrypt('PASSWORD1')) from dual;
select password.encrypt('PSW2') from dual;
select password.decrypt(app_password.encrypt('PSW2')) from dual;

o
o Pass result sets (REF CURSOR) between procedures and functions

set serveroutput on

-- Define TYPES package separately to be available to all programming


-- environments...
CREATE OR REPLACE PACKAGE types AS
TYPE cursortyp is REF CURSOR; -- use weak form
END;
/

-- Create test package to demonstrate passing result sets...


CREATE OR REPLACE PACKAGE test_ref_cursor AS
PROCEDURE main;
FUNCTION get_cursor_ref(typ NUMBER) RETURN types.cursortyp;
PROCEDURE process_cursor(cur types.cursortyp);
END;
/
show errors

CREATE OR REPLACE PACKAGE BODY test_ref_cursor AS

-- Main program entry point


PROCEDURE main IS
BEGIN
process_cursor( get_cursor_ref(1) );
process_cursor( get_cursor_ref(2) );
END;

-- Get and return a CURSOR REF/ Result Set


FUNCTION get_cursor_ref(typ NUMBER) RETURN types.cursortyp IS
cur types.cursortyp;
BEGIN
if typ = 1 THEN
OPEN cur FOR SELECT * FROM emp WHERE ROWNUM < 5;
ELSE
OPEN cur FOR SELECT * FROM dept WHERE ROWNUM < 5;
END IF;
RETURN cur;
END;

-- Process rows for an EMP or DEPT cursor


PROCEDURE process_cursor(cur types.cursortyp) IS
empRec emp%ROWTYPE;
deptRec dept%ROWTYPE;
BEGIN
LOOP
FETCH cur INTO empRec; -- Maybe it was an EMP cursor, try to
fetch...
EXIT WHEN cur%NOTFOUND;
dbms_output.put_line('EMP ROW: '||empRec.ename);
END LOOP;
EXCEPTION
WHEN ROWTYPE_MISMATCH THEN -- OK, so it was't EMP, let's try DEPT.
LOOP
FETCH cur INTO deptRec;
EXIT WHEN cur%NOTFOUND;
dbms_output.put_line('DEPT ROW: '||deptRec.dname);
END LOOP;
END;

END;
/
show errors

EXEC test_ref_cursor.main;

o
o Convert between different numbering systems (binary, octal, decimal and hex)
set serveroutput on

CREATE OR REPLACE PACKAGE dbms_numsystem AS


function bin2dec (binval in char ) RETURN number;
function dec2bin (N in number) RETURN varchar2;
function oct2dec (octval in char ) RETURN number;
function dec2oct (N in number) RETURN varchar2;
function hex2dec (hexval in char ) RETURN number;
function dec2hex (N in number) RETURN varchar2;
END dbms_numsystem;
/
show errors

CREATE OR REPLACE PACKAGE BODY dbms_numsystem AS

FUNCTION bin2dec (binval in char) RETURN number IS


i number;
digits number;
result number := 0;
current_digit char(1);
current_digit_dec number;
BEGIN
digits := length(binval);
for i in 1..digits loop
current_digit := SUBSTR(binval, i, 1);
current_digit_dec := to_number(current_digit);
result := (result * 2) + current_digit_dec;
end loop;
return result;
END bin2dec;

FUNCTION dec2bin (N in number) RETURN varchar2 IS


binval varchar2(64);
N2 number := N;
BEGIN
while ( N2 > 0 ) loop
binval := mod(N2, 2) || binval;
N2 := trunc( N2 / 2 );
end loop;
return binval;
END dec2bin;

FUNCTION oct2dec (octval in char) RETURN number IS


i number;
digits number;
result number := 0;
current_digit char(1);
current_digit_dec number;
BEGIN
digits := length(octval);
for i in 1..digits loop
current_digit := SUBSTR(octval, i, 1);
current_digit_dec := to_number(current_digit);
result := (result * 8) + current_digit_dec;
end loop;
return result;
END oct2dec;
FUNCTION dec2oct (N in number) RETURN varchar2 IS
octval varchar2(64);
N2 number := N;
BEGIN
while ( N2 > 0 ) loop
octval := mod(N2, 8) || octval;
N2 := trunc( N2 / 8 );
end loop;
return octval;
END dec2oct;

FUNCTION hex2dec (hexval in char) RETURN number IS


i number;
digits number;
result number := 0;
current_digit char(1);
current_digit_dec number;
BEGIN
digits := length(hexval);
for i in 1..digits loop
current_digit := SUBSTR(hexval, i, 1);
if current_digit in ('A','B','C','D','E','F') then
current_digit_dec := ascii(current_digit) - ascii('A') + 10;
else
current_digit_dec := to_number(current_digit);
end if;
result := (result * 16) + current_digit_dec;
end loop;
return result;
END hex2dec;

FUNCTION dec2hex (N in number) RETURN varchar2 IS


hexval varchar2(64);
N2 number := N;
digit number;
hexdigit char;
BEGIN
while ( N2 > 0 ) loop
digit := mod(N2, 16);
if digit > 9 then
hexdigit := chr(ascii('A') + digit - 10);
else
hexdigit := to_char(digit);
end if;
hexval := hexdigit || hexval;
N2 := trunc( N2 / 16 );
end loop;
return hexval;
END dec2hex;

END dbms_numsystem;
/
show errors

-- Examples:
select dbms_numsystem.dec2bin(22) from dual;
select dbms_numsystem.bin2dec('10110') from dual;
select dbms_numsystem.dec2oct(44978) from dual;
select dbms_numsystem.oct2dec(127662) from dual;
select dbms_numsystem.dec2hex(44978) from dual;
select dbms_numsystem.hex2dec('AFB2') from dual;

o
o Random number/ string generator package

create or replace package random


is
procedure srand(new_seed in number);
procedure get_rand(r OUT number);
procedure get_rand_max(r OUT number, n IN number);
function rand return number;
function rand_max(n IN number) return number;
function rand_string(ssiz IN number) return varchar2;
function smaller(x IN number, y IN number) return number;
pragma restrict_references(rand, WNDS);
pragma restrict_references(rand_max, WNDS);
pragma restrict_references(random, WNDS, RNPS);
pragma restrict_references(rand_string, WNDS);
pragma restrict_references(smaller, WNDS);
end random;
/

create or replace package body random


is
multiplier constant number := 22695477;
increment constant number := 1;
"2^32" constant number := 2 ** 32;
"2^16" constant number := 2 ** 16;
"0x7fff" constant number := 32767;
Seed number := 1;

function smaller(x IN number, y IN number) return number is


begin
if x <= y then
return x;
else
return y;
end if;
end smaller;

function rand_string(ssiz IN number) return varchar2 is


i number;
m number;
c char;
result varchar2(2000) := '';
begin
m := smaller(ssiz,2000);
for i in 1..m loop
c :=
substr('abcdefghijklmnopqrstuvwxyz0123456789',rand_max(36),1);
result := result || c;
end loop;
return result;
end rand_string;

procedure srand(new_seed in number) is


begin
Seed := new_seed;
end srand;

function rand return number is


begin
Seed := mod(multiplier * Seed + increment, "2^32");
return bitand(Seed/"2^16", "0x7fff");
end rand;

procedure get_rand(r OUT number) is


begin
r := rand;
end get_rand;

function rand_max(n IN number) return number is


begin
return mod(rand, n) + 1;
end rand_max;

procedure get_rand_max(r OUT number, n IN number) is


begin
r := rand_max(n);
end get_rand_max;

begin
select userenv('SESSIONID')
into Seed
from dual;
end random;
/

-- Some examples:
select random.rand_max(10) from dual;
select random.rand_max(10) from dual;
select random.rand_string(20) from dual;
select random.rand_string(20) from dual;

o
o Function to test for Leap Years

CREATE OR REPLACE FUNCTION isLeapYear(i_year NUMBER) RETURN boolean AS


BEGIN
-- A year is a leap year if it is evenly divisible by 4
-- but not if it's evenly divisible by 100
-- unless it's also evenly divisible by 400

IF mod(i_year, 400) = 0 OR ( mod(i_year, 4) = 0 AND mod(i_year,


100) != 0) THEN
return TRUE;
ELSE
return FALSE;
END IF;
END;
/
show errors

-- Let's test it
SET SERVEROUTPUT ON
BEGIN
IF isLeapYear(2004) THEN
dbms_output.put_line('Yes, it is a leap year');
ELSE
dbms_output.put_line('No, it is not a leap year');
END IF;
END;
/

o
o Print the ASCII table

set serveroutput on size 10240

declare
i number;
j number;
k number;
begin
for i in 2..15 loop
for j in 1..16 loop
k:=i*16+j;
dbms_output.put((to_char(k,'000'))||':'||chr(k)||' ');
if k mod 8 = 0 then
dbms_output.put_line('');
end if;
end loop;
end loop;
end;
/
show errors
o
o Recursive algorithms to calculate Fibonacci and Factorials

Computing the Factorial of a number (n!)


CREATE OR REPLACE FUNCTION fac (n POSITIVE) RETURN INTEGER IS
BEGIN
IF n = 1 THEN -- terminating condition
RETURN 1;
ELSE
RETURN n * fac(n - 1); -- recursive call
END IF;
END fac;
/

-- Test n!
SELECT fac(1), fac(2), fac(3), fac(4), fac(5) FROM dual;
-- Sample output:
-- FAC(1) FAC(2) FAC(3) FAC(4) FAC(5)
-- ---------- ---------- ---------- ---------- ----------
-- 1 2 6 24 120

-- Computing the Nth Fibonacci number


CREATE OR REPLACE FUNCTION fib (n POSITIVE) RETURN INTEGER IS
BEGIN
IF (n = 1) OR (n = 2) THEN -- terminating condition
RETURN 1;
ELSE
RETURN fib(n - 1) + fib(n - 2); -- recursive call
END IF;
END fib;
/

-- Test Fibonacci Series:


SELECT fib(1), fib(2), fib(3), fib(4), fib(5) FROM dual;

-- Sample output:
-- FIB(1) FIB(2) FIB(3) FIB(4) FIB(5)
-- ---------- ---------- ---------- ---------- ----------
-- 1 1 2 3 5
--

o
o Fetch LOB column values piece-wise from PL/SQL

set serveroutput on

DROP TABLE lob_table; -- Create table to hols LOBs


CREATE TABLE lob_table (
id INTEGER,
b_lob BLOB,
c_lob CLOB,
b_file BFILE );

INSERT INTO lob_table -- Create sample record


VALUES (1, EMPTY_BLOB(), 'abcde', NULL);

DECLARE
clob_locator CLOB;
charbuf VARCHAR2(20);
read_offset INTEGER;
read_amount INTEGER;
BEGIN
-- First we need to get the lob locator
SELECT c_lob INTO clob_locator FROM lob_table WHERE id = 1;

DBMS_OUTPUT.PUT_LINE('CLOB Size: ' ||


DBMS_LOB.GETLENGTH(clob_locator));

-- Read LOB field contents


read_offset := 1;
read_amount := 20;
dbms_lob.read(clob_locator, read_amount, read_offset, charbuf);
dbms_output.put_line('CLOB Value: ' || charbuf);
END;
/
o
o Upload and save binary files (like pictures, documents, etc) to/from the DB

set serveroutput on
DROP TABLE lob_table;
DROP SEQUENCE lob_seq;

CREATE OR REPLACE DIRECTORY my_dir AS '/app/oracle/';

CREATE TABLE lob_table (id NUMBER, fil BLOB);


CREATE SEQUENCE lob_seq;

CREATE OR REPLACE PROCEDURE load_file(p_file VARCHAR2)


IS
src_lob BFILE := BFILENAME('MY_DIR', p_file);
dest_lob BLOB;
BEGIN
INSERT INTO lob_table VALUES(lob_seq.nextval, EMPTY_BLOB())
RETURNING fil INTO dest_lob;

DBMS_LOB.OPEN(src_lob, DBMS_LOB.LOB_READONLY);
DBMS_LOB.LoadFromFile( DEST_LOB => dest_lob,
SRC_LOB => src_lob,
AMOUNT => DBMS_LOB.GETLENGTH(src_lob) );
DBMS_LOB.CLOSE(src_lob);

COMMIT;
END;
/
show errors

-- Let's test it
exec load_file('pic1.gif');
SELECT id, DBMS_LOB.GETLENGTH(fil) AS bytes_loaded
FROM lob_table;

rem
-----------------------------------------------------------------------
rem Filename: savelob.sql
rem Purpose: Save a binary file (images, documents, etc) from
database
rem to a flat file.
rem Author: Frank Naude, Oracle FAQ
rem
-----------------------------------------------------------------------

CREATE OR REPLACE PROCEDURE save_file(p_id NUMBER, p_file VARCHAR2)


IS
v_lob_loc BLOB;
v_lob_len NUMBER;
v_buffer RAW(32767);
v_buffer_size BINARY_INTEGER := 32767;
v_offset NUMBER := 1;
v_out_file UTL_FILE.FILE_TYPE;
BEGIN
SELECT fil INTO v_lob_loc FROM lob_table WHERE id = p_id;
v_lob_len := DBMS_LOB.GETLENGTH(v_lob_loc);

DBMS_LOB.OPEN(v_lob_loc, DBMS_LOB.LOB_READONLY);
v_out_file := UTL_FILE.FOPEN(location => 'MY_DIR',
filename => p_file,
open_mode => 'w',
max_linesize => 32767);

WHILE (v_offset <= v_lob_len) LOOP


dbms_output.put_line('v_start : ' || to_char(v_offset));
DBMS_LOB.READ(lob_loc => v_lob_loc,
amount => v_buffer_size,
offset => v_offset,
buffer => v_buffer);
v_offset := v_offset + v_buffer_size;
UTL_FILE.PUT_RAW(file => v_out_file,
buffer => v_buffer);
END LOOP;

UTL_FILE.FCLOSE(v_out_file);
DBMS_LOB.CLOSE(v_lob_loc);
END;
/
show errors

-- Let's test it
exec save_file(1, 'pic2.gif');
! ls -l /app/oracle/pic*.gif

o Fetch LONG column values piece-wise from PL/SQL

set serveroutput on

-- Create test table


drop table longtable;
create table longtable (longcol long) tablespace TOOLS;
insert into longtable values ( rpad('x', 257, 'QWERTY') );

DECLARE
cur1 PLS_INTEGER := DBMS_SQL.OPEN_CURSOR;;
rc NUMBER;
long_piece VARCHAR2(256);
piece_len INTEGER := 0;
long_tab DBMS_SQL.VARCHAR2S;
long_len INTEGER := 0;
BEGIN
DBMS_SQL.PARSE(cur1, 'select longcol from longtable',
DBMS_SQL.NATIVE);
DBMS_SQL.DEFINE_COLUMN_LONG(cur1, 1);
rc := DBMS_SQL.EXECUTE(cur1);
rc := DBMS_SQL.FETCH_ROWS(cur1); -- Get one row
-- Loop until all pieces of the long column are processed
LOOP
DBMS_SQL.COLUMN_VALUE_LONG(cur1, 1, 256, long_len, long_piece,
piece_len);
EXIT WHEN piece_len = 0;
DBMS_OUTPUT.PUT_LINE('Long piece len='|| piece_len);

long_tab( NVL(long_tab.LAST, 0)+1 ) := long_piece; -- Add piece to


table
long_len := long_len + piece_len;
END LOOP;
DBMS_SQL.CLOSE_CURSOR(cur1);
DBMS_OUTPUT.PUT_LINE('Total long col fetched, len='|| long_len);
END;
/
o
o
o Demonstrate writing to a file using the UTL_FILE package

CREATE DIRECTORY test_dir AS 'c:\';


-- CREATE DIRECTORY test_dir AS '/tmp';

DECLARE
fileHandler UTL_FILE.FILE_TYPE;
BEGIN
fileHandler := UTL_FILE.FOPEN('test_dir', 'test_file.txt', 'W');
UTL_FILE.PUTF(fileHandler, 'Look ma, I''m writing to a file!!!\n');
UTL_FILE.FCLOSE(fileHandler);
EXCEPTION
WHEN utl_file.invalid_path THEN
raise_application_error(-20000, 'ERROR: Invalid path for file.');
END;
o
o Map an external file to a database view

Utl_file_dir must be set to the directory you want to read from


show parameter utl_file_dir

-- Define the table's columns


CREATE OR REPLACE TYPE Alert_Row_Type AS OBJECT (
line NUMBER(8),
text VARCHAR2(2000)
);
/

-- Create a table of many row objects


CREATE OR REPLACE TYPE Alert_Type IS TABLE OF Alert_Row_Type;
/

-- Create a function to read the data into the table


CREATE OR REPLACE FUNCTION Get_Alert
RETURN Alert_Type
IS
Alert_Tab Alert_Type := Alert_Type(Alert_Row_Type(NULL, NULL));
v_file Utl_File.File_Type;
v_line NUMBER(10) := 1;
v_text VARCHAR2(2000);
b_read BOOLEAN := TRUE;
b_first BOOLEAN := TRUE;
BEGIN
dbms_output.put_line('About to open file...');
v_file := Utl_File.FOpen('/app/oracle/admin/orcl/bdump',
'alert_orcl.log', 'r');
WHILE b_read LOOP
BEGIN
Utl_File.Get_Line(v_file, v_text);
IF b_first THEN
b_first := FALSE;
ELSE
Alert_Tab.Extend;
END IF;

Alert_Tab(Alert_Tab.Last) := Alert_Row_Type(v_line, v_text);


v_line := v_line + 1;
EXCEPTION
WHEN NO_DATA_FOUND THEN
b_read := FALSE;
END;
END LOOP;
Utl_File.FClose(v_file);
RETURN Alert_Tab;
EXCEPTION
WHEN utl_file.invalid_path THEN
RAISE_APPLICATION_ERROR(-20001, 'ERROR: utl_file.invalid_path');
WHEN utl_file.invalid_mode THEN
RAISE_APPLICATION_ERROR(-20001, 'ERROR: utl_file.invalid_mode');
WHEN utl_file.invalid_filehandle THEN
RAISE_APPLICATION_ERROR(-20001, 'ERROR:
utl_file.invalid_filehandle');
WHEN utl_file.invalid_operation THEN
RAISE_APPLICATION_ERROR(-20001, 'ERROR: utl_file.invalid_operation');
WHEN utl_file.read_error THEN
RAISE_APPLICATION_ERROR(-20001, 'ERROR: utl_file.read_error');
WHEN utl_file.write_error THEN
RAISE_APPLICATION_ERROR(-20001, 'ERROR: utl_file.write_error');
WHEN utl_file.internal_error THEN
RAISE_APPLICATION_ERROR(-20001, 'ERROR: utl_file.internal_error');
WHEN OTHERS THEN
RAISE_APPLICATION_ERROR(-20001, 'ERROR: utl_file.other_error');
END;
/
show errors

-- Create a view to get the info from the function


CREATE OR REPLACE FORCE VIEW alert_log_file AS
SELECT LINE, TEXT
FROM Table(Cast(Get_Alert() As Alert_Type))
/

-- Test it!!!
set pages 50000
select * from alert_log_file
where text like '%ORA-%'
/
o
o Demonstrate Dynamic SQL

CREATE OR REPLACE PROCEDURE CREATE_TABLE1 AS


sql_stmt varchar2(4000);
BEGIN
EXECUTE IMMEDIATE 'CREATE TABLE x (a NUMBER)';
END;
/
show errors

CREATE OR REPLACE PROCEDURE CREATE_TABLE2 AS


cur integer;
rc integer;
BEGIN
cur := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE(cur, 'CREATE TABLE X (Y DATE)', DBMS_SQL.NATIVE);
rc := DBMS_SQL.EXECUTE(cur);
DBMS_SQL.CLOSE_CURSOR(cur);
END;
/
show errors

SET SERVEROUTPUT ON

CREATE OR REPLACE PROCEDURE DEPARTMENTS(NO IN DEPT.DEPTNO%TYPE) AS


v_cursor integer;
v_dname char(20);
v_rows integer;
BEGIN
v_cursor := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE(v_cursor, 'select dname from dept where deptno > :x',
DBMS_SQL.V7);
DBMS_SQL.BIND_VARIABLE(v_cursor, ':x', no);
DBMS_SQL.DEFINE_COLUMN_CHAR(v_cursor, 1, v_dname, 20);
v_rows := DBMS_SQL.EXECUTE(v_cursor);
loop
if DBMS_SQL.FETCH_ROWS(v_cursor) = 0 then
exit;
end if;
DBMS_SQL.COLUMN_VALUE_CHAR(v_cursor, 1, v_dname);
DBMS_OUTPUT.PUT_LINE('Deptartment name: '||v_dname);
end loop;
DBMS_SQL.CLOSE_CURSOR(v_cursor);
EXCEPTION
when others then
DBMS_SQL.CLOSE_CURSOR(v_cursor);
raise_application_error(-20000, 'Unknown Exception Raised: '||
sqlcode||' '||sqlerrm);
END;
/
show errors
o
o Demonstrate Java stored procedures

conn / as sysdba

-- @?/javavm/install/initjvm.sql
grant javauserpriv to scott;

conn scott/tiger

prompt Loading java source into database...

create or replace java source named "Hello" as


public class Hello { /* Pure Java
Code */
static public String Msg(String tail) {
return "Hello " + tail;
}
}
/
-- SHOW ERRORS not needed
-- Note, you can also use "loadjava" to load source files into Oracle.

prompt Publish Java to PL/SQL...

create or replace function hello (str varchar2) return varchar as


language java name 'Hello.Msg(java.lang.String) return
java.lang.String';
/
show errors

prompt Call Java function...

select hello('Frank') from dual


/

o
o Execute Operating System commands from PL/SQL (Java call)

rem
-----------------------------------------------------------------------
rem Filename: oscmd.sql
rem Purpose: Execute operating system commands from PL/SQL
rem Notes: Specify full paths to commands, for example,
rem specify /usr/bin/ps instead of ps.
rem Date: 09-Apr-2005
rem Author: Frank Naude, Oracle FAQ
rem
-----------------------------------------------------------------------

rem
-----------------------------------------------------------------------
rem Grant Java Access to user SCOTT
rem
-----------------------------------------------------------------------
conn / as sysdba

EXEC dbms_java.grant_permission('SCOTT',
'SYS:java.lang.RuntimePermission', 'writeFileDescriptor', '');
EXEC dbms_java.grant_permission('SCOTT',
'SYS:java.lang.RuntimePermission', 'readFileDescriptor', '');
EXEC dbms_java.grant_permission('SCOTT', 'SYS:java.io.FilePermission',
'/bin/sh', 'execute');
-- Other read ,write or execute permission may be requried

rem
-----------------------------------------------------------------------
rem Create Java class to execute OS commands...
rem
-----------------------------------------------------------------------

conn scott/tiger

CREATE OR REPLACE AND COMPILE JAVA SOURCE NAMED "Host" AS


import java.io.*;
public class Host {
public static void executeCommand(String command) {
try {
String[] finalCommand;
if (System.getProperty("os.name").toLowerCase().indexOf("windows")
!= -1) {
finalCommand = new String[4];
finalCommand[0] = "C:\\winnt\\system32\\cmd.exe";
finalCommand[1] = "/y";
finalCommand[2] = "/c";
finalCommand[3] = command;
} else { // Linux or Unix
System
finalCommand = new String[3];
finalCommand[0] = "/bin/sh";
finalCommand[1] = "-c";
finalCommand[2] = command;
}

// Execute the command...


final Process pr = Runtime.getRuntime().exec(finalCommand);

// Capture output from STDOUT...


BufferedReader br_in = null;
try {
br_in = new BufferedReader(new
InputStreamReader(pr.getInputStream()));
String buff = null;
while ((buff = br_in.readLine()) != null) {
System.out.println("stdout: " + buff);
try {Thread.sleep(100); } catch(Exception e) {}
}
br_in.close();
} catch (IOException ioe) {
System.out.println("Error printing process output.");
ioe.printStackTrace();
} finally {
try {
br_in.close();
} catch (Exception ex) {}
}

// Capture output from STDERR...


BufferedReader br_err = null;
try {
br_err = new BufferedReader(new
InputStreamReader(pr.getErrorStream()));
String buff = null;
while ((buff = br_err.readLine()) != null) {
System.out.println("stderr: " + buff);
try {Thread.sleep(100); } catch(Exception e) {}
}
br_err.close();
} catch (IOException ioe) {
System.out.println("Error printing execution errors.");
ioe.printStackTrace();
} finally {
try {
br_err.close();
} catch (Exception ex) {}
}
}
catch (Exception ex) {
System.out.println(ex.getLocalizedMessage());
}
}

};
/
show errors

rem
-----------------------------------------------------------------------
rem Publish the Java call to PL/SQL...
rem
-----------------------------------------------------------------------

CREATE OR REPLACE PROCEDURE host (p_command IN VARCHAR2)


AS LANGUAGE JAVA
NAME 'Host.executeCommand (java.lang.String)';
/
show errors

rem
-----------------------------------------------------------------------
rem Let's test it...
rem
-----------------------------------------------------------------------

CALL DBMS_JAVA.SET_OUTPUT(1000000);
SET SERVEROUTPUT ON SIZE 1000000
exec host('/usr/bin/ls');
o
o FTP Client (GET and PUT files from PL/SQL)

CREATE OR REPLACE PACKAGE BRNC_FTP_PKG


AS
/**
*
* PL/SQL FTP Client
*
* Created by: Russ Johnson, Braun Consulting
*
* www.braunconsult.com
*
* OVERVIEW
* --------------------
* This package uses the standard packages UTL_FILE and UTL_TCP to
perform
* client-side FTP functionality (PUT and GET) for text files as
defined in
* the World Wide Web Consortium's RFC 959 document -
http://www.w3.org/Protocols/rfc959/
* The procedures and functions in this package allow single or
multiple file transfer using
* standard TCP/IP connections.
*
* LIMITATIONS
* --------------------
* Currently the API is limited to transfer of ASCII text files
only. This is
* primarily because UTL_FILE only supports text I/O, but also
because the original
* design was for creating text files from data in the Oracle
database, then transferring the file to a remote host.
* Furthermore, the API does not support SSH/Secure FTP or
connection through a proxy server.
* Keep in mind that FTP passes the username/password combo in
plain text over TCP/IP.
*
* DB versions - 8i (8.1.x) and above. 8.0.x may work if it has
the SYS.UTL_TCP package.
*
*
* Note: Since UTL_FILE is used for the client-side I/O, this
package is also limited to
* transfer of files that exist in directories available to
UTL_FILE for read/write.
* These directories are defined by the UTL_FILE_DIR
parameter in the init.ora file.
*
* USAGE
* --------------------

* Three functions are available for FTP - PUT, GET, and


FTP_MULTIPLE. FTP_MULTIPLE takes
* a table of records that define the files to be transferred
(filename, directory, etc.).
* That table can have 1 record or multiple records. The PUT and
GET functions are included
* for convenience to FTP one file at a time. PUT and GET return
true if the file is transferred
* successfully and false if it fails. FTP_MULTIPLE returns true
if no batch-level errors occur
* (such as an invalid host, refused connection, or invalid login
information). It also takes the
* table of file records IN and passes it back OUT. Each record
contains individual error information.
*
* EXAMPLE
* --------------------
* Transfer multiple files - 1 GET and 2 PUT from a Windows machine
to a host (assuming UNIX here).
* Display any errors that occur.
* DECLARE
*
* v_username VARCHAR2(40) := 'rjohnson';
* v_password VARCHAR2(40) := 'password';
* v_hostname VARCHAR2(255) :=
'ftp.oracle.com';
* v_error_message VARCHAR2(1000);
* b_put BOOLEAN;
* t_files BRNC_FTP_PKG.t_ftp_rec; --
Declare our table of file records
*
* BEGIN
*
* t_files(1).localpath :=
'd:\oracle\utl_file\outbound';
* t_files(1).filename := 'myfile1.txt';
* t_files(1).remotepath :=
'/home/oracle/text_files';
* t_files(1).transfer_mode := 'PUT';
*
* t_files(2).localpath :=
'd:\oracle\utl_file\inbound';
* t_files(2).filename := 'incoming_file.xml';
* t_files(2).remotepath :=
'/home/oracle/xml_files';
* t_files(2).transfer_mode := 'GET';
*
* t_files(3).localpath :=
'd:\oracle\utl_file\outbound';
* t_files(3).filename := 'myfile2.txt';
* t_files(3).remotepath := '/home';
* t_files(3).transfer_mode := 'PUT';
*
* b_put := BRNC_FTP_PKG.FTP_MULTIPLE(v_error_message,
* t_files,
* v_username,
* v_password,
* v_hostname);
* IF b_put = TRUE
* THEN
* FOR i IN t_files.FIRST..t_files.LAST
* LOOP
* IF t_files.EXISTS(i)
* THEN
* DBMS_OUTPUT.PUT_LINE(t_files(i).status||' | '||
* t_files(i).error_message||' | '||
*
to_char(t_files(i).bytes_transmitted)||' | '||
* to_char(t_files(i).trans_start,'YYYY-
MM-DD HH:MI:SS')||' | '||
* to_char(t_files(i).trans_end,'YYYY-
MM-DD HH:MI:SS'));
* END IF;
* END LOOP;
* ELSE
* DBMS_OUTPUT.PUT_LINE(v_error_message);
* END IF;
*
* EXCEPTION
* WHEN OTHERS
* THEN
* DBMS_OUTPUT.PUT_LINE(SQLERRM);
* END;
*
* CREDITS
* --------------------
* The W3C's RFC 959 that describes the FTP process.
*
* http://www.w3c.org
*
* Much of the PL/SQL code in this package was based on Java code
written by
* Bruce Blackshaw of Enterprise Distributed Technologies Ltd.
None of that code
* was copied, but the objects and methods greatly helped my
understanding of the
* FTP Client process.
*
* http://www.enterprisedt.com
*
* VERSION HISTORY
* --------------------
* 1.0 11/19/2002 Unit-tested single and multiple transfers
between disparate hosts.
*
*
*/

/**
* Exceptions
*
*/

ctrl_exception EXCEPTION;
data_exception EXCEPTION;

/**
* Constants - FTP valid response codes
*
*/

CONNECT_CODE CONSTANT PLS_INTEGER := 220;


USER_CODE CONSTANT PLS_INTEGER := 331;
LOGIN_CODE CONSTANT PLS_INTEGER := 230;
PWD_CODE CONSTANT PLS_INTEGER := 257;
PASV_CODE CONSTANT PLS_INTEGER := 227;
CWD_CODE CONSTANT PLS_INTEGER := 250;
TSFR_START_CODE1 CONSTANT PLS_INTEGER := 125;
TSFR_START_CODE2 CONSTANT PLS_INTEGER := 150;
TSFR_END_CODE CONSTANT PLS_INTEGER := 226;
QUIT_CODE CONSTANT PLS_INTEGER := 221;
SYST_CODE CONSTANT PLS_INTEGER := 215;
TYPE_CODE CONSTANT PLS_INTEGER := 200;

/**
* FTP File record datatype
*
* Elements:
* localpath - full directory name in which the local file resides
or will reside
* Windows: 'd:\oracle\utl_file'
* UNIX: '/home/oracle/utl_file'
* filename - filename and extension for the file to be received or
sent
* changing the filename for the PUT or GET is currently
not allowed
* Examples: 'myfile.dat' 'myfile20021119.xml'
* remotepath - full directory name in which the local file will be
sent or the
* remote file exists. Should be in UNIX format
regardless of FTP server - '/one/two/three'
* filetype - reserved for future use, ignored in code
* transfer_mode - 'PUT' or 'GET'
* status - status of the transfer. 'ERROR' or 'SUCCESS'
* error_message - meaningful (hopefully) error message explaining
the reason for failure
* bytes_transmitted - how many bytes were sent/received
* trans_start - date/time the transmission started
* trans_end - date/time the transmission ended
*
*/

TYPE r_ftp_rec IS RECORD(localpath VARCHAR2(255),


filename VARCHAR2(255),
remotepath VARCHAR2(255),
filetype VARCHAR2(20),
transfer_mode VARCHAR2(5),
status VARCHAR2(40),
error_message VARCHAR2(255),
bytes_transmitted NUMBER,
trans_start DATE,
trans_end DATE);

/**
* FTP File Table - used to store many files for transfer
*
*/

TYPE t_ftp_rec IS TABLE of r_ftp_rec INDEX BY BINARY_INTEGER;

/**
* Internal convenience procedure for creating passive host IP
address
* and port number.
*
*/

PROCEDURE CREATE_PASV(p_pasv_cmd IN VARCHAR2,


p_pasv_host OUT VARCHAR2,
p_pasv_port OUT NUMBER);

/**
* Function used to validate FTP server responses based on the
* code passed in p_code. Reads single or multi-line responses.
*
*/

FUNCTION VALIDATE_REPLY(p_ctrl_con IN OUT UTL_TCP.CONNECTION,


p_code IN PLS_INTEGER,
p_reply OUT VARCHAR2)
RETURN BOOLEAN;

/**
* Function used to validate FTP server responses based on the
* code passed in p_code. Reads single or multi-line responses.
* Overloaded because some responses can have 2 valid codes.
*
*/

FUNCTION VALIDATE_REPLY(p_ctrl_con IN OUT UTL_TCP.CONNECTION,


p_code1 IN PLS_INTEGER,
p_code2 IN PLS_INTEGER,
p_reply OUT VARCHAR2)
RETURN BOOLEAN;

/**
* Procedure that handles the actual data transfer. Meant
* for internal package use. Returns information about the
* actual transfer.
*
*/

PROCEDURE TRANSFER_ASCII(u_ctrl_con IN OUT UTL_TCP.CONNECTION,


p_localpath IN VARCHAR2,
p_filename IN VARCHAR2,
p_pasv_host IN VARCHAR2,
p_pasv_port IN PLS_INTEGER,
p_transfer_mode IN VARCHAR2,
v_status OUT VARCHAR2,
v_error_message OUT VARCHAR2,
n_bytes_transmitted OUT NUMBER,
d_trans_start OUT DATE,
d_trans_end OUT DATE);

/**
* Function to handle FTP of many files.
* Returns TRUE if no batch-level errors occur.
* Returns FALSE if a batch-level error occurs.
*
* Parameters:
*
* p_error_msg - error message for batch level errors
* p_files - BRNC_FTP_PKG.t_ftp_rec table type. Accepts
* list of files to be transferred (may be any combination
of PUT or GET)
* returns the table updated with transfer status, error
message,
* bytes_transmitted, transmission start date/time and
transmission end
* date/time
* p_username - username for FTP server
* p_password - password for FTP server
* p_hostname - hostname or IP address of server Ex:
'ftp.oracle.com' or '127.0.0.1'
* p_port - port number to connect on. FTP is usually on 21, but
this may be overridden
* if the server is configured differently.
*
*/

FUNCTION FTP_MULTIPLE(p_error_msg OUT VARCHAR2,


p_files IN OUT t_ftp_rec,
p_username IN VARCHAR2,
p_password IN VARCHAR2,
p_hostname IN VARCHAR2,
p_port IN PLS_INTEGER DEFAULT 21)
RETURN BOOLEAN;

/**
* Convenience function for single-file PUT
*
* Parameters:
* p_localpath - full directory name in which the local file resides
or will reside
* Windows: 'd:\oracle\utl_file'
* UNIX: '/home/oracle/utl_file'
* p_filename - filename and extension for the file to be received
or sent
* changing the filename for the PUT or GET is currently
not allowed
* Examples: 'myfile.dat' 'myfile20021119.xml'
* p_remotepath - full directory name in which the local file will
be sent or the
* remote file exists. Should be in UNIX format
regardless of FTP server - '/one/two/three'
* p_username - username for FTP server
* p_password - password for FTP server
* p_hostname - FTP server IP address or host name Ex:
'ftp.oracle.com' or '127.0.0.1'
* v_status - status of the transfer. 'ERROR' or 'SUCCESS'
* v_error_message - meaningful (hopefully) error message explaining
the reason for failure
* n_bytes_transmitted - how many bytes were sent/received
* d_trans_start - date/time the transmission started
* d_trans_end - date/time the transmission ended
* p_port - port number to connect to, default is 21
* p_filetype - always set to 'ASCII', reserved for future use,
ignored in code
*
*/

FUNCTION PUT(p_localpath IN VARCHAR2,


p_filename IN VARCHAR2,
p_remotepath IN VARCHAR2,
p_username IN VARCHAR2,
p_password IN VARCHAR2,
p_hostname IN VARCHAR2,
v_status OUT VARCHAR2,
v_error_message OUT VARCHAR2,
n_bytes_transmitted OUT NUMBER,
d_trans_start OUT DATE,
d_trans_end OUT DATE,
p_port IN PLS_INTEGER DEFAULT 21,
p_filetype IN VARCHAR2 := 'ASCII')
RETURN BOOLEAN;

/**
* Convenience function for single-file GET
*
* Parameters:
* p_localpath - full directory name in which the local file resides
or will reside
* Windows: 'd:\oracle\utl_file'
* UNIX: '/home/oracle/utl_file'
* p_filename - filename and extension for the file to be received
or sent
* changing the filename for the PUT or GET is currently
not allowed
* Examples: 'myfile.dat' 'myfile20021119.xml'
* p_remotepath - full directory name in which the local file will
be sent or the
* remote file exists. Should be in UNIX format
regardless of FTP server - '/one/two/three'
* p_username - username for FTP server
* p_password - password for FTP server
* p_hostname - FTP server IP address or host name Ex:
'ftp.oracle.com' or '127.0.0.1'
* v_status - status of the transfer. 'ERROR' or 'SUCCESS'
* v_error_message - meaningful (hopefully) error message explaining
the reason for failure
* n_bytes_transmitted - how many bytes were sent/received
* d_trans_start - date/time the transmission started
* d_trans_end - date/time the transmission ended
* p_port - port number to connect to, default is 21
* p_filetype - always set to 'ASCII', reserved for future use,
ignored in code
*
*/

FUNCTION GET(p_localpath IN VARCHAR2,


p_filename IN VARCHAR2,
p_remotepath IN VARCHAR2,
p_username IN VARCHAR2,
p_password IN VARCHAR2,
p_hostname IN VARCHAR2,
v_status OUT VARCHAR2,
v_error_message OUT VARCHAR2,
n_bytes_transmitted OUT NUMBER,
d_trans_start OUT DATE,
d_trans_end OUT DATE,
p_port IN PLS_INTEGER DEFAULT 21,
p_filetype IN VARCHAR2 := 'ASCII')
RETURN BOOLEAN;

END BRNC_FTP_PKG;
/
CREATE OR REPLACE PACKAGE BODY BRNC_FTP_PKG
AS

/***********************************************************************
******
** Create the passive host IP and port number to connect to
**

************************************************************************
*****/

PROCEDURE CREATE_PASV(p_pasv_cmd IN VARCHAR2,


p_pasv_host OUT VARCHAR2,
p_pasv_port OUT NUMBER)
IS

v_pasv_cmd VARCHAR2(30) := p_pasv_cmd; --Host and port to


connect to for data transfer
n_port_dec NUMBER;
n_port_add NUMBER;

BEGIN

p_pasv_host :=
REPLACE(SUBSTR(v_pasv_cmd,1,INSTR(v_pasv_cmd,',',1,4)-1),',','.');

n_port_dec :=
TO_NUMBER(SUBSTR(v_pasv_cmd,INSTR(v_pasv_cmd,',',1,4)+1,
(INSTR(v_pasv_cmd,',',1,5)-(INSTR(v_pasv_cmd,',',1,4)+1))));
n_port_add :=
TO_NUMBER(SUBSTR(v_pasv_cmd,INSTR(v_pasv_cmd,',',1,5)+1,LENGTH(v_pasv_cm
d)-INSTR(v_pasv_cmd,',',1,5)));
p_pasv_port := (n_port_dec*256) + n_port_add;

EXCEPTION
WHEN OTHERS
THEN
--DBMS_OUTPUT.PUT_LINE(SQLERRM);
RAISE;

END CREATE_PASV;

/***********************************************************************
******
** Read a single or multi-line reply from the FTP server and
validate
** it against the code passed in p_code.
**
** Return TRUE if reply code matches p_code, FALSE if it doesn't or
error
** occurs
**
** Send full server response back to calling procedure

************************************************************************
*****/

FUNCTION VALIDATE_REPLY(p_ctrl_con IN OUT UTL_TCP.CONNECTION,


p_code IN PLS_INTEGER,
p_reply OUT VARCHAR2)
RETURN BOOLEAN
IS
n_code VARCHAR2(3) := p_code;
n_byte_count PLS_INTEGER;
v_msg VARCHAR2(255);
n_line_count PLS_INTEGER := 0;
BEGIN
LOOP
v_msg := UTL_TCP.GET_LINE(p_ctrl_con);
n_line_count := n_line_count + 1;
IF n_line_count = 1
THEN
p_reply := v_msg;
ELSE
p_reply := p_reply || SUBSTR(v_msg,4);
END IF;
EXIT WHEN INSTR(v_msg,'-',1,1) <> 4;
END LOOP;
IF to_number(SUBSTR(p_reply,1,3)) = n_code
THEN
RETURN TRUE;
ELSE
RETURN FALSE;
END IF;
EXCEPTION
WHEN OTHERS
THEN
p_reply := SQLERRM;
RETURN FALSE;
END VALIDATE_REPLY;

/***********************************************************************
******
** Reads a single or multi-line reply from the FTP server
**
** Return TRUE if reply code matches p_code1 or p_code2,
** FALSE if it doesn't or error occurs
**
** Send full server response back to calling procedure

************************************************************************
*****/

FUNCTION VALIDATE_REPLY(p_ctrl_con IN OUT UTL_TCP.CONNECTION,


p_code1 IN PLS_INTEGER,
p_code2 IN PLS_INTEGER,
p_reply OUT VARCHAR2)
RETURN BOOLEAN
IS
v_code1 VARCHAR2(3) := to_char(p_code1);
v_code2 VARCHAR2(3) := to_char(p_code2);
v_msg VARCHAR2(255);
n_line_count PLS_INTEGER := 0;
BEGIN
LOOP
v_msg := UTL_TCP.GET_LINE(p_ctrl_con);
n_line_count := n_line_count + 1;
IF n_line_count = 1
THEN
p_reply := v_msg;
ELSE
p_reply := p_reply || SUBSTR(v_msg,4);
END IF;
EXIT WHEN INSTR(v_msg,'-',1,1) <> 4;
END LOOP;
IF to_number(SUBSTR(p_reply,1,3)) IN(v_code1,v_code2)
THEN
RETURN TRUE;
ELSE
RETURN FALSE;
END IF;
EXCEPTION
WHEN OTHERS
THEN
p_reply := SQLERRM;
RETURN FALSE;
END VALIDATE_REPLY;

/***********************************************************************
******
** Handles actual data transfer. Responds with status, error
message, and
** transfer statistics.
**
** Potential errors could be with connection or file i/o
**

************************************************************************
*****/

PROCEDURE TRANSFER_ASCII(u_ctrl_con IN OUT UTL_TCP.CONNECTION,


p_localpath IN VARCHAR2,
p_filename IN VARCHAR2,
p_pasv_host IN VARCHAR2,
p_pasv_port IN PLS_INTEGER,
p_transfer_mode IN VARCHAR2,
v_status OUT VARCHAR2,
v_error_message OUT VARCHAR2,
n_bytes_transmitted OUT NUMBER,
d_trans_start OUT DATE,
d_trans_end OUT DATE)
IS
u_data_con UTL_TCP.CONNECTION;
u_filehandle UTL_FILE.FILE_TYPE;
v_tsfr_mode VARCHAR2(3) := p_transfer_mode;
v_mode VARCHAR2(1);
v_tsfr_cmd VARCHAR2(10);
v_buffer VARCHAR2(32767);
v_localpath VARCHAR2(255) := p_localpath;
v_filename VARCHAR2(255) := p_filename;
v_host VARCHAR2(20) := p_pasv_host;
n_port PLS_INTEGER := p_pasv_port;
n_bytes NUMBER;
v_msg VARCHAR2(255);
v_reply VARCHAR2(1000);
v_err_status VARCHAR2(20) := 'ERROR';

BEGIN

/** Initialize some of our OUT variables **/

v_status := 'SUCCESS';
v_error_message := ' ';
n_bytes_transmitted := 0;

IF UPPER(v_tsfr_mode) = 'PUT'
THEN
v_mode := 'r';
v_tsfr_cmd := 'STOR ';

ELSIF UPPER(v_tsfr_mode) = 'GET'


THEN
v_mode := 'w';
v_tsfr_cmd := 'RETR ';
END IF;

/** Open data connection on Passive host and port **/

u_data_con := UTL_TCP.OPEN_CONNECTION(v_host,n_port);
/** Open the local file to read and transfer data **/

u_filehandle := UTL_FILE.FOPEN(v_localpath,v_filename,v_mode);

/** Send the STOR command to tell the server we're going to
upload a file **/

n_bytes := UTL_TCP.WRITE_LINE(u_ctrl_con,v_tsfr_cmd||
v_filename);
IF
VALIDATE_REPLY(u_ctrl_con,TSFR_START_CODE1,TSFR_START_CODE2,v_reply) =
FALSE
THEN
RAISE ctrl_exception;
END IF;

d_trans_start := SYSDATE;

IF UPPER(v_tsfr_mode) = 'PUT'
THEN
LOOP
BEGIN
UTL_FILE.GET_LINE(u_filehandle,v_buffer);
EXCEPTION
WHEN NO_DATA_FOUND
THEN
EXIT;
END;

n_bytes := UTL_TCP.WRITE_LINE(u_data_con,v_buffer);
n_bytes_transmitted := n_bytes_transmitted + n_bytes;

END LOOP;

ELSIF UPPER(v_tsfr_mode) = 'GET'


THEN
LOOP
BEGIN
v_buffer := UTL_TCP.GET_LINE(u_data_con,TRUE);

/** Sometimes the TCP/IP buffer sends null data **/


/** we only want to receive the actual data **/

IF v_buffer IS NOT NULL


THEN
UTL_FILE.PUT_LINE(u_filehandle,v_buffer);
n_bytes := LENGTH(v_buffer);
n_bytes_transmitted := n_bytes_transmitted +
n_bytes;
END IF;

EXCEPTION
WHEN UTL_TCP.END_OF_INPUT
THEN
EXIT;
END;

END LOOP;

END IF;

/** Flush the buffer on the data connection **/

--UTL_TCP.FLUSH(u_data_con);

d_trans_end := SYSDATE;

/** Close the file **/

UTL_FILE.FCLOSE(u_filehandle);

/** Close the Data Connection **/

UTL_TCP.CLOSE_CONNECTION(u_data_con);

/** Verify the transfer succeeded **/

IF VALIDATE_REPLY(u_ctrl_con,TSFR_END_CODE,v_reply) = FALSE
THEN
RAISE ctrl_exception;
END IF;

EXCEPTION
WHEN ctrl_exception
THEN
v_status := v_err_status;
v_error_message := v_reply;
IF UTL_FILE.IS_OPEN(u_filehandle)
THEN
UTL_FILE.FCLOSE(u_filehandle);
END IF;
UTL_TCP.CLOSE_CONNECTION(u_data_con);

WHEN UTL_FILE.invalid_path
THEN
v_status := v_err_status;
v_error_message := 'Directory '||v_localpath||' is not available
to UTL_FILE. Check the init.ora file for valid UTL_FILE directories.';
UTL_TCP.CLOSE_CONNECTION(u_data_con);

WHEN UTL_FILE.invalid_operation
THEN
v_status := v_err_status;

IF UPPER(v_tsfr_mode) = 'PUT'
THEN
v_error_message := 'The file '||V_filename||' in the
directory '||v_localpath||' could not be opened for reading.';

ELSIF UPPER(v_tsfr_mode) = 'GET'


THEN
v_error_message := 'The file '||V_filename||' in the
directory '||v_localpath||' could not be opened for writing.';

END IF;

IF UTL_FILE.IS_OPEN(u_filehandle)
THEN
UTL_FILE.FCLOSE(u_filehandle);
END IF;
UTL_TCP.CLOSE_CONNECTION(u_data_con);

WHEN UTL_FILE.read_error
THEN
v_status := v_err_status;
v_error_message := 'The system encountered an error while trying
to read '||v_filename||' in the directory '||v_localpath;

IF UTL_FILE.IS_OPEN(u_filehandle)
THEN
UTL_FILE.FCLOSE(u_filehandle);
END IF;
UTL_TCP.CLOSE_CONNECTION(u_data_con);

WHEN UTL_FILE.write_error
THEN
v_status := v_err_status;
v_error_message := 'The system encountered an error while trying
to write to '||v_filename||' in the directory '||v_localpath;

IF UTL_FILE.IS_OPEN(u_filehandle)
THEN
UTL_FILE.FCLOSE(u_filehandle);
END IF;
UTL_TCP.CLOSE_CONNECTION(u_data_con);

WHEN UTL_FILE.internal_error
THEN
v_status := v_err_status;
v_error_message := 'The UTL_FILE package encountered an
unexpected internal system error.';

IF UTL_FILE.IS_OPEN(u_filehandle)
THEN
UTL_FILE.FCLOSE(u_filehandle);
END IF;
UTL_TCP.CLOSE_CONNECTION(u_data_con);

WHEN OTHERS
THEN
v_status := v_err_status;
v_error_message := SQLERRM;
IF UTL_FILE.IS_OPEN(u_filehandle)
THEN
UTL_FILE.FCLOSE(u_filehandle);
END IF;
UTL_TCP.CLOSE_CONNECTION(u_data_con);
END TRANSFER_ASCII;
/***********************************************************************
******
** Handles connection to host and FTP of multiple files
** Files can be any combination of PUT and GET
**

************************************************************************
*****/

FUNCTION FTP_MULTIPLE(p_error_msg OUT VARCHAR2,


p_files IN OUT t_ftp_rec,
p_username IN VARCHAR2,
p_password IN VARCHAR2,
p_hostname IN VARCHAR2,
p_port IN PLS_INTEGER DEFAULT 21)
RETURN BOOLEAN
IS
v_username VARCHAR2(30) := p_username;
v_password VARCHAR2(30) := p_password;
v_hostname VARCHAR2(30) := p_hostname;
n_port PLS_INTEGER := p_port;
u_ctrl_con UTL_TCP.CONNECTION;
n_byte_count PLS_INTEGER;
n_first_index NUMBER;
v_msg VARCHAR2(250);
v_reply VARCHAR2(1000);
v_pasv_host VARCHAR2(20);
n_pasv_port NUMBER;

invalid_transfer EXCEPTION;
BEGIN

p_error_msg := 'FTP Successful'; --Assume the overall transfer


will succeed

/** Attempt to connect to the host machine **/

u_ctrl_con := UTL_TCP.OPEN_CONNECTION(v_hostname,n_port);
IF VALIDATE_REPLY(u_ctrl_con,CONNECT_CODE,v_reply) = FALSE
THEN
RAISE ctrl_exception;
END IF;

/** Send username **/

n_byte_count := UTL_TCP.WRITE_LINE(u_ctrl_con,'USER '||


v_username);
IF VALIDATE_REPLY(u_ctrl_con,USER_CODE,v_reply) = FALSE
THEN
RAISE ctrl_exception;
END IF;

/** Send password **/


n_byte_count := UTL_TCP.WRITE_LINE(u_ctrl_con,'PASS '||
v_password);
IF VALIDATE_REPLY(u_ctrl_con,LOGIN_CODE,v_reply) = FALSE
THEN
RAISE ctrl_exception;
END IF;

/** We should be logged in, time to transfer all files **/

FOR i IN p_files.FIRST..p_files.LAST
LOOP
IF p_files.EXISTS(i)
THEN
BEGIN

/** Change to the remotepath directory **/

n_byte_count := UTL_TCP.WRITE_LINE(u_ctrl_con,'CWD
'||p_files(i).remotepath);
IF VALIDATE_REPLY(u_ctrl_con,CWD_CODE,v_reply) =
FALSE
THEN
RAISE ctrl_exception;
END IF;

/** Switch to IMAGE mode **/

n_byte_count := UTL_TCP.WRITE_LINE(u_ctrl_con,'TYPE
I');
IF VALIDATE_REPLY(u_ctrl_con,TYPE_CODE,v_reply) =
FALSE
THEN
RAISE ctrl_exception;
END IF;

/** Get a Passive connection to use for data


transfer **/

n_byte_count :=
UTL_TCP.WRITE_LINE(u_ctrl_con,'PASV');
IF VALIDATE_REPLY(u_ctrl_con,PASV_CODE,v_reply) =
FALSE
THEN
RAISE ctrl_exception;
END IF;

CREATE_PASV(SUBSTR(v_reply,INSTR(v_reply,'(',1,1)+1,INSTR(v_reply,')',1,
1)-INSTR(v_reply,'(',1,1)-1),v_pasv_host,n_pasv_port);

/** Transfer Data **/

IF UPPER(p_files(i).transfer_mode) = 'PUT'
THEN
TRANSFER_ASCII(u_ctrl_con,
p_files(i).localpath,
p_files(i).filename,
v_pasv_host,
n_pasv_port,
p_files(i).transfer_mode,
p_files(i).status,
p_files(i).error_message,
p_files(i).bytes_transmitted,
p_files(i).trans_start,
p_files(i).trans_end);

ELSIF UPPER(p_files(i).transfer_mode) = 'GET'


THEN
TRANSFER_ASCII(u_ctrl_con,
p_files(i).localpath,
p_files(i).filename,
v_pasv_host,
n_pasv_port,
p_files(i).transfer_mode,
p_files(i).status,
p_files(i).error_message,
p_files(i).bytes_transmitted,
p_files(i).trans_start,
p_files(i).trans_end);
ELSE
RAISE invalid_transfer; -- Raise an exception
here
END IF;

EXCEPTION
WHEN ctrl_exception
THEN
p_files(i).status := 'ERROR';
p_files(i).error_message := v_reply;

WHEN invalid_transfer
THEN
p_files(i).status := 'ERROR';
p_files(i).error_message := 'Invalid transfer
method. Use PUT or GET.';

END;
END IF;
END LOOP;

/** Send QUIT command **/


n_byte_count := UTL_TCP.WRITE_LINE(u_ctrl_con,'QUIT');

/** Don't need to validate QUIT, just close the connection **/

UTL_TCP.CLOSE_CONNECTION(u_ctrl_con);
RETURN TRUE;

EXCEPTION
WHEN ctrl_exception
THEN
p_error_msg := v_reply;
UTL_TCP.CLOSE_ALL_CONNECTIONS;
RETURN FALSE;
WHEN OTHERS
THEN
p_error_msg := SQLERRM;
UTL_TCP.CLOSE_ALL_CONNECTIONS;
RETURN FALSE;
END FTP_MULTIPLE;

/***********************************************************************
******
** Convenience function for single-file PUT
** Formats file information for FTP_MULTIPLE function and calls it.
**

************************************************************************
*****/

FUNCTION PUT(p_localpath IN VARCHAR2,


p_filename IN VARCHAR2,
p_remotepath IN VARCHAR2,
p_username IN VARCHAR2,
p_password IN VARCHAR2,
p_hostname IN VARCHAR2,
v_status OUT VARCHAR2,
v_error_message OUT VARCHAR2,
n_bytes_transmitted OUT NUMBER,
d_trans_start OUT DATE,
d_trans_end OUT DATE,
p_port IN PLS_INTEGER DEFAULT 21,
p_filetype IN VARCHAR2 := 'ASCII')
RETURN BOOLEAN
IS
t_files t_ftp_rec;
v_username VARCHAR2(30) := p_username;
v_password VARCHAR2(50) := p_password;
v_hostname VARCHAR2(100) := p_hostname;
n_port PLS_INTEGER := p_port;
v_err_msg VARCHAR2(255);
b_ftp BOOLEAN;
BEGIN
t_files(1).localpath := p_localpath;
t_files(1).filename := p_filename;
t_files(1).remotepath := p_remotepath;
t_files(1).filetype := p_filetype;
t_files(1).transfer_mode := 'PUT';

b_ftp := FTP_MULTIPLE(v_err_msg,
t_files,
v_username,
v_password,
v_hostname,
n_port);
IF b_ftp = FALSE
THEN
v_status := 'ERROR';
v_error_message := v_err_msg;
RETURN FALSE;
ELSIF b_ftp = TRUE
THEN
v_status := t_files(1).status;
v_error_message := t_files(1).error_message;
n_bytes_transmitted := t_files(1).bytes_transmitted;
d_trans_start := t_files(1).trans_start;
d_trans_end := t_files(1).trans_end;
RETURN TRUE;
END IF;
EXCEPTION
WHEN OTHERS
THEN
v_status := 'ERROR';
v_error_message := SQLERRM;
RETURN FALSE;
--DBMS_OUTPUT.PUT_LINE(SQLERRM);
END PUT;

/***********************************************************************
******
** Convenience function for single-file GET
** Formats file information for FTP_MULTIPLE function and calls it.
**

************************************************************************
*****/

FUNCTION GET(p_localpath IN VARCHAR2,


p_filename IN VARCHAR2,
p_remotepath IN VARCHAR2,
p_username IN VARCHAR2,
p_password IN VARCHAR2,
p_hostname IN VARCHAR2,
v_status OUT VARCHAR2,
v_error_message OUT VARCHAR2,
n_bytes_transmitted OUT NUMBER,
d_trans_start OUT DATE,
d_trans_end OUT DATE,
p_port IN PLS_INTEGER DEFAULT 21,
p_filetype IN VARCHAR2 := 'ASCII')
RETURN BOOLEAN
IS
t_files t_ftp_rec;
v_username VARCHAR2(30) := p_username;
v_password VARCHAR2(50) := p_password;
v_hostname VARCHAR2(100) := p_hostname;
n_port PLS_INTEGER := p_port;
v_err_msg VARCHAR2(255);
b_ftp BOOLEAN;
BEGIN
t_files(1).localpath := p_localpath;
t_files(1).filename := p_filename;
t_files(1).remotepath := p_remotepath;
t_files(1).filetype := p_filetype;
t_files(1).transfer_mode := 'GET';
b_ftp := FTP_MULTIPLE(v_err_msg,
t_files,
v_username,
v_password,
v_hostname,
n_port);
IF b_ftp = FALSE
THEN
v_status := 'ERROR';
v_error_message := v_err_msg;
RETURN FALSE;
ELSIF b_ftp = TRUE
THEN
v_status := t_files(1).status;
v_error_message := t_files(1).error_message;
n_bytes_transmitted := t_files(1).bytes_transmitted;
d_trans_start := t_files(1).trans_start;
d_trans_end := t_files(1).trans_end;
RETURN TRUE;
END IF;
EXCEPTION
WHEN OTHERS
THEN
v_status := 'ERROR';
v_error_message := SQLERRM;
RETURN FALSE;
--DBMS_OUTPUT.PUT_LINE(SQLERRM);
END GET;

END BRNC_FTP_PKG;
/

o
o Send e-mail messages from PL/SQL (using UTL_TCP)

CREATE OR REPLACE PROCEDURE SEND_MAIL (


msg_from varchar2 := 'oracle',
msg_to varchar2,
msg_subject varchar2 := 'E-Mail message from your database',
msg_text varchar2 := '' )
IS
c utl_tcp.connection;
rc integer;
BEGIN
c := utl_tcp.open_connection('127.0.0.1', 25); -- open the SMTP
port 25 on local machine
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
rc := utl_tcp.write_line(c, 'HELO localhost');
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
rc := utl_tcp.write_line(c, 'MAIL FROM: '||msg_from);
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
rc := utl_tcp.write_line(c, 'RCPT TO: '||msg_to);
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
rc := utl_tcp.write_line(c, 'DATA'); -- Start message
body
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
rc := utl_tcp.write_line(c, 'Subject: '||msg_subject);
rc := utl_tcp.write_line(c, '');
rc := utl_tcp.write_line(c, msg_text);
rc := utl_tcp.write_line(c, '.'); -- End of message
body
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
rc := utl_tcp.write_line(c, 'QUIT');
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
utl_tcp.close_connection(c); -- Close the
connection
EXCEPTION
when others then
raise_application_error(
-20000, 'Unable to send e-mail message from pl/sql because
of: '||
sqlerrm);
END;
/
show errors

-- Examples:
set serveroutput on

exec send_mail(msg_to =>'you@yourdomain.com');

exec send_mail(msg_to =>'you@yourdomain.com', -


msg_text=>'Look Ma, I can send mail from plsql' -
);
o
o Send e-mail messages from PL/SQL with MIME attachments

CREATE OR REPLACE PROCEDURE SEND_MAIL (


msg_from varchar2 := 'EMAILADDRESS@DOMAIN.COM', ----- MAIL BOX
SENDING THE EMAIL
msg_to varchar2 := 'EMAILADDRESS@DOMAIN.COM', ----- MAIL BOX
RECIEVING THE EMAIL
msg_subject varchar2 := 'Output file TEST1', ----- EMAIL
SUBJECT
msg_text varchar2 := 'THIS IS THE TEXT OF THE EMAIL MESSAGE.',
v_output1 varchar2 := 'THIS IS THE TEXT OF THE ATTACHMENT FILE. THIS
TEXT SHOULD BE IN A TEXT FILE ATTACHED TO THE EMAIL.')
IS
c utl_tcp.connection;
rc integer;
crlf VARCHAR2(2):= CHR(13)||CHR(10);
mesg VARCHAR2( 32767 );
BEGIN
c := utl_tcp.open_connection('196.35.140.18', 25); ----- OPEN
SMTP PORT CONNECTION
rc := utl_tcp.write_line(c, 'HELO 196.35.140.18'); -----
PERFORMS HANDSHAKING WITH SMTP SERVER
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
rc := utl_tcp.write_line(c, 'EHLO 196.35.140.18'); -----
PERFORMS HANDSHAKING WITH SMTP SERVER, INCLUDING EXTRA INFORMATION
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
rc := utl_tcp.write_line(c, 'MAIL FROM: '||msg_from); ----- MAIL
BOX SENDING THE EMAIL
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
rc := utl_tcp.write_line(c, 'RCPT TO: '||msg_to); ----- MAIL
BOX RECIEVING THE EMAIL
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
rc := utl_tcp.write_line(c, 'DATA'); ----- EMAIL
MESSAGE BODY START
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
rc := utl_tcp.write_line(c, 'Date: '||TO_CHAR( SYSDATE, 'dd Mon yy
hh24:mi:ss' ));
rc := utl_tcp.write_line(c, 'From: '||msg_from||' <'||msg_from||'>');
rc := utl_tcp.write_line(c, 'MIME-Version: 1.0');
rc := utl_tcp.write_line(c, 'To: '||msg_to||' <'||msg_to||'>');
rc := utl_tcp.write_line(c, 'Subject: '||msg_subject);
rc := utl_tcp.write_line(c, 'Content-Type: multipart/mixed;');
----- INDICATES THAT THE BODY CONSISTS OF MORE THAN ONE PART
rc := utl_tcp.write_line(c, ' boundary="-----SECBOUND"');
----- SEPERATOR USED TO SEPERATE THE BODY PARTS
rc := utl_tcp.write_line(c, '');
----- INSERTS A BLANK LINE. PART OF THE MIME FORMAT AND NONE OF THEM
SHOULD BE REMOVED.
rc := utl_tcp.write_line(c, '-------SECBOUND');
rc := utl_tcp.write_line(c, 'Content-Type: text/plain');
----- 1ST BODY PART. EMAIL TEXT MESSAGE
rc := utl_tcp.write_line(c, 'Content-Transfer-Encoding: 7bit');
rc := utl_tcp.write_line(c, '');
rc := utl_tcp.write_line(c, msg_text);
----- TEXT OF EMAIL MESSAGE
rc := utl_tcp.write_line(c, '');
rc := utl_tcp.write_line(c, '-------SECBOUND');
rc := utl_tcp.write_line(c, 'Content-Type: text/plain;');
----- 2ND BODY PART.
rc := utl_tcp.write_line(c, ' name="Test.txt"');
rc := utl_tcp.write_line(c, 'Content-Transfer_Encoding: 8bit');
rc := utl_tcp.write_line(c, 'Content-Disposition: attachment;');
----- INDICATES THAT THIS IS AN ATTACHMENT
rc := utl_tcp.write_line(c, ' filename="Test.txt"');
----- SUGGESTED FILE NAME FOR ATTACHMENT
rc := utl_tcp.write_line(c, '');
rc := utl_tcp.write_line(c, v_output1);
rc := utl_tcp.write_line(c, '-------SECBOUND--');
rc := utl_tcp.write_line(c, '');
rc := utl_tcp.write_line(c, '.'); ----- EMAIL
MESSAGE BODY END
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
rc := utl_tcp.write_line(c, 'QUIT'); ----- ENDS EMAIL
TRANSACTION
dbms_output.put_line(utl_tcp.get_line(c, TRUE));
utl_tcp.close_connection(c); ----- CLOSE SMTP
PORT CONNECTION
EXCEPTION
when others then
raise_application_error(-20000, SQLERRM);
END;
/
o
o Same as above, but implemented using UTL_SMTP

DECLARE
v_From VARCHAR2(80) := 'oracle@mycompany.com';
v_Recipient VARCHAR2(80) := 'test@mycompany.com';
v_Subject VARCHAR2(80) := 'test subject';
v_Mail_Host VARCHAR2(30) := 'mail.mycompany.com';
v_Mail_Conn utl_smtp.Connection;
crlf VARCHAR2(2) := chr(13)||chr(10);
BEGIN
v_Mail_Conn := utl_smtp.Open_Connection(v_Mail_Host, 25);

utl_smtp.Helo(v_Mail_Conn, v_Mail_Host);

utl_smtp.Mail(v_Mail_Conn, v_From);

utl_smtp.Rcpt(v_Mail_Conn, v_Recipient);

utl_smtp.Data(v_Mail_Conn,
'Date: ' || to_char(sysdate, 'Dy, DD Mon YYYY hh24:mi:ss') || crlf
||
'From: ' || v_From || crlf ||
'Subject: '|| v_Subject || crlf ||
'To: ' || v_Recipient || crlf ||

'MIME-Version: 1.0'|| crlf || -- Use MIME mail standard


'Content-Type: multipart/mixed;'|| crlf ||
' boundary="-----SECBOUND"'|| crlf ||
crlf ||

'-------SECBOUND'|| crlf ||
'Content-Type: text/plain;'|| crlf ||
'Content-Transfer_Encoding: 7bit'|| crlf ||
crlf ||
'some message text'|| crlf || -- Message body
'more message text'|| crlf ||
crlf ||

'-------SECBOUND'|| crlf ||
'Content-Type: text/plain;'|| crlf ||
' name="excel.csv"'|| crlf ||
'Content-Transfer_Encoding: 8bit'|| crlf ||
'Content-Disposition: attachment;'|| crlf ||
' filename="excel.csv"'|| crlf ||
crlf ||
'CSV,file,attachement'|| crlf || -- Content of attachment
crlf ||

'-------SECBOUND--' -- End MIME mail


);

utl_smtp.Quit(v_mail_conn);
EXCEPTION
WHEN utl_smtp.Transient_Error OR utl_smtp.Permanent_Error then
raise_application_error(-20000, 'Unable to send mail: '||sqlerrm);
END;
/

o
o Mailmerge: Merge data from the table/view data sources into a custom template

/*************************** Create result temp table, test table and


context *************************/

create GLOBAL TEMPORARY table TEMP_MAIL_MERGE


(
PKID NUMBER ,
RESULT VARCHAR2(4000) )
On Commit Delete Rows
/

create table data_sources


(ID Number(10) primary key,
P2 Varchar2(10),
P3 Varchar2(50),
P4 DATE,
P5 NUMBER
)
/

Insert into DATA_SOURCES


(ID, P2, P3, P4, P5)
Values
(1, 'Claudiu', 'Ariton', TO_DATE('09/21/1976 00:00:00', 'MM/DD/YYYY
HH24:MI:SS'), 100000)
/
Commit
/

create or replace context mm_ctx using mail_merge


/

create or replace view data_sources_v as


select ID PKID, P2, P3, P4, P5,
SYS_CONTEXT('mm_ctx','param1',2000) param1 from
data_sources
/

/*************************** Create package head


*************************/

Create or Replace Package mail_merge as

/
************************************************************************
**************
*
* TITLE......: Mail Merge
* DESCRIPTION: Merge data from the data source into a custom template
*
* AUTHOR.....: Claudiu Ariton
* DATE.......: april 2004
*
* Modifications
*
************************************************************************
**************/

type Argv is table of varchar2(4000) index by binary_integer;


emptyargv argv;

procedure set_ctx( p_name in varchar2, p_value in varchar2,p_ctx in


varchar2 default 'mm_ctx' );

function mail_merge(
p_message in varchar2,
p_argv in argv ,
p_esc_char in varchar2 default '$') return varchar2;

Procedure generic_mail_merge(p_ttext varchar2, p_query in varchar2,


p_date_format in varchar2 default 'dd-MON-yyyy hh24:mi:ss',
p_bindid in number default null,p_list_val in argv default emptyargv);

end mail_merge;
/

/*************************** Create package body


*************************/

Create or Replace Package Body mail_merge as

--
------------------------------------------------------------------------
-----------
-- Set_ctx - set generic context
--
------------------------------------------------------------------------
-----------
procedure set_ctx( p_name in varchar2, p_value in varchar2,p_ctx in
varchar2 default 'mm_ctx' )
as

begin
dbms_session.set_context( p_ctx, p_name, p_value, USER);
end;

--
------------------------------------------------------------------------
-----------
-- mail_merge - Merge data from list of vaalues into a custom template
--
------------------------------------------------------------------------
-----------
function mail_merge(
p_message in varchar2,
p_argv in argv ,
p_esc_char in varchar2 default '$') return varchar2 is
--
l_message long := null;
l_str long := p_message;
l_idx number := 1;
l_ptr number := 1;
l_poz varchar2(10);
l_on number;
begin
if nvl( instr( p_message, p_esc_char ), 0 ) = 0 and
nvl( instr( p_message, '\' ), 0 ) = 0 then
return p_message;
end if;
loop

l_on:=0;
l_ptr := instr( l_str, p_esc_char );
exit when l_ptr = 0 or l_ptr is null;
l_message := l_message || substr( l_str, 1, l_ptr-1 );
l_str := substr( l_str, l_ptr+1 );

l_poz:=null;
while substr( l_str, 1, 1 ) in
('0','1','2','3','4','5','6','7','8','9') loop
l_poz:=l_poz||substr( l_str, 1, 1 );
l_str := substr( l_str, 2 );
l_on:=1;
end loop;

begin
l_message := l_message || p_argv(to_number(l_poz));
exception
when no_data_found then l_message := l_message || '<unknown>';
when others then null;
end;

if (substr( l_str,1,1 ) = p_esc_char) and (l_on=0) then


l_message := l_message || p_esc_char;
l_str := substr( l_str, 2 );
end if;

end loop;

l_str := l_message || l_str;


l_message := null;
loop
l_ptr := instr( l_str, '\' );
exit when l_ptr = 0 or l_ptr is null;
l_message := l_message || substr( l_str, 1, l_ptr-1 );
l_str := substr( l_str, l_ptr+1 );
if substr( l_str, 1, 1 ) = 'n' then
l_message := l_message || chr(10);
l_str := substr( l_str, 2 );
elsif substr( l_str, 1, 1 ) = 't' then
l_message := l_message || chr(9);
l_str := substr( l_str, 2 );
elsif substr( l_str, 1, 1 ) = '\' then
l_message := l_message || '\';
l_str := substr( l_str, 2 );
else
l_message := l_message || '\';
end if;
end loop;

return l_message || l_str;


end mail_merge;

--
------------------------------------------------------------------------
-----------
-- mail_merge - Merge data from a table/viiew into a custom template
--
------------------------------------------------------------------------
-----------
Procedure generic_mail_merge(p_ttext varchar2, p_query in varchar2,
p_date_format in varchar2 default 'dd-MON-yyyy hh24:mi:ss',
p_bindid in number default null,p_list_val in argv default emptyargv)
is
l_theCursor integer default dbms_sql.open_cursor;
l_defcolumn varchar2(4000);
l_columnValue argv;
l_status integer;
l_descTbl dbms_sql.desc_tab;
l_colCnt number;
v_result varchar2(4000);
v_pkid number;
begin
execute immediate
'alter session set
nls_date_format='''|| p_date_format ||'''';

dbms_sql.parse( l_theCursor, p_query, dbms_sql.native );

if p_bindid is not null then


dbms_sql.bind_variable( l_theCursor, ':1', p_bindid );
end if;

dbms_sql.describe_columns
( l_theCursor, l_colCnt, l_descTbl );

-- seteaza context
begin
for i in 1..p_list_val.count loop
set_ctx('param'||to_char(i),p_list_val(i));
end loop;
exception
when others then null;
end;

for i in 1 .. l_colCnt loop


dbms_sql.define_column
(l_theCursor, i, l_defcolumn, 4000);
end loop;

l_status := dbms_sql.execute(l_theCursor);
while ( dbms_sql.fetch_rows(l_theCursor) > 0 ) loop
for i in 1 .. l_colCnt loop
dbms_sql.column_value
( l_theCursor, i, l_columnValue(i) );
if upper(l_descTbl(i).col_name)='PKID' then
v_pkid:=to_number(l_columnValue(i));
end if;
end loop;

v_result:=mail_merge( p_ttext,l_columnValue);
insert into TEMP_MAIL_MERGE(pkid,result) values
(v_pkid,v_result);
end loop;
dbms_sql.close_cursor( l_theCursor);
execute immediate
'alter session set nls_date_format=''dd-MON-rr'' ';
exception
when others then
if dbms_sql.is_open(l_theCursor) then
dbms_sql.close_cursor( l_theCursor);
end if;
execute immediate
'alter session set nls_date_format=''dd-MON-rr'' ';
raise;
end;

end mail_merge;
/

/*************************** Test it *************************/


declare
v_list_val mail_merge.argv;
v_query varchar2(4000);
begin
v_list_val(1):='(040)-2313543';
v_query:= 'select * from data_sources_v vt where 1=1'||' AND
vt.pkid=:1';
mail_merge.generic_mail_merge('$2 $3 born on $4 has to pay $$$5. Call
us at $6. Thank you very much.',
v_query,'dd-MON-yyyy',1,v_list_val);
end;
/

select * from temp_mail_merge


/

o
o Read an Internet Web pages from PL/SQL

set pages 50000

select utl_http.request('http://www.orafaq.net/') from dual;


o
o Track DLL changes (create, drop, alter) within a schema
DROP TRIGGER audit_ddl_changes
/
DROP TABLE dll_audit_log
/

CREATE TABLE dll_audit_log (


stamp DATE,
username VARCHAR2(30),
osuser VARCHAR2(30),
machine VARCHAR2(30),
terminal VARCHAR2(30),
operation VARCHAR2(30),
objtype VARCHAR2(30),
objname VARCHAR2(30))
/

CREATE OR REPLACE TRIGGER audit_ddl_changes


AFTER create OR drop OR alter
ON scott.SCHEMA -- Change SCOTT to your schema name!!!
-- ON DATABASE
BEGIN
INSERT INTO dll_audit_log VALUES
(SYSDATE,
SYS_CONTEXT('USERENV', 'SESSION_USER'),
SYS_CONTEXT('USERENV', 'OS_USER'),
SYS_CONTEXT('USERENV', 'HOST'),
SYS_CONTEXT('USERENV', 'TERMINAL'),
ORA_SYSEVENT,
ORA_DICT_OBJ_TYPE,
ORA_DICT_OBJ_NAME
);
END;
/
show errors

-- Now, let's test it


CREATE TABLE my_test_table (col1 DATE)
/
DROP TABLE my_test_table
/
set pages 50000
SELECT * FROM dll_audit_log
/
o
o Count the number of rows in ALL tables for the current schema

set serveroutput on size 1000000

DECLARE
t_c1_tname user_tables.table_name%TYPE;
t_command varchar2(200);
t_cid integer;
t_total_records number(10);
stat integer;
row_count integer;
t_limit integer := 0; -- Only show tables with more rows
cursor c1 is select table_name from user_tables order by table_name;
BEGIN
t_limit := 0;
open c1;
loop
fetch c1 into t_c1_tname;
exit when c1%NOTFOUND;
t_command := 'SELECT COUNT(0) FROM '||t_c1_tname;
t_cid := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE(t_cid,t_command,dbms_sql.native);
DBMS_SQL.DEFINE_COLUMN(t_cid,1,t_total_records);
stat := DBMS_SQL.EXECUTE(t_cid);
row_count := DBMS_SQL.FETCH_ROWS(t_cid);
DBMS_SQL.COLUMN_VALUE(t_cid,1,t_total_records);
if t_total_records > t_limit then
DBMS_OUTPUT.PUT_LINE(rpad(t_c1_tname,55,' ')||
to_char(t_total_records,'99999999')||'
record(s)');

end if;
DBMS_SQL.CLOSE_CURSOR(t_cid);
end loop;
close c1;
END;
/
o
o List tables from schema with more than X rows

REM First of all create the following function - rowcount...

CREATE OR REPLACE FUNCTION rowcount(tname VARCHAR2) RETURN NUMBER IS


x NUMBER;
stmt VARCHAR2(200);
BEGIN
stmt := 'select count(*) from '||tname;
execute immediate stmt into x;
return x;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RETURN 0;
END;
/
SHOW ERRORS

REM Then write this query...

SELECT table_name, roucount(table_name) Records


FROM cat
WHERE roucount(table_name) >= 100;
/

o
o Replace all occurences of a substring with another substring
create or replace function strreplace(str varchar2, from_str varchar2,
to_str varchar2)
return varchar2
AS
str_temp varchar2(4000);
str_pos  number := instr(str, from_str);
BEGIN
str_temp := str;
while ( str_pos > 0 ) loop
str_temp := substr(str_temp, 0, str_pos-1) || to_str ||
substr(str_temp, str_pos + length(from_str));
str_pos  := instr(str_temp, from_str);
end loop;
return str_temp;
END;
/
show errors

-- Examples

select strreplace('This is a beautiful day!', 'beautiful', 'horrible')


from dual
/

select 'mv '||name||' '||strreplace(name, 'OLDSID', 'NEWSID')


from v$datafile
/
o
o Spell out numbers to words (handy for cheque printing)

CREATE OR REPLACE FUNCTION NUMBER_CONVERSION(NUM NUMBER) RETURN VARCHAR2


IS
A VARCHAR2(1000);
B VARCHAR2(20);
X NUMBER;
Y NUMBER := 1;
Z NUMBER;
LSIGN NUMBER;
NO NUMBER;
BEGIN
X:= INSTR(NUM, '.');
LSIGN := SIGN(NUM);
NO := ABS(NUM);
IF X = 0 THEN
SELECT TO_CHAR(TO_DATE(NO, 'J'), 'JSP') INTO A FROM DUAL;
ELSE
SELECT to_char(to_date(SUBSTR(NO, 1,
NVL(INSTR(NO, '.')-1, LENGTH(NO))),
'J'), 'JSP') INTO A FROM DUAL;
SELECT LENGTH(SUBSTR(NO, INSTR(NO, '.')+1)) INTO Z FROM DUAL;
A := A ||' POINT ';
WHILE Y< Z+1 LOOP
SELECT TO_CHAR(TO_DATE(SUBSTR(NO, (INSTR(NO, '.')+Y), 1), 'J'),
'JSP')
INTO B FROM DUAL;
A := A || B ||' ';
y :=y+1;
END LOOP;
END IF;
IF LSIGN = -1 THEN
RETURN 'NEGATIVE '||A;
ELSE
RETURN A;
END IF;
END;
/
show errors

-- Examples:
Select number_conversion(-3786.9899876) from dual;
Select number_conversion(7685.78788) from dual;
Select number_conversion(7678) from dual;
o
o Print cheque amounts in Indian Style

SET SERVEROUTPUT ON;

CREATE OR REPLACE PROCEDURE cheq(n NUMBER)


as
L NUMBER := 0;
OUTPUT VARCHAR2(2000) := '';
X VARCHAR2(2000) := '';
X1 VARCHAR2(2000) := '';
C1 VARCHAR2(2000) := '';
BEGIN
L := length(N);

IF N < 0 OR N > 999999999 THEN


DBMS_OUTPUT.PUT_LINE('INVALID AMOUNT');
else

if ( N = 0 ) THEN
X := 'ZERO ';
elsif ( N <= 99999 ) THEN
X := to_char(to_date(N,'J'),'JSP') || ' ';
else

if ( to_number(substr(N, L - 5 + 1)) = 0 ) then


X := '';
else
X := to_char(to_date(to_number(substr(N, L - 5 +
1)),'J'),'JSP') || ' ';
end if;

if ( L = 6 ) then
X1 := to_char(to_date(to_number(substr(N, 1, L -
5)),'J'),'JSP') || ' LAKH ';
else
if ( to_number(substr(N, L - 5 -1, 2)) = 0 ) then
X1 := '';
else
X1 := to_char(to_date(to_number(substr(N, L - 5 - 1,
2)),'J'),'JSP') || ' LAKH ';
end if;

if ( L >= 8 ) then
C1 := to_char(to_date(to_number(substr(N, 1, L-
7)),'J'),'JSP')||' CRORE ';
end if;
end if;
end if;

if ( N = 0 OR N = 1 ) THEN
DBMS_OUTPUT.PUT_LINE(N||' => '||X||'RUPEE ONLY');
else
DBMS_OUTPUT.PUT_LINE(N||' => '||C1||X1||X||'RUPEES ONLY');
end if;
end if;
END CHEQ;
/
show errors
o
o NYSIIS function (an improvement on SoundeX)

CREATE OR REPLACE FUNCTION NYSIIS (v_text IN VARCHAR2) RETURN VARCHAR2


is

v_sub varchar2(300);
v_length number(10);
v_textin varchar2(300);
v_key varchar2(1);

begin
SELECT UPPER ( v_text ) into v_textin from dual;
dbms_output.put_line( 'Entered surname :' || v_textin);

dbms_output.put_line( ' [1] remove all S and Z chars from the end of
the surname ' );
LOOP
SELECT SUBSTR ( v_textin , (length (v_textin))) into v_sub from
dual;
dbms_output.put_line('Last letter :' || v_sub);
if v_sub = 'S' OR v_sub = 'Z' THEN
SELECT SUBSTR ( v_textin , 1 , (length (v_textin) -1 )) into
v_textin from dual ;
dbms_output.put_line('As last letter s or z drop last letter
giving :' || v_textin || ' and check new last letter');
else
dbms_output.put_line('Last letter not s or z completed step 1');
EXIT;
end if;
END LOOP;
dbms_output.put_line('Step 1 completed giving :' || v_textin );

dbms_output.put_line( ' [2] transcode initial strings MAC => MC and


PF => F and PH => F ' );
if SUBSTR ( v_textin , 1 , 3 ) = 'MAC' THEN
SELECT 'MC' || SUBSTR ( v_textin , 4 ) into v_textin from dual ;
elsif SUBSTR ( v_textin , 1 , 2 ) = 'PH' THEN
SELECT 'F' || SUBSTR ( v_textin , 3 ) into v_textin from dual ;
elsif SUBSTR ( v_textin , 1 , 2 ) = 'PF' THEN
SELECT 'F' || SUBSTR ( v_textin , 3 ) into v_textin from dual ;
end if;

dbms_output.put_line('Step 2 completed giving :' || v_textin );

dbms_output.put_line( ' [3] transcode trailing strings IX => IC and


EX => EC and YE,EE,IE => Y and NT,ND => D ' );
SELECT SUBSTR ( v_textin , (length (v_textin) - 1)) into v_sub from
dual;
dbms_output.put_line('Last 2 letters :' || v_sub);
if UPPER(v_sub) in ('IX','EX','YE','EE','IE','NT','ND') THEN
SELECT decode ( UPPER(v_sub) , 'IX','IC',
'EX','EC',
'YE','Y',
'EE','Y',
'IE','Y',
'NT','D',
'ND','D', NULL ) into v_sub from dual ;
SELECT SUBSTR ( v_textin , 1, (length (v_textin) - 1)) || v_sub
into v_textin from dual ;
end if;
dbms_output.put_line('Step 3 completed giving :' || v_textin );

dbms_output.put_line( '[4] transcode EV to EF if not at start of


name');
SELECT SUBSTR(v_textin, 1 , 1) || REPLACE( SUBSTR(v_textin,
2),'EV','EF') into v_textin from dual ;
dbms_output.put_line('Step 4 completed giving :' || v_textin );

dbms_output.put_line( '[5] first character of name as first


character of key continue with remaining characters');
SELECT SUBSTR ( v_textin , 1 , 1) into v_key from dual;
SELECT SUBSTR ( v_textin , 2 ) into v_textin from dual;
dbms_output.put_line('Step 5 completed first character of key :' ||
v_key );

dbms_output.put_line( '[6] replace all vowels with A');


SELECT TRANSLATE( v_textin,'AEIOU','AAAAA') into v_textin from
dual ;
dbms_output.put_line('Step 6 completed giving :' || v_textin );

dbms_output.put_line( '[7] remove any W that follows a vowel');


SELECT REPLACE( v_textin,'AW','A') into v_textin from dual ;
dbms_output.put_line('Step 7 completed giving :' || v_textin );

dbms_output.put_line( '[8] transcode GHT to GT ');


SELECT REPLACE( v_textin,'GHT','GT') into v_textin from dual ;
dbms_output.put_line('Step 8 completed giving :' || v_textin );

dbms_output.put_line( '[9] transcode DG to G ');


SELECT REPLACE( v_textin,'DG','G') into v_textin from dual ;
dbms_output.put_line('Step 9 completed giving :' || v_textin );
dbms_output.put_line( '[10] transcode PH to F ');
SELECT REPLACE( v_textin,'PH','F') into v_textin from dual ;
dbms_output.put_line('Step 10 completed giving :' || v_textin );

dbms_output.put_line( '[11] if not first character, eliminate all H


preceded or followed by a vowel ');
if length(v_textin) > 1 THEN
SELECT SUBSTR(v_textin, 1 , 1) || REPLACE( SUBSTR(v_textin,
2),'HA','A') into v_textin from dual ;
SELECT SUBSTR(v_textin, 1 , 1) || REPLACE( SUBSTR(v_textin,
2),'AH','A') into v_textin from dual ;
end if;
dbms_output.put_line('Step 11 completed giving :' || v_textin );

dbms_output.put_line( '[12] change KN to N, else K to C ');


SELECT REPLACE( v_textin,'KN','N') into v_textin from dual ;
SELECT TRANSLATE( v_textin,'K','C') into v_textin from dual ;
dbms_output.put_line( 'Step 12 completed giving :' || v_textin );

dbms_output.put_line( '[13] transcode M to N if not at start of


name');
SELECT SUBSTR(v_textin, 1 , 1) || REPLACE( SUBSTR(v_textin,
2),'M','N') into v_textin from dual ;
dbms_output.put_line('Step 13 completed giving :' || v_textin );

dbms_output.put_line( '[14] transcode Q to G if not at start of


name');
SELECT SUBSTR(v_textin, 1 , 1) || REPLACE( SUBSTR(v_textin,
2),'Q','G') into v_textin from dual ;
dbms_output.put_line('Step 14 completed giving :' || v_textin );

dbms_output.put_line( '[15] transcode transcode SH to S ');


SELECT REPLACE( v_textin,'SH','S') into v_textin from dual ;
dbms_output.put_line('Step 15 completed giving :' || v_textin );

dbms_output.put_line( '[16] transcode transcode SCH to S ');


SELECT REPLACE( v_textin,'SCH','S') into v_textin from dual ;
dbms_output.put_line('Step 16 completed giving :' || v_textin );

dbms_output.put_line( '[17] transcode transcode YW to Y ');


SELECT REPLACE( v_textin,'YW','Y') into v_textin from dual ;
dbms_output.put_line('Step 17 completed giving :' || v_textin );

dbms_output.put_line( '[18] if not first or last character, change Y


to A');
if length(v_textin) > 2 THEN
SELECT SUBSTR(v_textin, 1 , 1) || REPLACE( SUBSTR(v_textin, 2,
length(v_textin) - 2),'Y','A') || SUBSTR ( v_textin , (length
(v_textin))) into v_textin from dual ;
end if;
dbms_output.put_line('Step 18 completed giving :' || v_textin );

dbms_output.put_line( '[19] transcode transcode WR to R ');


SELECT REPLACE( v_textin,'WR','R') into v_textin from dual ;
dbms_output.put_line('Step 19 completed giving :' || v_textin );
dbms_output.put_line( '[20] if not first character, change Z to S
');
if length(v_textin) > 1 THEN
SELECT SUBSTR(v_textin, 1 , 1) || REPLACE( SUBSTR(v_textin,
2),'Z','S') into v_textin from dual ;
end if;
dbms_output.put_line('Step 20 completed giving :' || v_textin );

dbms_output.put_line( '[21] transcode terminal AY to Y');


if length(v_textin) > 1 THEN
SELECT SUBSTR ( v_textin , (length (v_textin) - 1)) into v_sub
from dual;
if v_sub = 'AY' THEN
SELECT SUBSTR(v_textin, 1 , length(v_textin) - 2 ) || 'Y' into
v_textin from dual ;
end if;
end if;
dbms_output.put_line('Step 21 completed giving :' || v_textin );

dbms_output.put_line( '[22] remove trailing vowels');


LOOP
SELECT SUBSTR ( v_textin , (length (v_textin))) into v_sub from
dual;
dbms_output.put_line('Last letter :' || v_sub);
if v_sub = 'A' THEN
SELECT SUBSTR ( v_textin , 1 , (length (v_textin) -1 )) into
v_textin from dual ;
dbms_output.put_line('As last letter A drop last letter
giving :' || v_textin || ' and check new last letter');
else
dbms_output.put_line('Last letter not A step 22 completed');
EXIT;
end if;
END LOOP;
dbms_output.put_line('Step 22 completed giving :' || v_textin );

dbms_output.put_line( '[23] collapse all strings of repeated


characters');
if length(v_textin) > 2 THEN
LOOP
SELECT v_textin into v_sub from dual;
SELECT REPLACE( v_textin,'AA','A') into v_textin from dual ;
SELECT REPLACE( v_textin,'BB','B') into v_textin from dual ;
SELECT REPLACE( v_textin,'CC','C') into v_textin from dual ;
SELECT REPLACE( v_textin,'DD','D') into v_textin from dual ;
SELECT REPLACE( v_textin,'FF','F') into v_textin from dual ;
SELECT REPLACE( v_textin,'GG','G') into v_textin from dual ;
SELECT REPLACE( v_textin,'HH','H') into v_textin from dual ;
SELECT REPLACE( v_textin,'JJ','J') into v_textin from dual ;
SELECT REPLACE( v_textin,'LL','L') into v_textin from dual ;
SELECT REPLACE( v_textin,'NN','N') into v_textin from dual ;
SELECT REPLACE( v_textin,'PP','P') into v_textin from dual ;
SELECT REPLACE( v_textin,'RR','R') into v_textin from dual ;
SELECT REPLACE( v_textin,'SS','S') into v_textin from dual ;
SELECT REPLACE( v_textin,'TT','T') into v_textin from dual ;
SELECT REPLACE( v_textin,'VV','V') into v_textin from dual ;
SELECT REPLACE( v_textin,'WW','W') into v_textin from dual ;
SELECT REPLACE( v_textin,'XX','X') into v_textin from dual ;
SELECT REPLACE( v_textin,'ZZ','Z') into v_textin from dual ;
dbms_output.put_line( v_textin );
if v_sub = v_textin OR v_sub is NULL THEN
EXIT;
end if;
END LOOP;
end if;

dbms_output.put_line('Step 23 completed giving :' || v_textin );

dbms_output.put_line('[24] put back in first letter and return' );


SELECT v_key || v_textin into v_textin from dual;

RETURN( v_textin );

end NYSIIS;
/

o
o Converts a string of text into seperate soundex values

rem
------------------------------------------------------------------------
--
rem Filename: M_SOUNDEX.SQL
rem Purpose: Converts a string of text into seperate soundex values.
Treating
rem it as space deliminated words. Useful when searching text
strings for a sounds like.
rem
rem Notes: USEAGE "select M_SOUNDEX('the cat sat on the mat') from
dual;"
rem M_SOUNDEX('THECATSATONTHEMAT')
rem -----------------------------------
rem T000 C300 S300 O500 T000 M300
rem
rem
rem select M_SOUNDEX('the cat sat on the mat') from dual where
rem M_SOUNDEX('the cat sat on the mat') like ('%' || SOUNDEX('cot') ||
'%');
rem
rem Date: 01-Mar-2005
rem Author: Trevor Fairhurst, trevgf@yahoo.com
rem
------------------------------------------------------------------------
--

CREATE OR REPLACE FUNCTION "M_SOUNDEX" (v_text IN VARCHAR2) RETURN


VARCHAR2 is
v_number number(10);
v_textin varchar2(4000);
v_textout varchar2(4000);
begin
SELECT UPPER (TRIM( v_text )) into v_textin from dual;
dbms_output.put_line( 'Entered text :' || v_textin);
SELECT '' into v_textout from dual;
LOOP
SELECT instr( v_textin , ' ' , 1 , 1 ) into v_number from dual;
if v_number = 0 THEN
SELECT v_textout || ' ' || SOUNDEX(v_textin) into v_textout from
dual ;
EXIT;
else
SELECT v_textout || ' ' || SOUNDEX(substr (v_textin , 0 ,
v_number - 1 )) into v_textout from dual;
SELECT substr (v_textin , v_number + 1 ) into v_textin from
dual;
end if;
END LOOP;
RETURN( v_textout );
end M_SOUNDEX;
/

o
o Package to generate HTML-type documentation for Oracle objects
o 1) REATE OR REPLACE PACKAGE documentator
o AS
o
o /* **************************************
o Set Dependencies
o * **************************************/
o PROCEDURE getRefObjects(
o IN_OBJECT VARCHAR2,
o IN_OBJECT_TYPE VARCHAR2);
o
o /* **************************************
o Get Table Dependencies
o * **************************************/
o PROCEDURE getTblRefObjects(
o IN_OBJECT VARCHAR2,
o IN_OBJECT_TYPE VARCHAR2);
o
o /* **************************************
o Get common object details
o * **************************************/
o PROCEDURE getTopObjectDetails(
o IN_OBJECT VARCHAR2,
o IN_OBJECT_TYPE VARCHAR2);
o
o /* **************************************
o Set Doc Header
o * **************************************/
o PROCEDURE setDocHeader(
o IN_OBJECT VARCHAR2,
o IN_OBJECT_TYPE VARCHAR2);
o
o /* **************************************
o Get all privileges
o * **************************************/
o PROCEDURE getUnitAllPrivs(IN_OBJECT VARCHAR2);
o
o /* **************************************
o Initialize variables
o * **************************************/
o PROCEDURE initDoc(IN_OBJECT VARCHAR2);
o
o /* **************************************
o Finish documents preparation and send emails
o * **************************************/
o PROCEDURE finishDoc(
o IN_OBJECT VARCHAR2,
o IN_FILE_NAME VARCHAR2,
o IN_EMAIL VARCHAR2);
o
o /* **************************************
o Prepare document for Procedure/Function/Package
o * **************************************/
o PROCEDURE getPkgDoc(
o IN_OBJECT VARCHAR2,
o IN_FILE_NAME VARCHAR2);
o
o /* **************************************
o Prepare document for Table
o * **************************************/
o PROCEDURE getTblDoc(
o IN_OBJECT VARCHAR2,
o IN_OBJECT_TYPE VARCHAR2,
o IN_FILE_NAME VARCHAR2);
o
o /* **************************************
o Main procedure to generate document
o * **************************************/
o PROCEDURE generateDocInfo(
o IN_OBJECT VARCHAR2,
o IN_FILE_NAME VARCHAR2,
o IN_EMAIL VARCHAR2);
o
o /* **************************************
o Main procedure to generate document for all objects for the given object type
o * **************************************/
o PROCEDURE generateDocInfoByType(
o IN_OBJECT_TYPE VARCHAR2,
o IN_FILE_NAME VARCHAR2,
o IN_EMAIL VARCHAR2);
o
o /* **************************************
o Main procedure to generate document for all objects for the given object types
o * **************************************/
o PROCEDURE generateDocInfoByTypes(
o IN_OBJECT_TYPE VARCHAR2,
o IN_FILE_NAME VARCHAR2,
o IN_EMAIL VARCHAR2);
o
o /* **************************************
o Get list of procedures/functions with arguments
o * **************************************/
o PROCEDURE getArgumentsList(IN_OBJECT VARCHAR2);
o
o /* **************************************
o Get trigger columns
o * **************************************/
o PROCEDURE getTriggerColumns(IN_OBJECT VARCHAR2);
o
o /* **************************************
o Get trigger details
o * **************************************/
o PROCEDURE getTriggerDetails(IN_OBJECT VARCHAR2);
o
o /* **************************************
o Get PL/SQL unit Source
o * **************************************/
o PROCEDURE getUnitSource(
o IN_OBJECT VARCHAR2,
o IN_OBJECT_TYPE VARCHAR2);
o
o /* **************************************
o Get type status
o * **************************************/
o PROCEDURE getTypeStatus(
o IN_OBJECT VARCHAR2,
o IN_OBJECT_TYPE VARCHAR2);
o
o /* **************************************
o Get type attributes
o * **************************************/
o PROCEDURE getTypeAttrs(IN_OBJECT VARCHAR2);
o
o /* **************************************
o Get list of type methods
o * **************************************/
o PROCEDURE getTypeMethods(IN_OBJECT VARCHAR2);
o
o /* **************************************
o Get table columns
o * **************************************/
o PROCEDURE getTblCols(IN_OBJECT VARCHAR2);
o
o /* **************************************
o Get Table Constraints
o * **************************************/
o PROCEDURE getTblConstraints(IN_OBJECT VARCHAR2);
o
o /* **************************************
o Get all Table privileges
o * **************************************/
o PROCEDURE getTblPrivs(IN_OBJECT VARCHAR2);
o
o /* **************************************
o Get Table Indexes
o * **************************************/
o PROCEDURE getTblIndexes(IN_OBJECT VARCHAR2);
o
o PROCEDURE sendEmailAttachments(
o IN_FROM_EMAIL VARCHAR2,
o IN_TO_EMAILS VARCHAR2,
o IN_CC_EMAILS VARCHAR2,
o IN_SUBJ VARCHAR2,
o IN_TEXT CLOB,
o IN_MIME_TYPE VARCHAR2 DEFAULT 'text/plain',
o IN_FILENAME_1 VARCHAR2,
o IN_CONTENT_1 CLOB,
o IN_FILENAME_2 VARCHAR2 DEFAULT NULL,
o IN_CONTENT_2 CLOB DEFAULT NULL,
o IN_FILENAME_3 VARCHAR2 DEFAULT NULL,
o IN_CONTENT_3 CLOB DEFAULT NULL);
o
o END documentator;
o /
o show error

2) /*
************************************************************************
************
Package to generate HTML-type documentation for Oracle objects.
Supported objects:
Package and Package body
Function
Procedure
Trigger
User Type
Table
View

Prerequisites:
UTL_SMTP package installed in DB
Active/installed option Oracle JServer/JVM

To call:
a) documentator.generateDocInfo( IN_OBJECT, IN_FILE_NAME,
IN_EMAIL)
where
IN_OBJECT - object name
IN_FILE_NAME - output file name which will be used as base
for output file names.
IN_EMAIL - TO email

Example:
documentator.generateDocInfo('EMP', 'emp.htm',
'email@your_domain.com');

will prepare doc for the object EMP in a current schema

b) documentator.generateDocInfoByType( IN_OBJECT_TYPE,
IN_FILE_NAME, IN_EMAIL)
where
IN_OBJECT_TYPE - object type (ex. PACKAGE, etc..) Package will
generate documentation for all objects for the given type
IN_FILE_NAME - output file name which will be used as base
for output file names.
IN_EMAIL - TO email
Example:
documentator.generateDocInfoByType('TABLE', 'table.htm',
'email@your_domain.com');

will prepare doc for all tables in a current schema

c) documentator.generateDocInfoByTypes(<TYPES>, IN_FILE_NAME,
IN_EMAIL)
where
IN_OBJECT_TYPE - objects types (PACKAGE, PROCEDURE, etc..)
comma separated.
IN_FILE_NAME - output file name which will be used as base
for output file names.
IN_EMAIL - TO email
example:
exec documentator.generateDocInfoByTypes('FUNCTION, PROCEDURE,
PACKAGE, TRIGGER, TYPE, TABLE, VIEW', 'objects.htm',
'email@your_domain.com');

will prepare doc for all passed object types in a current schema

As result, package will generate and send by email 3 attachments


with documentation.
To view documentation, save attachments in any folder and open
file with name passed as parameter IN_FILE_NAME.

Note:
Oracle 8i view user_constraints does not contain column
INDEX_NAME, so it populates with ' '

Created:
Oleg Savkin toyway@yahoo.com 12/05/2005
Modified:
Oleg Savkin toyway@yahoo.com 01/24/2006 Modified to use in Oracle
8i

*
************************************************************************
************/
CREATE OR REPLACE PACKAGE BODY documentator
AS

docBody CLOB;
main_page CLOB;
ref_page CLOB;

gv_schema VARCHAR2(30);
gv_db_name VARCHAR2(30);
gv_user VARCHAR2(30);
gv_version VARCHAR2(255);

-- array to keep types


TYPE type_array IS TABLE OF VARCHAR2(100) INDEX BY BINARY_INTEGER;
type_index type_array;

-- array to keep emails


TYPE email_array IS TABLE OF VARCHAR2(100) INDEX BY BINARY_INTEGER;
email_index email_array;

/******************************************
Private email procedures/functions
******************************************/

/* ******************************************************************
-- Private procedure
-- procedure to parse text with Types
-- and store them in type_index type
* ******************************************************************/
PROCEDURE splitType(IN_TEXT VARCHAR2)
IS
lv_in_text VARCHAR2(4000);
lv_out_text VARCHAR2(4000);
ln_divider VARCHAR2(1) := ',';
ln_pos PLS_INTEGER;
ln_word_cnt PLS_INTEGER;
BEGIN

type_index.DELETE;

IF IN_TEXT IS NOT NULL


THEN
lv_in_text := TRIM(IN_TEXT);
-- correct input text
IF SUBSTR( TRIM(lv_in_text), LENGTH(TRIM(lv_in_text)), 1) <>
ln_divider
THEN
lv_in_text := lv_in_text||ln_divider;
END IF;
ln_word_cnt := 0;
LOOP
ln_pos := INSTR(lv_in_text, ',');
EXIT WHEN (NVL(ln_pos, 0) = 0);
ln_word_cnt := ln_word_cnt+1;
type_index(ln_word_cnt) := TRIM(SUBSTR(lv_in_text, 1, ln_pos
- 1));
lv_in_text := SUBSTR(lv_in_text, ln_pos + 1);
END LOOP;
ELSE
type_index(1) := '';
END IF;
END splitType;

/* ******************************************************************
-- Private procedure
-- procedure to parse text with Email IDs
-- and store them in email_index type
* ******************************************************************/
PROCEDURE splitEmail(IN_TEXT VARCHAR2)
IS
lv_in_text VARCHAR2(4000);
lv_out_text VARCHAR2(4000);
ln_divider VARCHAR2(1) := ',';
ln_pos PLS_INTEGER;
ln_word_cnt PLS_INTEGER;
BEGIN

email_index.DELETE;

IF IN_TEXT IS NOT NULL


THEN
lv_in_text := TRIM(IN_TEXT);
-- correct input text
IF SUBSTR( TRIM(lv_in_text), LENGTH(TRIM(lv_in_text)), 1) <>
ln_divider
THEN
lv_in_text := lv_in_text||ln_divider;
END IF;
ln_word_cnt := 0;
LOOP
ln_pos := INSTR(lv_in_text, ',');
EXIT WHEN (NVL(ln_pos, 0) = 0);
ln_word_cnt := ln_word_cnt+1;
email_index(ln_word_cnt) := TRIM(SUBSTR(lv_in_text, 1, ln_pos
- 1));
lv_in_text := SUBSTR(lv_in_text, ln_pos + 1);
END LOOP;
ELSE
email_index(1) := '';
END IF;

END splitEmail;

/*
************************************************************************
****
Write email body in email
*
************************************************************************
****/
PROCEDURE setEmailBody(
IN_CONNECTION IN OUT NOCOPY UTL_SMTP.CONNECTION,
IN_TEXT CLOB)
AS

ln_length PLS_INTEGER;
ln_offset PLS_INTEGER;
lv_text VARCHAR2(32767);
ln_amount BINARY_INTEGER := 32767;

ln_total number;

BEGIN
ln_length := dbms_lob.getlength( IN_TEXT );
ln_offset := 1;
LOOP
BEGIN
DBMS_LOB.READ(IN_TEXT, ln_amount, ln_offset, lv_text);
EXCEPTION
WHEN no_data_found
THEN
EXIT;
END;

EXIT WHEN lv_text IS NULL OR ln_amount = 0;

utl_smtp.write_data(IN_CONNECTION, lv_text);
ln_offset := ln_offset + ln_amount;

END LOOP;

EXCEPTION
WHEN OTHERS
THEN
RAISE;
END setEmailBody;

/*
************************************************************************
****
start email with attachments
*
************************************************************************
****/
FUNCTION setBeginAttachEmail(
IN_FROM_EMAIL VARCHAR2,
IN_TO_EMAILS VARCHAR2,
IN_CC_EMAILS VARCHAR2,
IN_SUBJ VARCHAR2,
IN_TEXT CLOB,
IN_MIME_TYPE VARCHAR2 DEFAULT 'text/plain')
RETURN UTL_SMTP.CONNECTION
AS
conn_ UTL_SMTP.CONNECTION; -- Customize the SMTP host, port and
your domain name below.
smtp_host VARCHAR2(256) := 'localhost';
smtp_port PLS_INTEGER := 25;
lv_sender VARCHAR2(2000);
lv_to_email VARCHAR2(2000);
lv_cc_email VARCHAR2(2000);
lv_subj VARCHAR2(500);
lv_msg VARCHAR2(32767);
boundary CONSTANT VARCHAR2(256) :=
'CES.Boundary.DACA587499938898';

BEGIN

-- Set up connection
conn_:= utl_smtp.open_connection( smtp_host, smtp_port );
utl_smtp.helo( conn_, smtp_host );

IF IN_FROM_EMAIL IS NULL
THEN
lv_sender := 'documentator@your_domain.com';
ELSE
lv_sender := IN_FROM_EMAIL;
END IF;

-- get email requisites


lv_sender := IN_FROM_EMAIL;
lv_to_email := IN_TO_EMAILS;
lv_cc_email := IN_CC_EMAILS;

-- prepare Email FROM:


utl_smtp.mail( conn_, lv_sender);

-- prepare Email TO:


splitEmail(lv_to_email);
FOR I IN email_index.FIRST..email_index.LAST
LOOP
utl_smtp.rcpt( conn_, email_index(I) );
END LOOP;

-- prepare Email CC:


IF lv_cc_email IS NOT NULL
THEN
splitEmail(lv_cc_email);
FOR I IN email_index.FIRST..email_index.LAST
LOOP
utl_smtp.rcpt( conn_, email_index(I) );
END LOOP;
END IF;

utl_smtp.open_data(conn_);

--making a message buffer


lv_msg:= 'From: ' || lv_sender || utl_tcp.CRLF ||
'Subject: ' || IN_SUBJ || utl_tcp.CRLF ||
'To: ' || lv_to_email || utl_tcp.CRLF ||
'Cc: ' || lv_cc_email || utl_tcp.CRLF;

lv_msg := lv_msg || 'Mime-Version: 1.0' || utl_tcp.CRLF ||


'Content-Type: multipart/mixed; boundary="' || boundary || '"'
||
utl_tcp.CRLF || utl_tcp.CRLF ||
'This is a Mime message, which your current mail reader may
not' || utl_tcp.CRLF ||
'understand. Parts of the message will appear as text. If the
remainder' || utl_tcp.CRLF ||
'appears as random characters in the message body, instead of
as' || utl_tcp.CRLF ||
'attachments, then you''ll have to extract these parts and
decode them' || utl_tcp.CRLF ||
'manually.' || utl_tcp.CRLF || utl_tcp.CRLF;
utl_smtp.write_data(conn_, lv_msg);

IF IN_TEXT IS NOT NULL


THEN
----------------------------------------
-- Send the main message text
----------------------------------------
-- mime header
utl_smtp.write_data(conn_, '--' || boundary || utl_tcp.CRLF);
utl_smtp.write_data(conn_, 'Content-Type: '||IN_MIME_TYPE||
utl_tcp.CRLF);
utl_smtp.write_data(conn_, utl_tcp.CRLF);
setEmailBody(conn_, IN_TEXT);
utl_smtp.write_data(conn_, utl_tcp.CRLF);
END IF;

RETURN conn_;

EXCEPTION
WHEN others
THEN
RAISE;
END setBeginAttachEmail;
/*
************************************************************************
****
End email session
*
************************************************************************
****/
PROCEDURE setEndEmail( IN_CONNECTION IN OUT NOCOPY
UTL_SMTP.CONNECTION)
AS
BEGIN
-- closing connection
utl_smtp.close_data( IN_CONNECTION );
utl_smtp.quit( IN_CONNECTION );
EXCEPTION
WHEN OTHERS
THEN
RAISE;
END setEndEmail;

/* **************************************
Get common object details
* **************************************/
PROCEDURE getTopObjectDetails(
IN_OBJECT VARCHAR2,
IN_OBJECT_TYPE VARCHAR2)
IS
lv_created VARCHAR2(30);
lv_last_ddl_time VARCHAR2(30);
lv_status VARCHAR2(30);
BEGIN

-- page header
DBMS_LOB.WRITE(docBody, LENGTH( '<HTML>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<HTML>'||CHR(10));

DBMS_LOB.WRITE(docBody, LENGTH( '<FONT FACE="ARIAL" SIZE=2>'||


CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<FONT FACE="ARIAL"
SIZE=2>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TABLE WIDTH=100%>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TABLE WIDTH=100%>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD
BGCOLOR=#110000></FONT></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)
+1, '<TD BGCOLOR=#110000></FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TABLE>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TABLE>'||CHR(10));

DBMS_LOB.WRITE(docBody, LENGTH( '<TABLE WIDTH=100% CELLPADDING=3


BORDER=2>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TABLE
WIDTH=100% CELLPADDING=3 BORDER=2>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=#FF7F00><B><FONT
FACE="ARIAL" SIZE=5><A NAME="'||IN_OBJECT_TYPE||gv_schema||'.'||
IN_OBJECT||'">'||INITCAP(IN_OBJECT_TYPE)||' '||gv_schema||'.'||
IN_OBJECT||'</A></FONT></B></TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD BGCOLOR=#FF7F00><B><FONT
FACE="ARIAL" SIZE=5><A NAME="'||IN_OBJECT_TYPE||gv_schema||'.'||
IN_OBJECT||'">'||INITCAP(IN_OBJECT_TYPE)||' '||gv_schema||'.'||
IN_OBJECT||'</A></FONT></B></TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TABLE>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TABLE>'||CHR(10));

-- Common DB Info and Report DateTime


DBMS_LOB.WRITE(docBody, LENGTH( '<TABLE WIDTH=100% CELLPADDING=2
BORDER=2>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TABLE
WIDTH=100% CELLPADDING=2 BORDER=2>'||CHR(10));

DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
VALIGN=top WIDTH=100><B><FONT FACE="ARIAL" SIZE=2>Object
Type</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TD
BGCOLOR=LEMONCHIFFON VALIGN=top WIDTH=100><B><FONT FACE="ARIAL"
SIZE=2>Object Type</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||IN_OBJECT_TYPE||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT FACE="ARIAL"
SIZE=2>'||IN_OBJECT_TYPE||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));

DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
VALIGN=top WIDTH=100><B><FONT FACE="ARIAL"
SIZE=2>Datasource</FONT></B></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD BGCOLOR=LEMONCHIFFON VALIGN=top
WIDTH=100><B><FONT FACE="ARIAL" SIZE=2>Datasource</FONT></B></TD>'||
CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||gv_db_name||' ( Oracle '||gv_version||' )
</FONT></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TD
VALIGN=top><FONT FACE="ARIAL" SIZE=2>'||gv_db_name||' ( Oracle '||
gv_version||' )</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));

DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
VALIGN=top WIDTH=100><B><FONT FACE="ARIAL"
SIZE=2>Login</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)
+1, '<TD BGCOLOR=LEMONCHIFFON VALIGN=top WIDTH=100><B><FONT FACE="ARIAL"
SIZE=2>Login</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||gv_user||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT FACE="ARIAL"
SIZE=2>'||gv_user||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
VALIGN=top WIDTH=100><B><FONT FACE="ARIAL" SIZE=2>Report
Date</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TD
BGCOLOR=LEMONCHIFFON VALIGN=top WIDTH=100><B><FONT FACE="ARIAL"
SIZE=2>Report Date</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||TO_CHAR(sysdate, 'MM/DD/YYYY
HH24:MI:SS')||'</FONT></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1,
'<TD VALIGN=top><FONT FACE="ARIAL" SIZE=2>'||TO_CHAR(sysdate,
'MM/DD/YYYY HH24:MI:SS')||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));

DBMS_LOB.WRITE(docBody, LENGTH( '</TABLE>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '</TABLE>'||CHR(10));

DBMS_LOB.WRITE(docBody, LENGTH( '</HTML>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '</HTML>'||CHR(10));

END getTopObjectDetails;

/* **************************************
Set Dependencies
* **************************************/
PROCEDURE getRefObjects(
IN_OBJECT VARCHAR2,
IN_OBJECT_TYPE VARCHAR2)
IS
is_reference BOOLEAN;
BEGIN

DBMS_LOB.WRITE(docBody, LENGTH( CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, CHR(10) );
-- Header
DBMS_LOB.WRITE(docBody, LENGTH( '<TABLE WIDTH=100% CELLPADDING=3
BORDER=2>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TABLE
WIDTH=100% CELLPADDING=3 BORDER=2>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=#ccccff><FONT
FACE="ARIAL" SIZE=4>Dependencies</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD BGCOLOR=#ccccff><FONT FACE="ARIAL"
SIZE=4>Dependencies</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TABLE>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TABLE>'||CHR(10));

-- ************************************************
-- get Referencing
DBMS_LOB.WRITE(docBody, LENGTH( '<TABLE WIDTH=100% CELLPADDING=3
BORDER=2>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TABLE
WIDTH=100% CELLPADDING=3 BORDER=2>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=#ccccff><FONT
FACE="ARIAL" SIZE=2>Referencing Objects</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD BGCOLOR=#ccccff><FONT FACE="ARIAL"
SIZE=2>Referencing Objects</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TABLE>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TABLE>'||CHR(10));

DBMS_LOB.WRITE(docBody, LENGTH( '<TABLE WIDTH=100% CELLPADDING=2


BORDER=2>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TABLE
WIDTH=100% CELLPADDING=2 BORDER=2>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=200><B><FONT FACE="ARIAL" SIZE=2>Object
Name</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TD
BGCOLOR=LEMONCHIFFON ALIGN=CENTER VALIGN=top WIDTH=200><B><FONT
FACE="ARIAL" SIZE=2>Object Name</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=200><B><FONT FACE="ARIAL" SIZE=2>Object
Type</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TD
BGCOLOR=LEMONCHIFFON ALIGN=CENTER VALIGN=top WIDTH=200><B><FONT
FACE="ARIAL" SIZE=2>Object Type</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));

is_reference := FALSE;

FOR lrec IN (
SELECT
TRIM(referenced_owner||'.'||name) as object_name,
TRIM(type) as object_type
FROM ALL_DEPENDENCIES
WHERE referenced_name = IN_OBJECT
AND name <> IN_OBJECT
UNION ALL
SELECT owner||'.'||synonym_name as object_name,
'SYNONYM' as object_type
FROM all_synonyms
WHERE table_name = IN_OBJECT
ORDER BY 2)
LOOP
is_reference := TRUE;
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||lrec.object_name||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT FACE="ARIAL"
SIZE=2>'||lrec.object_name||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||lrec.object_type||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT FACE="ARIAL"
SIZE=2>'||lrec.object_type||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));
END LOOP;

IF NOT is_reference
THEN
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>NOT FOUND</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT FACE="ARIAL"
SIZE=2>NOT FOUND</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));
END IF;

DBMS_LOB.WRITE(docBody, LENGTH( '</TABLE>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '</TABLE>'||CHR(10));
is_reference := FALSE;

-- ************************************************
-- get Referenced
DBMS_LOB.WRITE(docBody, LENGTH( '<TABLE WIDTH=100% CELLPADDING=3
BORDER=2>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TABLE
WIDTH=100% CELLPADDING=3 BORDER=2>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=#ccccff><FONT
FACE="ARIAL" SIZE=2>Referenced Objects</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD BGCOLOR=#ccccff><FONT FACE="ARIAL"
SIZE=2>Referenced Objects</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TABLE>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TABLE>'||CHR(10));

DBMS_LOB.WRITE(docBody, LENGTH( '<TABLE WIDTH=100% CELLPADDING=2


BORDER=2>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TABLE
WIDTH=100% CELLPADDING=2 BORDER=2>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=200><B><FONT FACE="ARIAL" SIZE=2>Object
Name</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TD
BGCOLOR=LEMONCHIFFON ALIGN=CENTER VALIGN=top WIDTH=200><B><FONT
FACE="ARIAL" SIZE=2>Object Name</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=200><B><FONT FACE="ARIAL" SIZE=2>Object
Type</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TD
BGCOLOR=LEMONCHIFFON ALIGN=CENTER VALIGN=top WIDTH=200><B><FONT
FACE="ARIAL" SIZE=2>Object Type</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));

FOR lrec IN (
SELECT TRIM(referenced_owner||'.'||referenced_name) as
object_name,
DECODE(referenced_type, 'NON-EXISTENT', ' ',
TRIM(referenced_type)) as object_type
FROM all_dependencies
WHERE name = IN_OBJECT
AND referenced_name <> IN_OBJECT
AND type = IN_OBJECT_TYPE
ORDER BY 2, 1)
LOOP
is_reference := TRUE;
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||lrec.object_name||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT FACE="ARIAL"
SIZE=2>'||lrec.object_name||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||lrec.object_type||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT FACE="ARIAL"
SIZE=2>'||lrec.object_type||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));
END LOOP;

IF NOT is_reference
THEN
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>NOT FOUND</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT FACE="ARIAL"
SIZE=2>NOT FOUND</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));
END IF;

DBMS_LOB.WRITE(docBody, LENGTH( '</TABLE>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '</TABLE>'||CHR(10));

END getRefObjects;

/* **************************************
Get Table Dependencies
* **************************************/
PROCEDURE getTblRefObjects(
IN_OBJECT VARCHAR2,
IN_OBJECT_TYPE VARCHAR2)
IS
is_reference BOOLEAN;
BEGIN

DBMS_LOB.WRITE(docBody, LENGTH( CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, CHR(10) );
-- Header
DBMS_LOB.WRITE(docBody, LENGTH( '<TABLE WIDTH=100% CELLPADDING=3
BORDER=2>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TABLE
WIDTH=100% CELLPADDING=3 BORDER=2>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=#ccccff><FONT
FACE="ARIAL" SIZE=4>Dependencies</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD BGCOLOR=#ccccff><FONT FACE="ARIAL"
SIZE=4>Dependencies</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TABLE>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TABLE>'||CHR(10));

-- ************************************************
-- get Referencing
DBMS_LOB.WRITE(docBody, LENGTH( '<TABLE WIDTH=100% CELLPADDING=3
BORDER=2>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TABLE
WIDTH=100% CELLPADDING=3 BORDER=2>'||CHR(10));

DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=#ccccff><FONT
FACE="ARIAL" SIZE=2>Referencing Objects</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD BGCOLOR=#ccccff><FONT FACE="ARIAL"
SIZE=2>Referencing Objects</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TABLE>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TABLE>'||CHR(10));

DBMS_LOB.WRITE(docBody, LENGTH( '<TABLE WIDTH=100% CELLPADDING=2


BORDER=2>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TABLE
WIDTH=100% CELLPADDING=2 BORDER=2>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=200><B><FONT FACE="ARIAL" SIZE=2>Object
Name</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TD
BGCOLOR=LEMONCHIFFON ALIGN=CENTER VALIGN=top WIDTH=200><B><FONT
FACE="ARIAL" SIZE=2>Object Name</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=200><B><FONT FACE="ARIAL" SIZE=2>Object
Type</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TD
BGCOLOR=LEMONCHIFFON ALIGN=CENTER VALIGN=top WIDTH=200><B><FONT
FACE="ARIAL" SIZE=2>Object Type</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));

is_reference := FALSE;

-- referencing
FOR lrec IN (
SELECT
TRIM(owner||'.'||name) as object_name,
TRIM(type) as object_type
FROM all_dependencies
WHERE referenced_name = IN_OBJECT
AND name <> IN_OBJECT
UNION ALL
SELECT owner||'.'||synonym_name as object_name,
'SYNONYM' as object_type
FROM all_synonyms
WHERE table_name = IN_OBJECT
UNION ALL
SELECT owner||'.'||index_name as object_name,
'INDEX' as object_type
FROM all_indexes
WHERE table_name = IN_OBJECT
ORDER BY 2)
LOOP
is_reference := TRUE;
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||lrec.object_name||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT FACE="ARIAL"
SIZE=2>'||lrec.object_name||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||lrec.object_type||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT FACE="ARIAL"
SIZE=2>'||lrec.object_type||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));
END LOOP;

IF NOT is_reference
THEN
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>NOT FOUND</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT FACE="ARIAL"
SIZE=2>NOT FOUND</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));
END IF;

DBMS_LOB.WRITE(docBody, LENGTH( '</TABLE>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '</TABLE>'||CHR(10));

-- referenced objects
DBMS_LOB.WRITE(docBody, LENGTH( '<TABLE WIDTH=100% CELLPADDING=3
BORDER=2>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TABLE
WIDTH=100% CELLPADDING=3 BORDER=2>'||CHR(10));

DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=#ccccff><FONT
FACE="ARIAL" SIZE=2>Referenced Objects</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD BGCOLOR=#ccccff><FONT FACE="ARIAL"
SIZE=2>Referenced Objects</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TABLE>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TABLE>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TABLE WIDTH=100% CELLPADDING=2
BORDER=2>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TABLE
WIDTH=100% CELLPADDING=2 BORDER=2>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=200><B><FONT FACE="ARIAL" SIZE=2>Object
Name</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TD
BGCOLOR=LEMONCHIFFON ALIGN=CENTER VALIGN=top WIDTH=200><B><FONT
FACE="ARIAL" SIZE=2>Object Name</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=200><B><FONT FACE="ARIAL" SIZE=2>Object
Type</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TD
BGCOLOR=LEMONCHIFFON ALIGN=CENTER VALIGN=top WIDTH=200><B><FONT
FACE="ARIAL" SIZE=2>Object Type</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));

is_reference := FALSE;

FOR lrec IN (
SELECT
TRIM(referenced_owner||'.'||referenced_name) as
object_name,
TRIM(referenced_type) as object_type
FROM all_dependencies
WHERE name = IN_OBJECT)
LOOP
is_reference := TRUE;
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||lrec.object_name||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT FACE="ARIAL"
SIZE=2>'||lrec.object_name||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||lrec.object_type||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT FACE="ARIAL"
SIZE=2>'||lrec.object_type||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));
END LOOP;

IF NOT is_reference
THEN
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>NOT FOUND</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT FACE="ARIAL"
SIZE=2>NOT FOUND</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));
END IF;

DBMS_LOB.WRITE(docBody, LENGTH( '</TABLE>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '</TABLE>'||CHR(10));
END getTblRefObjects;

/* **************************************
Set Doc Header
* **************************************/
PROCEDURE setDocHeader(
IN_OBJECT VARCHAR2,
IN_OBJECT_TYPE VARCHAR2)
IS
lv_Code_Size PLS_INTEGER;
lv_created VARCHAR2(30);
lv_last_ddl_time VARCHAR2(30);
lv_status VARCHAR2(30);
BEGIN

-- Object status and other info


getTopObjectDetails(IN_OBJECT , IN_OBJECT_TYPE);

-- Object status and other info


SELECT TO_CHAR(created,'MM/DD/YYYY HH24:MI:SS'),
TO_CHAR(last_ddl_time,'MM/DD/YYYY HH24:MI:SS'), status
INTO lv_created, lv_last_ddl_time, lv_status
FROM user_objects
WHERE object_name = IN_OBJECT
AND object_type = IN_OBJECT_TYPE;

SELECT TO_CHAR(MAX(line)) INTO lv_Code_Size


FROM user_source
WHERE name = IN_OBJECT
AND type = IN_OBJECT_TYPE;

-- Body info
DBMS_LOB.WRITE(docBody, LENGTH( '<TABLE WIDTH=100% CELLPADDING=2
BORDER=3>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TABLE
WIDTH=100% CELLPADDING=2 BORDER=3>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=#ccccff
WIDTH=300><FONT FACE="ARIAL" SIZE=4>Information</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD BGCOLOR=#ccccff WIDTH=300><FONT
FACE="ARIAL" SIZE=4>Information</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TABLE>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TABLE>'||CHR(10));

DBMS_LOB.WRITE(docBody, LENGTH( '<TABLE WIDTH=100% CELLPADDING=2


BORDER=3>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TABLE
WIDTH=100% CELLPADDING=2 BORDER=3>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
VALIGN=top WIDTH=200><B><FONT FACE="ARIAL"
SIZE=2>Status</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)
+1, '<TD BGCOLOR=LEMONCHIFFON VALIGN=top WIDTH=200><B><FONT FACE="ARIAL"
SIZE=2>Status</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top WIDTH=200><FONT
FACE="ARIAL" SIZE=2>'||lv_status||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top WIDTH=200><FONT
FACE="ARIAL" SIZE=2>'||lv_status||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
VALIGN=top WIDTH=200><B><FONT FACE="ARIAL" SIZE=2>Code
Size</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TD
BGCOLOR=LEMONCHIFFON VALIGN=top WIDTH=200><B><FONT FACE="ARIAL"
SIZE=2>Code Size</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top WIDTH=200><FONT
FACE="ARIAL" SIZE=2>'||lv_Code_Size||' lines</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top WIDTH=200><FONT
FACE="ARIAL" SIZE=2>'||lv_Code_Size||' lines</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));

DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
VALIGN=top WIDTH=200><B><FONT FACE="ARIAL" SIZE=2>Creation
Date</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TD
BGCOLOR=LEMONCHIFFON VALIGN=top WIDTH=200><B><FONT FACE="ARIAL"
SIZE=2>Creation Date</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top WIDTH=200><FONT
FACE="ARIAL" SIZE=2>'||lv_created||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top WIDTH=200><FONT
FACE="ARIAL" SIZE=2>'||lv_created||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
VALIGN=top WIDTH=200><B><FONT FACE="ARIAL" SIZE=2>Last
Update</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TD
BGCOLOR=LEMONCHIFFON VALIGN=top WIDTH=200><B><FONT FACE="ARIAL"
SIZE=2>Last Update</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top WIDTH=200><FONT
FACE="ARIAL" SIZE=2>'||lv_last_ddl_time||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top WIDTH=200><FONT
FACE="ARIAL" SIZE=2>'||lv_last_ddl_time||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));

DBMS_LOB.WRITE(docBody, LENGTH( '</TABLE>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '</TABLE>'||CHR(10));

END setDocHeader;

/* **************************************
Set Table Doc Header
* **************************************/
PROCEDURE setTblDocHeader(
IN_OBJECT VARCHAR2,
IN_OBJECT_TYPE VARCHAR2)
IS
lv_created VARCHAR2(30);
lv_last_ddl_time VARCHAR2(30);
lv_last_analyzed VARCHAR2(30);
lv_num_rows VARCHAR2(30);
lv_partitioned VARCHAR2(30);
lv_nested VARCHAR2(30);
lv_comment VARCHAR2(4000);
lv_status VARCHAR2(30);
BEGIN

-- Object status and other info


getTopObjectDetails(IN_OBJECT , IN_OBJECT_TYPE);

-- Object status and other info


SELECT TO_CHAR(created,'MM/DD/YYYY HH24:MI:SS'),
TO_CHAR(last_ddl_time,'MM/DD/YYYY HH24:MI:SS'), status
INTO lv_created, lv_last_ddl_time, lv_status
FROM user_objects
WHERE object_name = IN_OBJECT
AND object_type = IN_OBJECT_TYPE;

IF IN_OBJECT_TYPE = 'TABLE'
THEN
SELECT TO_CHAR(last_analyzed,'MM/DD/YYYY HH24:MI:SS'),
TRIM(TO_CHAR( NVL(num_rows, 0), '999,999,999,999,999')),
partitioned,
nested
INTO lv_last_analyzed, lv_num_rows, lv_partitioned, lv_nested
FROM user_tables
WHERE table_name = IN_OBJECT;

SELECT TO_CHAR(a.last_analyzed,'MM/DD/YYYY HH24:MI:SS'),


TRIM(TO_CHAR( NVL(a.num_rows, 0),
'999,999,999,999,999')),
a.partitioned,
a.nested,
b.comments
INTO lv_last_analyzed, lv_num_rows, lv_partitioned, lv_nested,
lv_comment
FROM user_tables a,
user_tab_comments b
WHERE a.table_name = IN_OBJECT
AND b.table_name = a.table_name;
END IF;

-- Body info
DBMS_LOB.WRITE(docBody, LENGTH( '<TABLE WIDTH=100% CELLPADDING=2
BORDER=3>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TABLE
WIDTH=100% CELLPADDING=2 BORDER=3>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=#ccccff
WIDTH=300><FONT FACE="ARIAL" SIZE=4>Information</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD BGCOLOR=#ccccff WIDTH=300><FONT
FACE="ARIAL" SIZE=4>Information</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TABLE>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TABLE>'||CHR(10));

DBMS_LOB.WRITE(docBody, LENGTH( '<TABLE WIDTH=100% CELLPADDING=2


BORDER=3>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TABLE
WIDTH=100% CELLPADDING=2 BORDER=3>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
VALIGN=top WIDTH=200><B><FONT FACE="ARIAL" SIZE=2>Creation
Date</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TD
BGCOLOR=LEMONCHIFFON VALIGN=top WIDTH=200><B><FONT FACE="ARIAL"
SIZE=2>Creation Date</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top WIDTH=200><FONT
FACE="ARIAL" SIZE=2>'||lv_created||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top WIDTH=200><FONT
FACE="ARIAL" SIZE=2>'||lv_created||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
VALIGN=top WIDTH=200><B><FONT FACE="ARIAL" SIZE=2>Last
Update</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TD
BGCOLOR=LEMONCHIFFON VALIGN=top WIDTH=200><B><FONT FACE="ARIAL"
SIZE=2>Last Update</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top WIDTH=200><FONT
FACE="ARIAL" SIZE=2>'||lv_last_ddl_time||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top WIDTH=200><FONT
FACE="ARIAL" SIZE=2>'||lv_last_ddl_time||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));

IF IN_OBJECT_TYPE <> 'VIEW'


THEN
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
VALIGN=top WIDTH=200><B><FONT FACE="ARIAL" SIZE=2>Num Rows
(analyzed)</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1,
'<TD BGCOLOR=LEMONCHIFFON VALIGN=top WIDTH=200><B><FONT FACE="ARIAL"
SIZE=2>Num Rows (analyzed)</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top
WIDTH=200><FONT FACE="ARIAL" SIZE=2>'||lv_num_rows||'</FONT></TD>'||
CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top
WIDTH=200><FONT FACE="ARIAL" SIZE=2>'||lv_num_rows||'</FONT></TD>'||
CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
VALIGN=top WIDTH=200><B><FONT FACE="ARIAL" SIZE=2>Last
Analyzed</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1,
'<TD BGCOLOR=LEMONCHIFFON VALIGN=top WIDTH=200><B><FONT FACE="ARIAL"
SIZE=2>Last Analyzed</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top
WIDTH=200><FONT FACE="ARIAL" SIZE=2>'||
lv_last_analyzed||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top WIDTH=200><FONT
FACE="ARIAL" SIZE=2>'||lv_last_analyzed||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));

DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
VALIGN=top WIDTH=200><B><FONT FACE="ARIAL"
SIZE=2>Partitioned</FONT></B></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD BGCOLOR=LEMONCHIFFON VALIGN=top
WIDTH=200><B><FONT FACE="ARIAL" SIZE=2>Partitioned</FONT></B></TD>'||
CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top
WIDTH=200><FONT FACE="ARIAL" SIZE=2>'||lv_partitioned||'</FONT></TD>'||
CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top
WIDTH=200><FONT FACE="ARIAL" SIZE=2>'||lv_partitioned||'</FONT></TD>'||
CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
VALIGN=top WIDTH=200><B><FONT FACE="ARIAL"
SIZE=2>Nested</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)
+1, '<TD BGCOLOR=LEMONCHIFFON VALIGN=top WIDTH=200><B><FONT FACE="ARIAL"
SIZE=2>Nested</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top
WIDTH=200><FONT FACE="ARIAL" SIZE=2>'||lv_nested||'</FONT></TD>'||
CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top
WIDTH=200><FONT FACE="ARIAL" SIZE=2>'||lv_nested||'</FONT></TD>'||
CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));
ELSE
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
VALIGN=top WIDTH=200><B><FONT FACE="ARIAL"
SIZE=2>Status</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)
+1, '<TD BGCOLOR=LEMONCHIFFON VALIGN=top WIDTH=200><B><FONT FACE="ARIAL"
SIZE=2>Status</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top
WIDTH=200><FONT FACE="ARIAL" SIZE=2>'||lv_status||'</FONT></TD>'||
CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top
WIDTH=200><FONT FACE="ARIAL" SIZE=2>'||lv_status||'</FONT></TD>'||
CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));
END IF;

DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
VALIGN=top WIDTH=200><B><FONT FACE="ARIAL"
SIZE=2>Comment</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)
+1, '<TD BGCOLOR=LEMONCHIFFON VALIGN=top WIDTH=200><B><FONT FACE="ARIAL"
SIZE=2>Comment</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top WIDTH=600><FONT
FACE="ARIAL" SIZE=2>'||lv_comment||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top WIDTH=600><FONT
FACE="ARIAL" SIZE=2>'||lv_comment||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));

DBMS_LOB.WRITE(docBody, LENGTH( '</TABLE>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '</TABLE>'||CHR(10));

END setTblDocHeader;

/* **************************************
Get all Unit privileges
* **************************************/
PROCEDURE getUnitAllPrivs(IN_OBJECT VARCHAR2)
IS
is_privs BOOLEAN;

TYPE c_privs_t IS REF CURSOR;


c_privs_c c_privs_t;

ref_c VARCHAR2(4000) :=
'SELECT
(SELECT DECODE(privilege, ''DEBUG'', ''Y'')
FROM all_tab_privs
WHERE table_name = :1
AND grantee = :2
AND privilege = ''DEBUG'') deb,
(SELECT DECODE(privilege, ''EXECUTE'', ''Y'')
FROM all_tab_privs
WHERE table_name = :3
AND grantee = :4
AND privilege = ''EXECUTE'') exe
FROM DUAL';

privs_data_deb VARCHAR2(30);
privs_data_exe VARCHAR2(30);

BEGIN

-- Build table with possible privs for package/proc


DBMS_LOB.WRITE(docBody, LENGTH( CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<P>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<P>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TABLE WIDTH=100% CELLPADDING=3
BORDER=2>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TABLE
WIDTH=100% CELLPADDING=3 BORDER=2>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=#ccccff><FONT
FACE="ARIAL" SIZE=4>Privileges</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD BGCOLOR=#ccccff><FONT FACE="ARIAL"
SIZE=4>Privileges</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TABLE>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TABLE>'||CHR(10));

DBMS_LOB.WRITE(docBody, LENGTH( '<TABLE WIDTH=100% CELLPADDING=2


BORDER=2>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TABLE
WIDTH=100% CELLPADDING=2 BORDER=2>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=100><B><FONT FACE="ARIAL" SIZE=2>Object
Name</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TD
BGCOLOR=LEMONCHIFFON ALIGN=CENTER VALIGN=top WIDTH=100><B><FONT
FACE="ARIAL" SIZE=2>Object Name</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=100><B><FONT FACE="ARIAL"
SIZE=2>Debug</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)
+1, '<TD BGCOLOR=LEMONCHIFFON ALIGN=CENTER VALIGN=top WIDTH=100><B><FONT
FACE="ARIAL" SIZE=2>Debug</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=100><B><FONT FACE="ARIAL"
SIZE=2>Execute</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)
+1, '<TD BGCOLOR=LEMONCHIFFON ALIGN=CENTER VALIGN=top WIDTH=100><B><FONT
FACE="ARIAL" SIZE=2>Execute</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));

-- Fill out table with Privs


is_privs := FALSE;

FOR lrec IN (
SELECT DISTINCT grantee
FROM all_tab_privs
WHERE table_name = IN_OBJECT)
LOOP
is_privs := TRUE;
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||lrec.grantee||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT FACE="ARIAL"
SIZE=2>'||lrec.grantee||'</FONT></TD>'||CHR(10));

OPEN c_privs_c FOR ref_c


USING IN_OBJECT, lrec.grantee, IN_OBJECT, lrec.grantee;
LOOP
FETCH c_privs_c INTO privs_data_deb, privs_data_exe;

EXIT WHEN c_privs_c%NOTFOUND;

DBMS_LOB.WRITE(docBody, LENGTH( '<TD ALIGN=CENTER


VALIGN=top><FONT FACE="ARIAL" SIZE=2>'||privs_data_deb||'</FONT></TD>'||
CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TD ALIGN=CENTER
VALIGN=top><FONT FACE="ARIAL" SIZE=2>'||privs_data_deb||'</FONT></TD>'||
CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD ALIGN=CENTER
VALIGN=top><FONT FACE="ARIAL" SIZE=2>'||privs_data_exe||'</FONT></TD>'||
CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TD ALIGN=CENTER
VALIGN=top><FONT FACE="ARIAL" SIZE=2>'||privs_data_exe||'</FONT></TD>'||
CHR(10));

END LOOP;
CLOSE c_privs_c;
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));

END LOOP;

IF NOT is_privs
THEN
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD ALIGN=CENTER
VALIGN=top><FONT FACE="ARIAL" SIZE=2>NOT FOUND</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD ALIGN=CENTER VALIGN=top><FONT
FACE="ARIAL" SIZE=2>NOT FOUND</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));
END IF;

DBMS_LOB.WRITE(docBody, LENGTH( '</TABLE>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '</TABLE>'||CHR(10));
END getUnitAllPrivs;

/* **************************************
Initialize variables
* **************************************/
PROCEDURE initDoc(IN_OBJECT VARCHAR2)
IS

BEGIN
-- main page
DBMS_LOB.CREATETEMPORARY(main_page, TRUE); -- initialize
DBMS_LOB.WRITE(main_page, LENGTH( '<HTML>'||CHR(10) ), 1,
'<HTML>'||CHR(10));

DBMS_LOB.WRITE(main_page, LENGTH( '<HEAD>'||CHR(10) ),


DBMS_LOB.GETLENGTH( main_page) +1, '<HEAD>'||CHR(10));
DBMS_LOB.WRITE(main_page, LENGTH( '<META HTTP-EQUIV="content-type"
CONTENT="text/html; charset=Shift_JIS">'||CHR(10) ), DBMS_LOB.GETLENGTH(
main_page) +1, '<META HTTP-EQUIV="content-type" CONTENT="text/html;
charset=Shift_JIS">'||CHR(10));
DBMS_LOB.WRITE(main_page, LENGTH( '<TITLE>Report for '||
IN_OBJECT||'</TITLE>'||CHR(10) ), DBMS_LOB.GETLENGTH( main_page) +1,
'<TITLE>Report for '||IN_OBJECT||'</TITLE>'||CHR(10));
DBMS_LOB.WRITE(main_page, LENGTH( '</HEAD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( main_page) +1, '</HEAD>'||CHR(10) );
DBMS_LOB.WRITE(main_page, LENGTH( '<FRAMESET COLS="25%,*">'||
CHR(10) ), DBMS_LOB.GETLENGTH( main_page) +1, '<FRAMESET
COLS="25%,*">'||CHR(10));

-- body
DBMS_LOB.CREATETEMPORARY(docBody, TRUE); -- initialize
DBMS_LOB.WRITE(docBody, 1, 1, CHR(10));

-- Build reference page


DBMS_LOB.CREATETEMPORARY(ref_page, TRUE); -- initialize
DBMS_LOB.WRITE(ref_page, LENGTH( '<HTML>'||CHR(10) ), 1,
'<HTML>'||CHR(10));
DBMS_LOB.WRITE(ref_page, LENGTH( '<BODY BGCOLOR=WHITE TEXT=BLACK
LINK=BLUE VLINK=#FF7F00>'||CHR(10) ), DBMS_LOB.GETLENGTH( ref_page) +1,
'<BODY BGCOLOR=WHITE TEXT=BLACK LINK=BLUE VLINK=#FF7F00>'||CHR(10));
DBMS_LOB.WRITE(ref_page, LENGTH( '<TABLE WIDTH=100% CELLPADDING=3
BORDER=2>'||CHR(10) ), DBMS_LOB.GETLENGTH( ref_page) +1, '<TABLE
WIDTH=100% CELLPADDING=3 BORDER=2>'||CHR(10));
DBMS_LOB.WRITE(ref_page, LENGTH( '<TD BGCOLOR=#ccccff><B><FONT
FACE="ARIAL" SIZE=3>Report for: '||IN_OBJECT||'s'||'</FONT></B></TD>'||
CHR(10) ), DBMS_LOB.GETLENGTH( ref_page) +1, '<TD
BGCOLOR=#ccccff><B><FONT FACE="ARIAL" SIZE=3>Report for: '||
IN_OBJECT||'s'||'</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(ref_page, LENGTH( '</TABLE>'||CHR(10) ),
DBMS_LOB.GETLENGTH( ref_page) +1, '</TABLE>'||CHR(10));
DBMS_LOB.WRITE(ref_page, LENGTH( '<TABLE CELLPADDING=5
BORDER=2>'||CHR(10) ), DBMS_LOB.GETLENGTH( ref_page) +1, '<TABLE
CELLPADDING=5 BORDER=2>'||CHR(10));

-- get Schema name


SELECT SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA'),
SYS_CONTEXT('USERENV', 'DB_NAME'),
SYS_CONTEXT('USERENV', 'CURRENT_USER')
INTO gv_schema, gv_db_name, gv_user
FROM dual;

-- get Oracle version


SELECT product||' ('||version||')' INTO gv_version
FROM product_component_version
WHERE UPPER(product) like 'ORACLE%';

END initDoc;

/* **************************************
Finish documents preparation and send emails
* **************************************/
PROCEDURE finishDoc(
IN_OBJECT VARCHAR2,
IN_FILE_NAME VARCHAR2,
IN_EMAIL VARCHAR2)
IS
sessiontimezone VARCHAR2(10) := '';
BEGIN

-- finish "BODY" part


DBMS_LOB.WRITE(docBody, LENGTH( CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, CHR(10) );
DBMS_LOB.WRITE(docBody, LENGTH( '<P>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<P>'||CHR(10));

DBMS_LOB.WRITE(docBody, LENGTH( '<P>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '<P>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TABLE WIDTH=100% CELLPADDING=1
BORDER=1>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TABLE
WIDTH=100% CELLPADDING=1 BORDER=1>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD ALIGN=LEFT VALIGN=top
BGCOLOR=GRAY><FONT FACE="ARIAL" SIZE=1 COLOR=WHITE>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD ALIGN=LEFT VALIGN=top
BGCOLOR=GRAY><FONT FACE="ARIAL" SIZE=1 COLOR=WHITE>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( 'PL/SQL generated document. '||
TO_CHAR(SYSDATE, 'MM/DD/YYYY HH24:MI:SS')||' '||sessiontimezone||CHR(10)
), DBMS_LOB.GETLENGTH( docBody)+1, 'PL/SQL generated document. '||
TO_CHAR(SYSDATE, 'MM/DD/YYYY HH24:MI:SS')||' '||sessiontimezone||
CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TABLE>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TABLE>'||CHR(10));

-- finish "MAIN" document


-- add record to the main page
DBMS_LOB.WRITE(main_page, LENGTH( '<FRAME SRC="'||'Reference_'||
IN_FILE_NAME||'">'||CHR(10) ), DBMS_LOB.GETLENGTH( main_page) +1,
'<FRAME SRC="'||'Reference_'||IN_FILE_NAME||'">'||CHR(10));
DBMS_LOB.WRITE(main_page, LENGTH( '<FRAME SRC="'||'Content_'||
IN_FILE_NAME||'" NAME="OBJECT_CONTENT">'||CHR(10) ), DBMS_LOB.GETLENGTH(
main_page) +1, '<FRAME SRC="'||'Content_'||IN_FILE_NAME||'"
NAME="OBJECT_CONTENT">'||CHR(10));

DBMS_LOB.WRITE(main_page, LENGTH( '</FRAMESET>'||CHR(10) ),


DBMS_LOB.GETLENGTH( main_page) +1, '</FRAMESET>'||CHR(10));
DBMS_LOB.WRITE(main_page, LENGTH( '</HTML>'||CHR(10) ),
DBMS_LOB.GETLENGTH( main_page) +1, '</HTML>'||CHR(10));

-- finish Reference page


DBMS_LOB.WRITE(ref_page, LENGTH( '</TABLE>'||CHR(10) ),
DBMS_LOB.GETLENGTH( ref_page) +1, '</TABLE>'||CHR(10));
DBMS_LOB.WRITE(ref_page, LENGTH( '</BODY>'||CHR(10) ),
DBMS_LOB.GETLENGTH( ref_page) +1, '</BODY>'||CHR(10));
DBMS_LOB.WRITE(ref_page, LENGTH( '</HTML>'||CHR(10) ),
DBMS_LOB.GETLENGTH( ref_page) +1, '</HTML>'||CHR(10));

-- send email with documents


sendEmailAttachments(
IN_FROM_EMAIL => gv_schema||'@'||gv_db_name||'.com',
IN_TO_EMAILS => IN_EMAIL,
IN_CC_EMAILS => NULL,
IN_SUBJ => 'Description for '||IN_OBJECT,
IN_TEXT => NULL,
IN_MIME_TYPE => 'text/plain',
IN_FILENAME_1 => 'Content_'||IN_FILE_NAME, -- contant
IN_CONTENT_1 => docBody,
IN_FILENAME_2 => IN_FILE_NAME, -- main file
IN_CONTENT_2 => main_page,
IN_FILENAME_3 => 'Reference_'||IN_FILE_NAME, -- reference
page
IN_CONTENT_3 => ref_page);

END finishDoc;

/* **************************************
Prepare document for Procedure/Function/Package
* **************************************/
PROCEDURE getPkgDoc(
IN_OBJECT VARCHAR2,
IN_FILE_NAME VARCHAR2)
IS
BEGIN
FOR lrec IN (
SELECT DISTINCT UPPER(type) as object_type
FROM user_source
WHERE name = UPPER(IN_OBJECT))
LOOP

-- set header
setDocHeader(IN_OBJECT, lrec.object_type );

-- get Dependencies
getRefObjects(IN_OBJECT, lrec.object_type);

IF lrec.object_type IN ('PACKAGE BODY', 'FUNCTION',


'PROCEDURE')
THEN

-- list of privileges
getUnitAllPrivs(IN_OBJECT);

-- list of all arguments


getArgumentsList(IN_OBJECT);
ELSIF lrec.object_type = 'TRIGGER'
THEN

-- get trigger status


getTriggerColumns(IN_OBJECT);

-- get list of trigger columns


getTriggerColumns(IN_OBJECT);

-- trigger source
getUnitSource(IN_OBJECT, lrec.object_type);
ELSIF lrec.object_type IN ('TYPE', 'TYPE BODY')
THEN

-- get type status


getTypeStatus(IN_OBJECT, lrec.object_type);

IF lrec.object_type = 'TYPE'
THEN

-- attributes
getTypeAttrs(IN_OBJECT);
ELSE

-- type methods
getTypeMethods(IN_OBJECT);
END IF;

-- type source
getUnitSource(IN_OBJECT, lrec.object_type);
ELSE
NULL;
END IF;

-- add record to the reference page


DBMS_LOB.WRITE(ref_page, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( ref_page)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(ref_page, LENGTH( '<TD ALIGN=LEFT><FONT
FACE="ARIAL" SIZE=2><A HREF="'||'Content_'||IN_FILE_NAME||'#'||
lrec.object_type||gv_schema||'.'||IN_OBJECT||'"
TARGET="OBJECT_CONTENT">'||gv_schema||'.'||IN_OBJECT||'</FONT></A><FONT
FACE="ARIAL" SIZE=1> - '||lrec.object_type||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( ref_page)+1, '<TD ALIGN=LEFT><FONT FACE="ARIAL"
SIZE=2><A HREF="'||'Content_'||IN_FILE_NAME||'#'||lrec.object_type||
gv_schema||'.'||IN_OBJECT||'" TARGET="OBJECT_CONTENT">'||
gv_schema||'.'||IN_OBJECT||'</FONT></A><FONT FACE="ARIAL" SIZE=1> - '||
lrec.object_type||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(ref_page, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( ref_page)+1, '</TR>'||CHR(10));

END LOOP;
END getPkgDoc;

/* **************************************
Prepare document for Table
* **************************************/
PROCEDURE getTblDoc(
IN_OBJECT VARCHAR2,
IN_OBJECT_TYPE VARCHAR2,
IN_FILE_NAME VARCHAR2)
IS
BEGIN

-- set header
setTblDocHeader(IN_OBJECT, IN_OBJECT_TYPE );

-- get Dependencies
getTblRefObjects(IN_OBJECT, IN_OBJECT_TYPE );

-- columns
getTblCols(IN_OBJECT );

-- constraints
getTblConstraints(IN_OBJECT );

-- indexes
getTblIndexes(IN_OBJECT);

-- privileges
getTblPrivs(IN_OBJECT );

-- add record to the reference page


DBMS_LOB.WRITE(ref_page, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( ref_page)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(ref_page, LENGTH( '<TD ALIGN=LEFT><FONT
FACE="ARIAL" SIZE=2><A HREF="'||'Content_'||IN_FILE_NAME||'#'||
IN_OBJECT_TYPE||gv_schema||'.'||IN_OBJECT||'"
TARGET="OBJECT_CONTENT">'||gv_schema||'.'||IN_OBJECT||'</FONT></A><FONT
FACE="ARIAL" SIZE=1> - '||IN_OBJECT_TYPE||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( ref_page)+1, '<TD ALIGN=LEFT><FONT FACE="ARIAL"
SIZE=2><A HREF="'||'Content_'||IN_FILE_NAME||'#'||IN_OBJECT_TYPE||
gv_schema||'.'||IN_OBJECT||'" TARGET="OBJECT_CONTENT">'||
gv_schema||'.'||IN_OBJECT||'</FONT></A><FONT FACE="ARIAL" SIZE=1> - '||
IN_OBJECT_TYPE||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(ref_page, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( ref_page)+1, '</TR>'||CHR(10));

END getTblDoc;

/* **************************************
Main procedure to generate document
* **************************************/
PROCEDURE generateDocInfo(
IN_OBJECT VARCHAR2,
IN_FILE_NAME VARCHAR2,
IN_EMAIL VARCHAR2)
IS
lv_object_type VARCHAR2(255);
BEGIN

-- check object type


SELECT object_type INTO lv_object_type
FROM user_objects
WHERE object_name = UPPER(IN_OBJECT)
AND ROWNUM = 1;

-- initialize variables
initDoc(IN_OBJECT);

IF lv_object_type IN ('FUNCTION', 'PROCEDURE', 'PACKAGE', 'PACKAGE


BODY', 'TRIGGER', 'TYPE', 'TYPE BODY')
THEN
getPkgDoc(UPPER(IN_OBJECT), IN_FILE_NAME);
ELSIF lv_object_type IN ('TABLE', 'VIEW')
THEN
getTblDoc(UPPER(IN_OBJECT), UPPER(lv_object_type),
IN_FILE_NAME);
ELSE
NULL;
END IF;

-- finish documentation: send emails


finishDoc(INITCAP(IN_OBJECT), IN_FILE_NAME, IN_EMAIL);

EXCEPTION
WHEN no_data_found
THEN

DBMS_OUTPUT.PUT_LINE('**************************************************
**********');
DBMS_OUTPUT.PUT_LINE('Object "'||IN_OBJECT||'" not found');

DBMS_OUTPUT.PUT_LINE('**************************************************
**********');
WHEN others
THEN

DBMS_OUTPUT.PUT_LINE('**************************************************
**********');
DBMS_OUTPUT.PUT_LINE( 'ERROR: '||SQLERRM);
DBMS_OUTPUT.PUT_LINE('**************************************************
**********');
END generateDocInfo;

/* **************************************
Main procedure to generate document for all objects for the given
object type
* **************************************/
PROCEDURE generateDocInfoByType(
IN_OBJECT_TYPE VARCHAR2,
IN_FILE_NAME VARCHAR2,
IN_EMAIL VARCHAR2)
IS
ln_check PLS_INTEGER;
BEGIN

SELECT 1 INTO ln_check


FROM user_objects
WHERE object_type = UPPER(IN_OBJECT_TYPE)
AND ROWNUM = 1;

-- initialize variables
initDoc(INITCAP(IN_OBJECT_TYPE||'s'));

FOR lrec IN (
SELECT object_name,
object_type
FROM user_objects
WHERE object_type = UPPER(IN_OBJECT_TYPE)
ORDER BY object_name, object_type)
LOOP
IF lrec.object_type IN ('FUNCTION', 'PROCEDURE', 'PACKAGE',
'PACKAGE BODY', 'TRIGGER', 'TYPE' , 'TYPE BODY')
THEN
getPkgDoc(UPPER(lrec.object_name), IN_FILE_NAME);
ELSIF lrec.object_type IN ('TABLE', 'VIEW')
THEN
getTblDoc(UPPER(lrec.object_name), UPPER(lrec.object_type),
IN_FILE_NAME);
ELSE
NULL;
END IF;
END LOOP;

-- finish documentation: send emails


finishDoc(INITCAP(IN_OBJECT_TYPE)||'s', IN_FILE_NAME, IN_EMAIL);

EXCEPTION
WHEN no_data_found
THEN

DBMS_OUTPUT.PUT_LINE('**************************************************
**********');
DBMS_OUTPUT.PUT_LINE('No one object with type "'||
IN_OBJECT_TYPE||'" found');
DBMS_OUTPUT.PUT_LINE('**************************************************
**********');
WHEN others
THEN

DBMS_OUTPUT.PUT_LINE('**************************************************
**********');
DBMS_OUTPUT.PUT_LINE( 'ERROR: '||SQLERRM);

DBMS_OUTPUT.PUT_LINE('**************************************************
**********');
END generateDocInfoByType;

/* **************************************
Main procedure to generate document for all objects for the given
object types
* **************************************/
PROCEDURE generateDocInfoByTypes(
IN_OBJECT_TYPE VARCHAR2,
IN_FILE_NAME VARCHAR2,
IN_EMAIL VARCHAR2)
IS
ln_check PLS_INTEGER;
lb_flag BOOLEAN;
le_unsupportedType EXCEPTION;
BEGIN

-- load types in array


splitType(UPPER(IN_OBJECT_TYPE) );

-- initialize variables
initDoc(INITCAP(IN_OBJECT_TYPE));

lb_flag := FALSE;

-- loop through types


FOR I IN type_index.FIRST..type_index.LAST
LOOP
BEGIN
IF INSTR( 'FUNCTION, PROCEDURE, PACKAGE, PACKAGE BODY,
TRIGGER, TYPE, TYPE BODY, TABLE, VIEW', type_index(I)) = 0
THEN
RAISE le_unsupportedType;
END IF;

SELECT 1 INTO ln_check


FROM user_objects
WHERE object_type = type_index(I)
AND ROWNUM = 1;

FOR lrec IN (
SELECT object_name,
object_type
FROM user_objects
WHERE object_type = type_index(I)
ORDER BY object_name, object_type)
LOOP

lb_flag := TRUE;

IF lrec.object_type IN ('FUNCTION', 'PROCEDURE',


'PACKAGE', 'PACKAGE BODY', 'TRIGGER', 'TYPE' , 'TYPE BODY')
THEN
getPkgDoc(UPPER(lrec.object_name), IN_FILE_NAME);
ELSIF lrec.object_type IN ('TABLE', 'VIEW')
THEN
getTblDoc(UPPER(lrec.object_name), lrec.object_type,
IN_FILE_NAME);
ELSE
NULL;
END IF;

END LOOP;
EXCEPTION
WHEN le_unsupportedType
THEN
DBMS_OUTPUT.PUT_LINE('Type "'||type_index(I)||'" is
unsupported');
WHEN no_data_found
THEN
DBMS_OUTPUT.PUT_LINE('No one object with type "'||
type_index(I)||'" found');
END;

END LOOP;

IF lb_flag
THEN
-- finish documentation: send emails
finishDoc(INITCAP(IN_OBJECT_TYPE), IN_FILE_NAME, IN_EMAIL);

DBMS_OUTPUT.PUT_LINE('**************************************************
**********');
DBMS_OUTPUT.PUT_LINE('Document is generated');

DBMS_OUTPUT.PUT_LINE('**************************************************
**********');
ELSE

DBMS_OUTPUT.PUT_LINE('**************************************************
**********');
DBMS_OUTPUT.PUT_LINE('Document has not been generated');

DBMS_OUTPUT.PUT_LINE('**************************************************
**********');
END IF;

EXCEPTION
WHEN others
THEN
DBMS_OUTPUT.PUT_LINE('**************************************************
**********');
DBMS_OUTPUT.PUT_LINE('ERROR: '||SQLERRM );

DBMS_OUTPUT.PUT_LINE('**************************************************
**********');
END generateDocInfoByTypes;

/* **************************************
Get list of procedures/functions with arguments
* **************************************/
PROCEDURE getArgumentsList(IN_OBJECT VARCHAR2)
IS

lb_function BOOLEAN;
is_args BOOLEAN;

BEGIN

DBMS_LOB.WRITE(docBody, LENGTH( CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, CHR(10) );
DBMS_LOB.WRITE(docBody, LENGTH( '<P>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<P>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TABLE WIDTH=100% CELLPADDING=3
BORDER=2>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TABLE
WIDTH=100% CELLPADDING=3 BORDER=2>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=#ccccff><FONT
FACE="ARIAL" SIZE=4>Procedures/Functions</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD BGCOLOR=#ccccff><FONT FACE="ARIAL"
SIZE=4>Procedures/Functions</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TABLE>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TABLE>'||CHR(10));

DBMS_LOB.WRITE(docBody, LENGTH( '<TABLE WIDTH=100% CELLPADDING=2


BORDER=2>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TABLE
WIDTH=100% CELLPADDING=2 BORDER=2>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=100><B><FONT FACE="ARIAL" SIZE=2>Program
Unit</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TD
BGCOLOR=LEMONCHIFFON ALIGN=CENTER VALIGN=top WIDTH=100><B><FONT
FACE="ARIAL" SIZE=2>Program Unit</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=100><B><FONT FACE="ARIAL"
SIZE=2>Overload</FONT></B></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD BGCOLOR=LEMONCHIFFON ALIGN=CENTER
VALIGN=top WIDTH=100><B><FONT FACE="ARIAL"
SIZE=2>Overload</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=100><B><FONT FACE="ARIAL"
SIZE=2>Type</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1,
'<TD BGCOLOR=LEMONCHIFFON ALIGN=CENTER VALIGN=top WIDTH=100><B><FONT
FACE="ARIAL" SIZE=2>Type</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=100><B><FONT FACE="ARIAL"
SIZE=2>Parameter</FONT></B></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD BGCOLOR=LEMONCHIFFON ALIGN=CENTER
VALIGN=top WIDTH=100><B><FONT FACE="ARIAL"
SIZE=2>Parameter</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=100><B><FONT FACE="ARIAL"
SIZE=2>IN/OUT</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)
+1, '<TD BGCOLOR=LEMONCHIFFON ALIGN=CENTER VALIGN=top WIDTH=100><B><FONT
FACE="ARIAL" SIZE=2>IN/OUT</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=100><B><FONT FACE="ARIAL"
SIZE=2>Datatype</FONT></B></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD BGCOLOR=LEMONCHIFFON ALIGN=CENTER
VALIGN=top WIDTH=100><B><FONT FACE="ARIAL"
SIZE=2>Datatype</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));

-- Fill out table


is_args := FALSE;

FOR lrec IN (
SELECT DISTINCT
object_name,
DECODE(NVL(overload, -1), -1, 'No', 'Yes') is_overload,
overload
FROM user_arguments
WHERE package_name = IN_OBJECT OR object_name = IN_OBJECT
ORDER BY object_name, overload)
LOOP

DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2><B>'||lrec.object_name||'</B></FONT></TD>'||
CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2><B>'||lrec.object_name||'</B></FONT></TD>'||
CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD ALIGN=CENTER
VALIGN=top><FONT FACE="ARIAL" SIZE=2>'||
lrec.is_overload||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD ALIGN=CENTER VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||lrec.is_overload||'</FONT></TD>'||CHR(10));

lb_function := FALSE;
is_args := TRUE;

FOR lrec_arg IN (
SELECT object_name,
overload,
NVL(argument_name, 'RETURN VALUE') as parameter,
data_type,
in_out,
default_value,
position,
package_name
FROM user_arguments
WHERE NVL(package_name, IN_OBJECT) = IN_OBJECT AND
object_name = lrec.object_name
AND NVL(overload, -1) = NVL(lrec.overload, -1)
ORDER BY object_name, overload, position)
LOOP

IF lb_function = TRUE
THEN
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2></FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT FACE="ARIAL"
SIZE=2></FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2></FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT FACE="ARIAL"
SIZE=2></FONT></TD>'||CHR(10));
END IF;

-- Type
IF lrec_arg.parameter = 'RETURN VALUE' AND lb_function =
FALSE
THEN
DBMS_LOB.WRITE(docBody, LENGTH( '<TD ALIGN=CENTER
VALIGN=top><FONT FACE="ARIAL" SIZE=2><B>Function</B></FONT></TD>'||
CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TD ALIGN=CENTER
VALIGN=top><FONT FACE="ARIAL" SIZE=2><B>Function</B></FONT></TD>'||
CHR(10));
lb_function := TRUE;
ELSIF lb_function = FALSE
THEN
DBMS_LOB.WRITE(docBody, LENGTH( '<TD ALIGN=CENTER
VALIGN=top><FONT FACE="ARIAL" SIZE=2><B>Procedure</B></FONT></TD>'||
CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TD ALIGN=CENTER
VALIGN=top><FONT FACE="ARIAL" SIZE=2><B>Procedure</B></FONT></TD>'||
CHR(10));
lb_function := TRUE;
ELSE
DBMS_LOB.WRITE(docBody, LENGTH( '<TD ALIGN=CENTER
VALIGN=top><FONT FACE="ARIAL" SIZE=2></FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD ALIGN=CENTER VALIGN=top><FONT
FACE="ARIAL" SIZE=2></FONT></TD>'||CHR(10));
END IF;

-- Parameter
DBMS_LOB.WRITE(docBody, LENGTH( '<TD ALIGN=CENTER
VALIGN=top><FONT FACE="ARIAL" SIZE=2>'||
lrec_arg.parameter||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD ALIGN=CENTER VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||lrec_arg.parameter||'</FONT></TD>'||CHR(10));

-- In/Out
DBMS_LOB.WRITE(docBody, LENGTH( '<TD ALIGN=CENTER
VALIGN=top><FONT FACE="ARIAL" SIZE=2>'||
lrec_arg.IN_OUT||'</FONT></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)
+1, '<TD ALIGN=CENTER VALIGN=top><FONT FACE="ARIAL" SIZE=2>'||
lrec_arg.IN_OUT||'</FONT></TD>'||CHR(10));

-- Datatype
DBMS_LOB.WRITE(docBody, LENGTH( '<TD ALIGN=CENTER
VALIGN=top><FONT FACE="ARIAL" SIZE=2>'||
lrec_arg.data_type||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD ALIGN=CENTER VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||lrec_arg.data_type||'</FONT></TD>'||CHR(10));

DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));

END LOOP;

END LOOP;

IF NOT is_args
THEN
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD ALIGN=CENTER
VALIGN=top><FONT FACE="ARIAL" SIZE=2>NOT FOUND</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD ALIGN=CENTER VALIGN=top><FONT
FACE="ARIAL" SIZE=2>NOT FOUND</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));
END IF;

DBMS_LOB.WRITE(docBody, LENGTH( '</TABLE>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '</TABLE>'||CHR(10));

END getArgumentsList;

/* **************************************
Get trigger columns
* **************************************/
PROCEDURE getTriggerColumns(IN_OBJECT VARCHAR2)
IS

is_cols BOOLEAN;

BEGIN

DBMS_LOB.WRITE(docBody, LENGTH( CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, CHR(10) );
DBMS_LOB.WRITE(docBody, LENGTH( '<P>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<P>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TABLE WIDTH=100% CELLPADDING=3
BORDER=2>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TABLE
WIDTH=100% CELLPADDING=3 BORDER=2>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=#ccccff><FONT
FACE="ARIAL" SIZE=4>Trigger Columns</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD BGCOLOR=#ccccff><FONT FACE="ARIAL"
SIZE=4>Trigger Columns</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TABLE>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TABLE>'||CHR(10));

DBMS_LOB.WRITE(docBody, LENGTH( '<TABLE WIDTH=100% CELLPADDING=2


BORDER=2>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TABLE
WIDTH=100% CELLPADDING=2 BORDER=2>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=100><B><FONT FACE="ARIAL" SIZE=2>Table
Name</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TD
BGCOLOR=LEMONCHIFFON ALIGN=CENTER VALIGN=top WIDTH=100><B><FONT
FACE="ARIAL" SIZE=2>Table Name</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=100><B><FONT FACE="ARIAL" SIZE=2>Column
Name</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TD
BGCOLOR=LEMONCHIFFON ALIGN=CENTER VALIGN=top WIDTH=100><B><FONT
FACE="ARIAL" SIZE=2>Column Name</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=100><B><FONT FACE="ARIAL"
SIZE=2>Usage</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)
+1, '<TD BGCOLOR=LEMONCHIFFON ALIGN=CENTER VALIGN=top WIDTH=100><B><FONT
FACE="ARIAL" SIZE=2>Usage</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));

-- Fill out table


is_cols := FALSE;

FOR lrec IN (
SELECT table_owner||'.'||table_name as table_name,
column_name,
column_usage
FROM all_trigger_cols
WHERE trigger_name = IN_OBJECT)
LOOP

is_cols := TRUE;

DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||lrec.table_name||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT FACE="ARIAL"
SIZE=2>'||lrec.table_name||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD ALIGN=CENTER
VALIGN=top><FONT FACE="ARIAL" SIZE=2>'||
lrec.column_name||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD ALIGN=CENTER VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||lrec.column_name||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD ALIGN=CENTER
VALIGN=top><FONT FACE="ARIAL" SIZE=2>'||
lrec.column_usage||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD ALIGN=CENTER VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||lrec.column_usage||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));

END LOOP;

IF NOT is_cols
THEN
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD ALIGN=CENTER
VALIGN=top><FONT FACE="ARIAL" SIZE=2>NOT FOUND</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD ALIGN=CENTER VALIGN=top><FONT
FACE="ARIAL" SIZE=2>NOT FOUND</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));
END IF;

DBMS_LOB.WRITE(docBody, LENGTH( '</TABLE>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '</TABLE>'||CHR(10));

END getTriggerColumns;

/* **************************************
Get trigger details
* **************************************/
PROCEDURE getTriggerDetails(IN_OBJECT VARCHAR2)
IS

is_status BOOLEAN;

BEGIN

is_status := FALSE;

DBMS_LOB.WRITE(docBody, LENGTH( '<P>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '<P>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TABLE WIDTH=100% CELLPADDING=3
BORDER=2>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TABLE
WIDTH=100% CELLPADDING=3 BORDER=2>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=#ccccff><FONT
FACE="ARIAL" SIZE=4>Trigger Details</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD BGCOLOR=#ccccff><FONT FACE="ARIAL"
SIZE=4>Trigger Details</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TABLE>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TABLE>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TABLE WIDTH=100% CELLPADDING=2
BORDER=2>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TABLE
WIDTH=100% CELLPADDING=2 BORDER=2>'||CHR(10));

FOR lrec IN (
SELECT base_object_type||' '||table_name as base_object,
status,
trigger_type,
triggering_event,
referencing_names,
when_clause as when_condition
FROM all_triggers
WHERE trigger_name = IN_OBJECT)
LOOP
is_status := TRUE;

DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
VALIGN=top WIDTH=100><B><FONT FACE="ARIAL" SIZE=2>Base
Object</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TD
BGCOLOR=LEMONCHIFFON VALIGN=top WIDTH=100><B><FONT FACE="ARIAL"
SIZE=2>Base Object</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||lrec.base_object||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT FACE="ARIAL"
SIZE=2>'||lrec.base_object||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));

DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
VALIGN=top WIDTH=100><B><FONT FACE="ARIAL"
SIZE=2>Status</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)
+1, '<TD BGCOLOR=LEMONCHIFFON VALIGN=top WIDTH=100><B><FONT FACE="ARIAL"
SIZE=2>Status</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||lrec.status||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT FACE="ARIAL"
SIZE=2>'||lrec.status||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));

DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
VALIGN=top WIDTH=100><B><FONT FACE="ARIAL" SIZE=2>Trigger
Type</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TD
BGCOLOR=LEMONCHIFFON VALIGN=top WIDTH=100><B><FONT FACE="ARIAL"
SIZE=2>Trigger Type</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||lrec.trigger_type||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT FACE="ARIAL"
SIZE=2>'||lrec.trigger_type||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
VALIGN=top WIDTH=100><B><FONT FACE="ARIAL"
SIZE=2>Event</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)
+1, '<TD BGCOLOR=LEMONCHIFFON VALIGN=top WIDTH=100><B><FONT FACE="ARIAL"
SIZE=2>Event</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||lrec.triggering_event||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT FACE="ARIAL"
SIZE=2>'||lrec.triggering_event||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));

DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
VALIGN=top WIDTH=100><B><FONT FACE="ARIAL" SIZE=2>Referencing
names</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TD
BGCOLOR=LEMONCHIFFON VALIGN=top WIDTH=100><B><FONT FACE="ARIAL"
SIZE=2>Referencing names</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||lrec.referencing_names||'</FONT></TD>'||
CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||lrec.referencing_names||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));

DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
VALIGN=top WIDTH=100><B><FONT FACE="ARIAL" SIZE=2>WHEN
Condition</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1,
'<TD BGCOLOR=LEMONCHIFFON VALIGN=top WIDTH=100><B><FONT FACE="ARIAL"
SIZE=2>WHEN Condition</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||lrec.when_condition||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT FACE="ARIAL"
SIZE=2>'||lrec.when_condition||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));

END LOOP;

IF NOT is_status
THEN
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD ALIGN=CENTER
VALIGN=top><FONT FACE="ARIAL" SIZE=2>NOT FOUND</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD ALIGN=CENTER VALIGN=top><FONT
FACE="ARIAL" SIZE=2>NOT FOUND</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));
END IF;
DBMS_LOB.WRITE(docBody, LENGTH( '</TABLE>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TABLE>'||CHR(10));

END getTriggerDetails;

/* **************************************
Get PL/SQL Unit Source
* **************************************/
PROCEDURE getUnitSource(IN_OBJECT VARCHAR2, IN_OBJECT_TYPE VARCHAR2)
IS

BEGIN

DBMS_LOB.WRITE(docBody, LENGTH( '<P>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '<P>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<P>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<P>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<HTML>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<HTML>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TABLE WIDTH=100% CELLPADDING=3
BORDER=2>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TABLE
WIDTH=100% CELLPADDING=3 BORDER=2>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=#ccccff><FONT
FACE="ARIAL" SIZE=4>'||IN_OBJECT_TYPE||' Source</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD BGCOLOR=#ccccff><FONT FACE="ARIAL"
SIZE=4>'||IN_OBJECT_TYPE||' Source</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TABLE>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TABLE>'||CHR(10));

DBMS_LOB.WRITE(docBody, LENGTH( '<P>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '<P>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TT><FONT
FACE="COURIER"><PRE>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1,
'<TT><FONT FACE="COURIER"><PRE>'||CHR(10) );
DBMS_LOB.WRITE(docBody, LENGTH( 'CREATE OR REPLACE '),
DBMS_LOB.GETLENGTH( docBody)+1, 'CREATE OR REPLACE ');
FOR lrec IN (
SELECT text
FROM user_source
WHERE name = IN_OBJECT
AND TYPE = IN_OBJECT_TYPE
ORDER BY line)
LOOP
DBMS_LOB.WRITE(docBody, LENGTH( lrec.text),
DBMS_LOB.GETLENGTH( docBody)+1, lrec.text);
END LOOP;
DBMS_LOB.WRITE(docBody, LENGTH( CHR(10)||'/'||CHR(10)),
DBMS_LOB.GETLENGTH( docBody)+1, CHR(10)||'/'||CHR(10));

DBMS_LOB.WRITE(docBody, LENGTH( '</PRE>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '</PRE>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</FONT></TT>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</FONT></TT>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</HTML>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</HTML>'||CHR(10));

END getUnitSource;

/* **************************************
Get type status
* **************************************/
PROCEDURE getTypeStatus(IN_OBJECT VARCHAR2, IN_OBJECT_TYPE VARCHAR2)
IS

is_status BOOLEAN;

BEGIN

is_status := FALSE;

DBMS_LOB.WRITE(docBody, LENGTH( '<P>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '<P>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TABLE WIDTH=100% CELLPADDING=3
BORDER=2>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TABLE
WIDTH=100% CELLPADDING=3 BORDER=2>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=#ccccff><FONT
FACE="ARIAL" SIZE=4>Type Status</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD BGCOLOR=#ccccff><FONT FACE="ARIAL"
SIZE=4>Type Status</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TABLE>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TABLE>'||CHR(10));

DBMS_LOB.WRITE(docBody, LENGTH( '<TABLE WIDTH=100% CELLPADDING=2


BORDER=2>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TABLE
WIDTH=100% CELLPADDING=2 BORDER=2>'||CHR(10));

FOR lrec IN (
select a.typecode,
TO_CHAR(a.attributes) AS attributes,
TO_CHAR(a.methods) AS methods,
TO_CHAR(b.created,'MM/DD/YYYY HH24:MI:SS') as created,
TO_CHAR(b.last_ddl_time,'MM/DD/YYYY HH24:MI:SS') as
last_update,
b.status
FROM user_types a,
user_objects b
WHERE type_name = IN_OBJECT
AND b.object_name = a.type_name
AND object_type = IN_OBJECT_TYPE)
LOOP
is_status := TRUE;

DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
VALIGN=top WIDTH=100><B><FONT FACE="ARIAL" SIZE=2>Type
Code</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TD
BGCOLOR=LEMONCHIFFON VALIGN=top WIDTH=100><B><FONT FACE="ARIAL"
SIZE=2>Type Code</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||lrec.typecode||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT FACE="ARIAL"
SIZE=2>'||lrec.typecode||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));

DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
VALIGN=top WIDTH=100><B><FONT FACE="ARIAL"
SIZE=2>Status</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)
+1, '<TD BGCOLOR=LEMONCHIFFON VALIGN=top WIDTH=100><B><FONT FACE="ARIAL"
SIZE=2>Status</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||lrec.status||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT FACE="ARIAL"
SIZE=2>'||lrec.status||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));

DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
VALIGN=top WIDTH=100><B><FONT FACE="ARIAL"
SIZE=2>Created</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)
+1, '<TD BGCOLOR=LEMONCHIFFON VALIGN=top WIDTH=100><B><FONT FACE="ARIAL"
SIZE=2>Created</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||lrec.created||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT FACE="ARIAL"
SIZE=2>'||lrec.created||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));

DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
VALIGN=top WIDTH=100><B><FONT FACE="ARIAL" SIZE=2>Last
update</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TD
BGCOLOR=LEMONCHIFFON VALIGN=top WIDTH=100><B><FONT FACE="ARIAL"
SIZE=2>Last update</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||lrec.last_update||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT FACE="ARIAL"
SIZE=2>'||lrec.last_update||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));

DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
VALIGN=top WIDTH=100><B><FONT FACE="ARIAL"
SIZE=2>Attributes</FONT></B></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD BGCOLOR=LEMONCHIFFON VALIGN=top
WIDTH=100><B><FONT FACE="ARIAL" SIZE=2>Attributes</FONT></B></TD>'||
CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||lrec.attributes||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT FACE="ARIAL"
SIZE=2>'||lrec.attributes||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));

DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
VALIGN=top WIDTH=100><B><FONT FACE="ARIAL"
SIZE=2>Methods</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)
+1, '<TD BGCOLOR=LEMONCHIFFON VALIGN=top WIDTH=100><B><FONT FACE="ARIAL"
SIZE=2>Methods</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||lrec.methods||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT FACE="ARIAL"
SIZE=2>'||lrec.methods||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));

END LOOP;

IF NOT is_status
THEN
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD ALIGN=CENTER
VALIGN=top><FONT FACE="ARIAL" SIZE=2>NOT FOUND</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD ALIGN=CENTER VALIGN=top><FONT
FACE="ARIAL" SIZE=2>NOT FOUND</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));
END IF;

DBMS_LOB.WRITE(docBody, LENGTH( '</TABLE>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '</TABLE>'||CHR(10));

END getTypeStatus;

/* **************************************
Get type attributes
* **************************************/
PROCEDURE getTypeAttrs(IN_OBJECT VARCHAR2)
IS
is_attrs BOOLEAN;
BEGIN

-- Build table with possible privs for package/proc


DBMS_LOB.WRITE(docBody, LENGTH( CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, CHR(10) );
DBMS_LOB.WRITE(docBody, LENGTH( '<P>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<P>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TABLE WIDTH=100% CELLPADDING=3
BORDER=2>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TABLE
WIDTH=100% CELLPADDING=3 BORDER=2>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=#ccccff><FONT
FACE="ARIAL" SIZE=4>Type Attributes</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD BGCOLOR=#ccccff><FONT FACE="ARIAL"
SIZE=4>Type Attributes</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TABLE>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TABLE>'||CHR(10));

DBMS_LOB.WRITE(docBody, LENGTH( '<TABLE WIDTH=100% CELLPADDING=2


BORDER=2>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TABLE
WIDTH=100% CELLPADDING=2 BORDER=2>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=100><B><FONT FACE="ARIAL" SIZE=2>Attribute
Name</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TD
BGCOLOR=LEMONCHIFFON ALIGN=CENTER VALIGN=top WIDTH=100><B><FONT
FACE="ARIAL" SIZE=2>Attribute Name</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=100><B><FONT FACE="ARIAL" SIZE=2>Attr Type
Name</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TD
BGCOLOR=LEMONCHIFFON ALIGN=CENTER VALIGN=top WIDTH=100><B><FONT
FACE="ARIAL" SIZE=2>Attr Type Name</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=100><B><FONT FACE="ARIAL"
SIZE=2>Length</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)
+1, '<TD BGCOLOR=LEMONCHIFFON ALIGN=CENTER VALIGN=top WIDTH=100><B><FONT
FACE="ARIAL" SIZE=2>Length</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=100><B><FONT FACE="ARIAL"
SIZE=2>Precision</FONT></B></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD BGCOLOR=LEMONCHIFFON ALIGN=CENTER
VALIGN=top WIDTH=100><B><FONT FACE="ARIAL"
SIZE=2>Precision</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=100><B><FONT FACE="ARIAL"
SIZE=2>Scale</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)
+1, '<TD BGCOLOR=LEMONCHIFFON ALIGN=CENTER VALIGN=top WIDTH=100><B><FONT
FACE="ARIAL" SIZE=2>Scale</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));

-- Fill out table


is_attrs := FALSE;

FOR lrec IN (
SELECT attr_name,
attr_type_name,
TO_CHAR(length) AS length,
TO_CHAR(precision) AS precision,
TO_CHAR(scale) AS scale
FROM user_type_attrs
WHERE type_name = IN_OBJECT
ORDER BY attr_no)
LOOP

is_attrs := TRUE;

DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||lrec.attr_name||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT FACE="ARIAL"
SIZE=2>'||lrec.attr_name||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD ALIGN=CENTER
VALIGN=top><FONT FACE="ARIAL" SIZE=2>'||
lrec.attr_type_name||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD ALIGN=CENTER VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||lrec.attr_type_name||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD ALIGN=CENTER
VALIGN=top><FONT FACE="ARIAL" SIZE=2>'||lrec.length||'</FONT></TD>'||
CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TD ALIGN=CENTER
VALIGN=top><FONT FACE="ARIAL" SIZE=2>'||lrec.length||'</FONT></TD>'||
CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD ALIGN=CENTER
VALIGN=top><FONT FACE="ARIAL" SIZE=2>'||lrec.precision||'</FONT></TD>'||
CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TD ALIGN=CENTER
VALIGN=top><FONT FACE="ARIAL" SIZE=2>'||lrec.precision||'</FONT></TD>'||
CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD ALIGN=CENTER
VALIGN=top><FONT FACE="ARIAL" SIZE=2>'||lrec.scale||'</FONT></TD>'||
CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TD ALIGN=CENTER
VALIGN=top><FONT FACE="ARIAL" SIZE=2>'||lrec.scale||'</FONT></TD>'||
CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));

END LOOP;

IF NOT is_attrs
THEN
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD ALIGN=CENTER
VALIGN=top><FONT FACE="ARIAL" SIZE=2>NOT FOUND</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD ALIGN=CENTER VALIGN=top><FONT
FACE="ARIAL" SIZE=2>NOT FOUND</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));
END IF;

DBMS_LOB.WRITE(docBody, LENGTH( '</TABLE>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '</TABLE>'||CHR(10));

END getTypeAttrs;
/* **************************************
Get list of type methods
* **************************************/
PROCEDURE getTypeMethods(IN_OBJECT VARCHAR2)
IS

is_methods BOOLEAN;
lb_function BOOLEAN;
BEGIN

DBMS_LOB.WRITE(docBody, LENGTH( CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, CHR(10) );
DBMS_LOB.WRITE(docBody, LENGTH( '<P>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<P>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TABLE WIDTH=100% CELLPADDING=3
BORDER=2>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TABLE
WIDTH=100% CELLPADDING=3 BORDER=2>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=#ccccff><FONT
FACE="ARIAL" SIZE=4>Type Methods</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD BGCOLOR=#ccccff><FONT FACE="ARIAL"
SIZE=4>Type Methods</FONT></TD>'||CHR(10) );
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TABLE>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TABLE>'||CHR(10));

DBMS_LOB.WRITE(docBody, LENGTH( '<TABLE WIDTH=100% CELLPADDING=2


BORDER=2>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TABLE
WIDTH=100% CELLPADDING=2 BORDER=2>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=100><B><FONT FACE="ARIAL" SIZE=2>Method
Name</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TD
BGCOLOR=LEMONCHIFFON ALIGN=CENTER VALIGN=top WIDTH=100><B><FONT
FACE="ARIAL" SIZE=2>Method Name</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=100><B><FONT FACE="ARIAL" SIZE=2>Parameter
Name</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TD
BGCOLOR=LEMONCHIFFON ALIGN=CENTER VALIGN=top WIDTH=100><B><FONT
FACE="ARIAL" SIZE=2>Parameter Name</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=100><B><FONT FACE="ARIAL" SIZE=2>Parameter
Mode</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TD
BGCOLOR=LEMONCHIFFON ALIGN=CENTER VALIGN=top WIDTH=100><B><FONT
FACE="ARIAL" SIZE=2>Parameter Mode</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=100><B><FONT FACE="ARIAL" SIZE=2>Parameter
Type</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TD
BGCOLOR=LEMONCHIFFON ALIGN=CENTER VALIGN=top WIDTH=100><B><FONT
FACE="ARIAL" SIZE=2>Parameter Type</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));

-- Fill out table


is_methods := FALSE;

FOR lrec IN (
SELECT DISTINCT method_name, method_no
FROM user_method_params
WHERE type_name = IN_OBJECT
ORDER BY method_no)
LOOP

DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2><B>'||lrec.method_name||'</B></FONT></TD>'||
CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2><B>'||lrec.method_name||'</B></FONT></TD>'||
CHR(10));

is_methods := TRUE;
lb_function := FALSE;

FOR lrec_arg IN (
SELECT param_name,
param_mode,
param_type_name
FROM user_method_params
WHERE type_name = IN_OBJECT
AND method_name = lrec.method_name
ORDER BY method_no, param_no)
LOOP

IF lb_function = TRUE
THEN
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2></FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT FACE="ARIAL"
SIZE=2></FONT></TD>'||CHR(10));
END IF;

-- Parameter Name
DBMS_LOB.WRITE(docBody, LENGTH( '<TD ALIGN=CENTER
VALIGN=top><FONT FACE="ARIAL" SIZE=2>'||
lrec_arg.param_name||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD ALIGN=CENTER VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||lrec_arg.param_name||'</FONT></TD>'||CHR(10));

-- Param mode
DBMS_LOB.WRITE(docBody, LENGTH( '<TD ALIGN=CENTER
VALIGN=top><FONT FACE="ARIAL" SIZE=2>'||
lrec_arg.param_mode||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD ALIGN=CENTER VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||lrec_arg.param_mode||'</FONT></TD>'||CHR(10));

-- Param Datatype
DBMS_LOB.WRITE(docBody, LENGTH( '<TD ALIGN=CENTER
VALIGN=top><FONT FACE="ARIAL" SIZE=2>'||
lrec_arg.param_type_name||'</FONT></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH(
docBody)+1, '<TD ALIGN=CENTER VALIGN=top><FONT FACE="ARIAL" SIZE=2>'||
lrec_arg.param_type_name||'</FONT></TD>'||CHR(10));

DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));
lb_function := TRUE;

END LOOP;

END LOOP;

IF NOT is_methods
THEN
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD ALIGN=CENTER
VALIGN=top><FONT FACE="ARIAL" SIZE=2>NOT FOUND</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD ALIGN=CENTER VALIGN=top><FONT
FACE="ARIAL" SIZE=2>NOT FOUND</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));
END IF;

DBMS_LOB.WRITE(docBody, LENGTH( '</TABLE>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '</TABLE>'||CHR(10));

END getTypeMethods;

/* **************************************
Get table columns
* **************************************/
PROCEDURE getTblCols(IN_OBJECT VARCHAR2)
IS

BEGIN

DBMS_LOB.WRITE(docBody, LENGTH( '<P>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '<P>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<HTML>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<HTML>'||CHR(10));

DBMS_LOB.WRITE(docBody, LENGTH( '</TABLE>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '</TABLE>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TABLE WIDTH=100% CELLPADDING=3
BORDER=2>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TABLE
WIDTH=100% CELLPADDING=3 BORDER=2>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=#ccccff><FONT
FACE="ARIAL" SIZE=4>Columns</FONT></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH(
docBody)+1, '<TD BGCOLOR=#ccccff><FONT FACE="ARIAL"
SIZE=4>Columns</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TABLE>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TABLE>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TABLE WIDTH=100% CELLPADDING=2
BORDER=2>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TABLE
WIDTH=100% CELLPADDING=2 BORDER=2>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=240><B><FONT FACE="ARIAL"
SIZE=2>Name</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1,
'<TD BGCOLOR=LEMONCHIFFON ALIGN=CENTER VALIGN=top WIDTH=240><B><FONT
FACE="ARIAL" SIZE=2>Name</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=160><B><FONT FACE="ARIAL"
SIZE=2>Datatype</FONT></B></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD BGCOLOR=LEMONCHIFFON ALIGN=CENTER
VALIGN=top WIDTH=160><B><FONT FACE="ARIAL"
SIZE=2>Datatype</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=80><B><FONT FACE="ARIAL"
SIZE=2>Datalength</FONT></B></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD BGCOLOR=LEMONCHIFFON ALIGN=CENTER
VALIGN=top WIDTH=80><B><FONT FACE="ARIAL"
SIZE=2>Datalength</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=80><B><FONT FACE="ARIAL"
SIZE=2>Precision</FONT></B></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD BGCOLOR=LEMONCHIFFON ALIGN=CENTER
VALIGN=top WIDTH=80><B><FONT FACE="ARIAL"
SIZE=2>Precision</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=80><B><FONT FACE="ARIAL"
SIZE=2>Scale</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)
+1, '<TD BGCOLOR=LEMONCHIFFON ALIGN=CENTER VALIGN=top WIDTH=80><B><FONT
FACE="ARIAL" SIZE=2>Scale</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=80><B><FONT FACE="ARIAL"
SIZE=2>Null</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1,
'<TD BGCOLOR=LEMONCHIFFON ALIGN=CENTER VALIGN=top WIDTH=80><B><FONT
FACE="ARIAL" SIZE=2>Null</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=120><B><FONT FACE="ARIAL"
SIZE=2>Default</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)
+1, '<TD BGCOLOR=LEMONCHIFFON ALIGN=CENTER VALIGN=top WIDTH=120><B><FONT
FACE="ARIAL" SIZE=2>Default</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=200><B><FONT FACE="ARIAL"
SIZE=2>Comment</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)
+1, '<TD BGCOLOR=LEMONCHIFFON ALIGN=CENTER VALIGN=top WIDTH=200><B><FONT
FACE="ARIAL" SIZE=2>Comment</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));

FOR lrec IN (
SELECT a.column_name,
DECODE(NVL(a.data_type_owner, '*'), '*', '',
a.data_type_owner||'.')||a.data_type as Data_Type,
a.data_length,
TO_CHAR(a.data_precision) as data_precision,
TO_CHAR(a.data_scale) AS data_scale,
DECODE(a.nullable, 'N', 'No', 'Yes') as nullable,
a.data_default,
b.comments
FROM user_tab_columns a,
user_col_comments b
WHERE a.table_name = IN_OBJECT
AND b.table_name = a.table_name
AND b.column_name = a.column_name
ORDER BY a.column_id)
LOOP
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||lrec.column_name||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT FACE="ARIAL"
SIZE=2>'||lrec.column_name||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||lrec.Data_Type||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT FACE="ARIAL"
SIZE=2>'||lrec.Data_Type||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||lrec.data_length||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT FACE="ARIAL"
SIZE=2>'||lrec.data_length||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||lrec.data_precision||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT FACE="ARIAL"
SIZE=2>'||lrec.data_precision||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||lrec.data_scale||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT FACE="ARIAL"
SIZE=2>'||lrec.data_scale||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||lrec.nullable||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT FACE="ARIAL"
SIZE=2>'||lrec.nullable||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||lrec.data_default||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT FACE="ARIAL"
SIZE=2>'||lrec.data_default||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||lrec.comments||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT FACE="ARIAL"
SIZE=2>'||lrec.comments||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));
END LOOP;

DBMS_LOB.WRITE(docBody, LENGTH( '</TABLE>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '</TABLE>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</HTML> '||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</HTML> '||CHR(10));
END getTblCols;

/* **************************************
Get Table Constraints
* **************************************/
PROCEDURE getTblConstraints(IN_OBJECT VARCHAR2)
IS
is_cons BOOLEAN;
BEGIN

DBMS_LOB.WRITE(docBody, LENGTH( '<P>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '<P>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<HTML>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<HTML>'||CHR(10));

DBMS_LOB.WRITE(docBody, LENGTH( '</TABLE>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '</TABLE>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TABLE WIDTH=100% CELLPADDING=3
BORDER=2>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TABLE
WIDTH=100% CELLPADDING=3 BORDER=2>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=#ccccff><FONT
FACE="ARIAL" SIZE=4>Constraints</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD BGCOLOR=#ccccff><FONT FACE="ARIAL"
SIZE=4>Constraints</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TABLE>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TABLE>'||CHR(10));

DBMS_LOB.WRITE(docBody, LENGTH( '<TABLE WIDTH=100% CELLPADDING=2


BORDER=2>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TABLE
WIDTH=100% CELLPADDING=2 BORDER=2>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=240><B><FONT FACE="ARIAL"
SIZE=2>Name</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1,
'<TD BGCOLOR=LEMONCHIFFON ALIGN=CENTER VALIGN=top WIDTH=240><B><FONT
FACE="ARIAL" SIZE=2>Name</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=160><B><FONT FACE="ARIAL"
SIZE=2>Type</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1,
'<TD BGCOLOR=LEMONCHIFFON ALIGN=CENTER VALIGN=top WIDTH=160><B><FONT
FACE="ARIAL" SIZE=2>Type</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=80><B><FONT FACE="ARIAL"
SIZE=2>Constraint</FONT></B></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD BGCOLOR=LEMONCHIFFON ALIGN=CENTER
VALIGN=top WIDTH=80><B><FONT FACE="ARIAL"
SIZE=2>Constraint</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=80><B><FONT FACE="ARIAL"
SIZE=2>Status</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)
+1, '<TD BGCOLOR=LEMONCHIFFON ALIGN=CENTER VALIGN=top WIDTH=80><B><FONT
FACE="ARIAL" SIZE=2>Status</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=80><B><FONT FACE="ARIAL" SIZE=2>Index
name</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TD
BGCOLOR=LEMONCHIFFON ALIGN=CENTER VALIGN=top WIDTH=80><B><FONT
FACE="ARIAL" SIZE=2>Index name</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));

is_cons := FALSE;

FOR lrec IN (
SELECT a.constraint_name,
DECODE( a.constraint_type,
'C', 'Check',
'P', 'Primary key',
'U', 'Unique',
'R', 'Referential',
'V', 'Check option, on a view',
'O', 'Read only, on a view',
'Unknown') AS type,
a.search_condition AS constraint,
a.status,
' ' AS index_name,
b.owner||'.'||b.table_name||' ('||b.constraint_name||')' AS
reference
FROM user_constraints a,
user_constraints b
WHERE a.table_name = IN_OBJECT
AND b.constraint_name(+) = a.r_constraint_name
ORDER BY DECODE( a.constraint_type, 'P', 0, 'U', 1, 'R', 2,
3) )

LOOP

is_cons := TRUE;

DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||lrec.constraint_name||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT FACE="ARIAL"
SIZE=2>'||lrec.constraint_name||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||lrec.type||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT FACE="ARIAL"
SIZE=2>'||lrec.type||'</FONT></TD>'||CHR(10));
IF lrec.type = 'Referential'
THEN
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||lrec.reference||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT FACE="ARIAL"
SIZE=2>'||lrec.reference||'</FONT></TD>'||CHR(10));
ELSE
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||lrec.constraint||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT FACE="ARIAL"
SIZE=2>'||lrec.constraint||'</FONT></TD>'||CHR(10));
END IF;
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||INITCAP(lrec.status)||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT FACE="ARIAL"
SIZE=2>'||INITCAP(lrec.status)||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||lrec.index_name||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT FACE="ARIAL"
SIZE=2>'||lrec.index_name||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));
END LOOP;

IF NOT is_cons
THEN
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD ALIGN=CENTER
VALIGN=top><FONT FACE="ARIAL" SIZE=2>NOT FOUND</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD ALIGN=CENTER VALIGN=top><FONT
FACE="ARIAL" SIZE=2>NOT FOUND</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));
END IF;

DBMS_LOB.WRITE(docBody, LENGTH( '</TABLE>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '</TABLE>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</HTML> '||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</HTML> '||CHR(10));
END getTblConstraints;

/* **************************************
Get all Table privileges
* **************************************/
PROCEDURE getTblPrivs(IN_OBJECT VARCHAR2)
IS
is_privs BOOLEAN;

TYPE c_privs_t IS REF CURSOR;


c_privs_c c_privs_t;

-- :1 = IN_OBJECT
-- :2 = IN_GRANTEE

ref_c VARCHAR2(4000) :=
'SELECT
(SELECT DECODE(privilege, ''DELETE'', ''Y'')
FROM all_tab_privs
WHERE table_name = :1
AND grantee = :2
AND privilege = ''DELETE'') del,
(SELECT DECODE(privilege, ''INSERT'', ''Y'')
FROM all_tab_privs
WHERE table_name = :3
AND grantee = :4
AND privilege = ''INSERT'') ins,
(SELECT DECODE(privilege, ''SELECT'', ''Y'')
FROM all_tab_privs
WHERE table_name = :5
AND grantee = :6
AND privilege = ''SELECT'') sel,
(SELECT DECODE(privilege, ''UPDATE'', ''Y'')
FROM all_tab_privs
WHERE table_name = :7
AND grantee = :8
AND privilege = ''UPDATE'') upd
FROM DUAL';

privs_data_del VARCHAR2(30);
privs_data_ins VARCHAR2(30);
privs_data_sel VARCHAR2(30);
privs_data_upd VARCHAR2(30);

BEGIN

-- Build table with privileges


DBMS_LOB.WRITE(docBody, LENGTH( CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, CHR(10) );
DBMS_LOB.WRITE(docBody, LENGTH( '<P>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<P>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TABLE WIDTH=100% CELLPADDING=3
BORDER=2>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TABLE
WIDTH=100% CELLPADDING=3 BORDER=2>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=#ccccff><FONT
FACE="ARIAL" SIZE=4>Privileges</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD BGCOLOR=#ccccff><FONT FACE="ARIAL"
SIZE=4>Privileges</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TABLE>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TABLE>'||CHR(10));

DBMS_LOB.WRITE(docBody, LENGTH( '<TABLE WIDTH=100% CELLPADDING=2


BORDER=2>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TABLE
WIDTH=100% CELLPADDING=2 BORDER=2>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=100><B><FONT FACE="ARIAL"
SIZE=2>Grantee</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)
+1, '<TD BGCOLOR=LEMONCHIFFON ALIGN=CENTER VALIGN=top WIDTH=100><B><FONT
FACE="ARIAL" SIZE=2>Object Name</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=100><B><FONT FACE="ARIAL"
SIZE=2>Delete</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)
+1, '<TD BGCOLOR=LEMONCHIFFON ALIGN=CENTER VALIGN=top WIDTH=100><B><FONT
FACE="ARIAL" SIZE=2>Delete</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=100><B><FONT FACE="ARIAL"
SIZE=2>Insert</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)
+1, '<TD BGCOLOR=LEMONCHIFFON ALIGN=CENTER VALIGN=top WIDTH=100><B><FONT
FACE="ARIAL" SIZE=2>Insert</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=100><B><FONT FACE="ARIAL"
SIZE=2>Select</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)
+1, '<TD BGCOLOR=LEMONCHIFFON ALIGN=CENTER VALIGN=top WIDTH=100><B><FONT
FACE="ARIAL" SIZE=2>Select</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=100><B><FONT FACE="ARIAL"
SIZE=2>Update</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)
+1, '<TD BGCOLOR=LEMONCHIFFON ALIGN=CENTER VALIGN=top WIDTH=100><B><FONT
FACE="ARIAL" SIZE=2>Update</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));

-- Fill out table with Privs


is_privs := FALSE;

FOR lrec IN (
SELECT DISTINCT grantee
FROM all_tab_privs
WHERE table_name = IN_OBJECT)
LOOP
is_privs := TRUE;
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||lrec.grantee||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT FACE="ARIAL"
SIZE=2>'||lrec.grantee||'</FONT></TD>'||CHR(10));

OPEN c_privs_c FOR ref_c


USING IN_OBJECT, lrec.grantee, IN_OBJECT, lrec.grantee,
IN_OBJECT, lrec.grantee, IN_OBJECT, lrec.grantee;
LOOP
FETCH c_privs_c INTO privs_data_del, privs_data_ins,
privs_data_sel, privs_data_upd;
EXIT WHEN c_privs_c%NOTFOUND;

DBMS_LOB.WRITE(docBody, LENGTH( '<TD ALIGN=CENTER


VALIGN=top><FONT FACE="ARIAL" SIZE=2>'||privs_data_del||'</FONT></TD>'||
CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TD ALIGN=CENTER
VALIGN=top><FONT FACE="ARIAL" SIZE=2>'||privs_data_del||'</FONT></TD>'||
CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD ALIGN=CENTER
VALIGN=top><FONT FACE="ARIAL" SIZE=2>'||privs_data_ins||'</FONT></TD>'||
CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TD ALIGN=CENTER
VALIGN=top><FONT FACE="ARIAL" SIZE=2>'||privs_data_ins||'</FONT></TD>'||
CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD ALIGN=CENTER
VALIGN=top><FONT FACE="ARIAL" SIZE=2>'||privs_data_sel||'</FONT></TD>'||
CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TD ALIGN=CENTER
VALIGN=top><FONT FACE="ARIAL" SIZE=2>'||privs_data_sel||'</FONT></TD>'||
CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD ALIGN=CENTER
VALIGN=top><FONT FACE="ARIAL" SIZE=2>'||privs_data_upd||'</FONT></TD>'||
CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TD ALIGN=CENTER
VALIGN=top><FONT FACE="ARIAL" SIZE=2>'||privs_data_upd||'</FONT></TD>'||
CHR(10));
END LOOP;
CLOSE c_privs_c;

DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));
END LOOP;

IF NOT is_privs
THEN
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD ALIGN=CENTER
VALIGN=top><FONT FACE="ARIAL" SIZE=2>NOT FOUND</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD ALIGN=CENTER VALIGN=top><FONT
FACE="ARIAL" SIZE=2>NOT FOUND</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));
END IF;

DBMS_LOB.WRITE(docBody, LENGTH( '</TABLE>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '</TABLE>'||CHR(10));
END getTblPrivs;

/* **************************************
Get Table Indexes
* **************************************/
PROCEDURE getTblIndexes(IN_OBJECT VARCHAR2)
IS
is_idx BOOLEAN;
TYPE col_type IS TABLE OF user_ind_columns.column_name%TYPE INDEX
BY BINARY_INTEGER;
col_r col_type;
lv_col_names VARCHAR2(500);
BEGIN

DBMS_LOB.WRITE(docBody, LENGTH( '<P>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '<P>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<HTML>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<HTML>'||CHR(10));

DBMS_LOB.WRITE(docBody, LENGTH( '</TABLE>'||CHR(10) ),


DBMS_LOB.GETLENGTH( docBody)+1, '</TABLE>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TABLE WIDTH=100% CELLPADDING=3
BORDER=2>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TABLE
WIDTH=100% CELLPADDING=3 BORDER=2>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=#ccccff><FONT
FACE="ARIAL" SIZE=4>Indexes</FONT></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH(
docBody)+1, '<TD BGCOLOR=#ccccff><FONT FACE="ARIAL"
SIZE=4>Indexes</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TABLE>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TABLE>'||CHR(10));

DBMS_LOB.WRITE(docBody, LENGTH( '<TABLE WIDTH=100% CELLPADDING=2


BORDER=2>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1, '<TABLE
WIDTH=100% CELLPADDING=2 BORDER=2>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=240><B><FONT FACE="ARIAL"
SIZE=2>Name</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1,
'<TD BGCOLOR=LEMONCHIFFON ALIGN=CENTER VALIGN=top WIDTH=240><B><FONT
FACE="ARIAL" SIZE=2>Name</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=100><B><FONT FACE="ARIAL"
SIZE=2>Type</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1,
'<TD BGCOLOR=LEMONCHIFFON ALIGN=CENTER VALIGN=top WIDTH=100><B><FONT
FACE="ARIAL" SIZE=2>Type</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=240><B><FONT FACE="ARIAL"
SIZE=2>Columns</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)
+1, '<TD BGCOLOR=LEMONCHIFFON ALIGN=CENTER VALIGN=top WIDTH=240><B><FONT
FACE="ARIAL" SIZE=2>Columns</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=80><B><FONT FACE="ARIAL"
SIZE=2>Uniqueness</FONT></B></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD BGCOLOR=LEMONCHIFFON ALIGN=CENTER
VALIGN=top WIDTH=80><B><FONT FACE="ARIAL"
SIZE=2>Uniqueness</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=80><B><FONT FACE="ARIAL"
SIZE=2>Status</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)
+1, '<TD BGCOLOR=LEMONCHIFFON ALIGN=CENTER VALIGN=top WIDTH=80><B><FONT
FACE="ARIAL" SIZE=2>Status</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD BGCOLOR=LEMONCHIFFON
ALIGN=CENTER VALIGN=top WIDTH=80><B><FONT FACE="ARIAL" SIZE=2>Last
Analyzed</FONT></B></TD>'||CHR(10) ), DBMS_LOB.GETLENGTH( docBody)+1,
'<TD BGCOLOR=LEMONCHIFFON ALIGN=CENTER VALIGN=top WIDTH=80><B><FONT
FACE="ARIAL" SIZE=2>Last Analyzed</FONT></B></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));

is_idx := FALSE;

FOR lrec IN (
SELECT index_name,
index_type,
uniqueness,
status,
TO_CHAR(last_analyzed, 'MM/DD/YYYY HH24:MI:SS') as
last_analyzed
FROM all_indexes
WHERE table_name = IN_OBJECT)
LOOP
is_idx := TRUE;

-- get index columns


SELECT column_name
BULK COLLECT INTO col_r
FROM all_ind_columns
WHERE index_name = lrec.index_name
ORDER BY column_position;

lv_col_names :='';
FOR I IN col_r.FIRST..col_r.LAST
LOOP
lv_col_names := lv_col_names||col_r(I)||',';
END LOOP;

lv_col_names := '('||SUBSTR( lv_col_names, 1,


LENGTH(lv_col_names)-1)||')';

-- print index deltails


DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||lrec.index_name||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT FACE="ARIAL"
SIZE=2>'||lrec.index_name||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||lrec.index_type||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT FACE="ARIAL"
SIZE=2>'||lrec.index_type||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||lv_col_names||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT FACE="ARIAL"
SIZE=2>'||lv_col_names||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||lrec.uniqueness||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT FACE="ARIAL"
SIZE=2>'||lrec.uniqueness||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||lrec.status||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT FACE="ARIAL"
SIZE=2>'||lrec.status||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD VALIGN=top><FONT
FACE="ARIAL" SIZE=2>'||lrec.last_analyzed||'</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD VALIGN=top><FONT FACE="ARIAL"
SIZE=2>'||lrec.last_analyzed||'</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));
END LOOP;

IF NOT is_idx
THEN
DBMS_LOB.WRITE(docBody, LENGTH( '<TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TR>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '<TD ALIGN=CENTER
VALIGN=top><FONT FACE="ARIAL" SIZE=2>NOT FOUND</FONT></TD>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '<TD ALIGN=CENTER VALIGN=top><FONT
FACE="ARIAL" SIZE=2>NOT FOUND</FONT></TD>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</TR>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TR>'||CHR(10));
END IF;
DBMS_LOB.WRITE(docBody, LENGTH( '</TABLE>'||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</TABLE>'||CHR(10));
DBMS_LOB.WRITE(docBody, LENGTH( '</HTML> '||CHR(10) ),
DBMS_LOB.GETLENGTH( docBody)+1, '</HTML> '||CHR(10));
END getTblIndexes;

/* *****************************************
EMAIL
********************************************/
/*
************************************************************************
****
Send email with attachment. Attachment is a CLOB parameter
Parameters
IN_FROM_EMAIL - sender
IN_TO_EMAILS - list of emails separated by ; or ,
IN_CC_EMAILS - list of emails separated by ; or ,
IN_SUBJ - email's Subject
IN_TEXT - email's body

IN_FILENAME - attached file name


IN_CONTENT - attachemnt content

-- CHANGE HISTORY:
12/28/2005 email created

*
************************************************************************
*****/
PROCEDURE sendEmailAttachments(
IN_FROM_EMAIL VARCHAR2,
IN_TO_EMAILS VARCHAR2,
IN_CC_EMAILS VARCHAR2,
IN_SUBJ VARCHAR2,
IN_TEXT CLOB,
IN_MIME_TYPE VARCHAR2 DEFAULT 'text/plain',
IN_FILENAME_1 VARCHAR2,
IN_CONTENT_1 CLOB,
IN_FILENAME_2 VARCHAR2 DEFAULT NULL,
IN_CONTENT_2 CLOB DEFAULT NULL,
IN_FILENAME_3 VARCHAR2 DEFAULT NULL,
IN_CONTENT_3 CLOB DEFAULT NULL)
IS
lv_sender VARCHAR2(2000);
lv_to_email VARCHAR2(2000);
lv_cc_email VARCHAR2(2000);
lv_subj VARCHAR2(500);
lv_msg VARCHAR2(32767);

conn_ UTL_SMTP.CONNECTION;

boundary CONSTANT VARCHAR2(256) :=


'CES.Boundary.DACA587499938898';
le_params_exception EXCEPTION;
BEGIN
-- check parameters
IF (IN_FROM_EMAIL IS NULL OR IN_TO_EMAILS IS NULL)
THEN
RAISE le_params_exception;
END IF;

/* *****************************************
Email
********************************************/
-- set email header and email body
conn_ := setBeginAttachEmail(
IN_FROM_EMAIL,
IN_TO_EMAILS,
IN_CC_EMAILS,
IN_SUBJ,
IN_TEXT,
IN_MIME_TYPE);

-- *********** add attachment 1


lv_msg := utl_tcp.CRLF || '--' || boundary || utl_tcp.CRLF;-- ||
lv_msg := lv_msg || 'Content-Type: application/octet-stream;
name="' ||
IN_FILENAME_1 || '"' || utl_tcp.CRLF ||
'Content-Disposition: attachment; filename="' ||
IN_FILENAME_1 || '"' || utl_tcp.CRLF ||
'Content-Transfer-Encoding: 7bit' || utl_tcp.CRLF ||
utl_tcp.CRLF ;
utl_smtp.write_data( conn_, lv_msg );

-- Attachment Content:
setEmailBody( conn_, IN_CONTENT_1);

utl_smtp.write_data ( conn_, utl_tcp.CRLF );

-- *********** add attachment 2


IF IN_FILENAME_2 IS NOT NULL
THEN
lv_msg := utl_tcp.CRLF || '--' || boundary || utl_tcp.CRLF;--
||
lv_msg := lv_msg || 'Content-Type: application/octet-stream;
name="' ||
IN_FILENAME_2 || '"' || utl_tcp.CRLF ||
'Content-Disposition: attachment; filename="' ||
IN_FILENAME_2 || '"' || utl_tcp.CRLF ||
'Content-Transfer-Encoding: 7bit' || utl_tcp.CRLF ||
utl_tcp.CRLF ;
utl_smtp.write_data( conn_, lv_msg );

-- Attachment Content:
setEmailBody( conn_, IN_CONTENT_2);

utl_smtp.write_data ( conn_, utl_tcp.CRLF );


END IF;

-- *********** add attachment 3


IF IN_FILENAME_3 IS NOT NULL
THEN
lv_msg := utl_tcp.CRLF || '--' || boundary || utl_tcp.CRLF;--
||
lv_msg := lv_msg || 'Content-Type: application/octet-stream;
name="' ||
IN_FILENAME_3 || '"' || utl_tcp.CRLF ||
'Content-Disposition: attachment; filename="' ||
IN_FILENAME_3 || '"' || utl_tcp.CRLF ||
'Content-Transfer-Encoding: 7bit' || utl_tcp.CRLF ||
utl_tcp.CRLF ;
utl_smtp.write_data( conn_, lv_msg );

-- Attachment Content:
setEmailBody( conn_, IN_CONTENT_3);

utl_smtp.write_data ( conn_, utl_tcp.CRLF );


END IF;

-- end attachment
setEndEmail( conn_);

EXCEPTION
WHEN le_params_exception
THEN
RAISE_APPLICATION_ERROR (-20401, 'sendEmail: wrong parameters',
TRUE);
WHEN others
THEN
RAISE;
END sendEmailAttachments;

END documentator;
/
show error

You might also like