Sei sulla pagina 1di 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-dec1997;

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)

from

|| ' 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 ...

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:


dual; select sysdate NOW, sysdate+30/(24*60*60) NOW_PLUS_30_SECS from NOW NOW_PLUS_30_SECS -------------------- -------------------03-JUL-2002 16:47:23 03-JUL-2002 16:47:53

Here are a couple of examples:


Description Now Tomorow/ next day Seven days from now One hour from now Three hours from now SYSDATE SYSDATE + 1 SYSDATE + 7 SYSDATE + 1/24 SYSDATE + 3/24 Date Expression

An half hour from now 10 minutes from now 30 seconds from now Tomorrow at 12 midnight Tomorrow at 8 AM Next Monday at 12:00 noon First day of next month at 12 midnight First day of the current month The next Monday, Wednesday or Friday at 9 a.m

SYSDATE + 1/48 SYSDATE + 10/1440 SYSDATE + 30/86400 TRUNC(SYSDATE + 1) TRUNC(SYSDATE + 1) + 8/24 NEXT_DAY(TRUNC(SYSDATE), 'MONDAY') + 12/24 TRUNC(LAST_DAY(SYSDATE ) + 1) TRUNC(LAST_DAY(ADD_MONTHS(SYSDATE,-1))) + 1 TRUNC(LEAST(NEXT_DAY(sysdate,''MONDAY'' ),NEXT_DAY(sysdate,''WEDNESDAY''), 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", 30-59", 00-29" from group sum(decode(greatest(f1,30), least(f1, 59), 1, 0)) "Range sum(decode(greatest(f1, 0), least(f1, 29), 1, 0)) "Range my_table 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, 1, 2, 3, from my_table; 0.0, 0.1, 0.2, 0.31) "Tax rate"

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 WHERE rownum MINUS SELECT rowid WHERE rownum ( FROM tableX <= 7 FROM tableX < 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 from connect start LEVEL, EMPNO, ENAME, MGR EMP by prior EMPNO = MGR 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 rowby-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)) sum(decode(deptno,20,sal)) sum(decode(deptno,30,sal)) sum(decode(deptno,40,sal)) FROM scott.emp GROUP BY job) ORDER BY 1;

DEPT10, DEPT20, DEPT30, DEPT40

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


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

SELECT 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

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 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

tables

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 o

General PL/SQL: Scripts


o loop

Update/ delete from a huge table with intermittent commits


update tab1 set col1 = 'value2' where rowid = c1.rowid; i := i + 1; if i > 10000 then commit; i := 0; -- Commit after every X records

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 := begin -- Populate open c1 for loop exit when 1; the PL/SQL table from the cursor select * from dept; 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 function dec2bin (N in number) RETURN function oct2dec (octval in char ) RETURN function dec2oct (N in number) RETURN function hex2dec (hexval in char ) RETURN function dec2hex (N in number) RETURN END dbms_numsystem; / show errors number; varchar2; number; varchar2; number; varchar2;

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) select dbms_numsystem.bin2dec('10110') select dbms_numsystem.dec2oct(44978) select dbms_numsystem.oct2dec(127662) select dbms_numsystem.dec2hex(44978) select dbms_numsystem.hex2dec('AFB2') o o from from from from from from dual; dual; dual; dual; dual; dual;

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 increment "2^32" "2^16" "0x7fff" Seed

constant constant constant constant constant number

number number number number number

:= := := := := :=

22695477; 1; 2 ** 32; 2 ** 16; 32767; 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 lob_table ( id INTEGER, b_lob BLOB, c_lob CLOB, b_file BFILE ); -- Create table to hols LOBs

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: 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: WHEN utl_file.read_error THEN RAISE_APPLICATION_ERROR(-20001, 'ERROR: WHEN utl_file.write_error THEN RAISE_APPLICATION_ERROR(-20001, 'ERROR: WHEN utl_file.internal_error THEN RAISE_APPLICATION_ERROR(-20001, 'ERROR: WHEN OTHERS THEN RAISE_APPLICATION_ERROR(-20001, 'ERROR: END; / show errors

utl_file.invalid_mode');

utl_file.invalid_operation'); utl_file.read_error'); utl_file.write_error'); utl_file.internal_error'); utl_file.other_error');

-- 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 { System finalCommand = new String[3]; finalCommand[0] = "/bin/sh"; finalCommand[1] = "-c"; finalCommand[2] = command;

// Linux or Unix

// 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,'YYYYMM-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 data_exception EXCEPTION; EXCEPTION;

/** * Constants - FTP valid response codes * */ CONNECT_CODE USER_CODE LOGIN_CODE PWD_CODE PASV_CODE CWD_CODE TSFR_START_CODE1 TSFR_START_CODE2 TSFR_END_CODE QUIT_CODE SYST_CODE TYPE_CODE CONSTANT CONSTANT CONSTANT CONSTANT CONSTANT CONSTANT CONSTANT CONSTANT CONSTANT CONSTANT CONSTANT CONSTANT PLS_INTEGER PLS_INTEGER PLS_INTEGER PLS_INTEGER PLS_INTEGER PLS_INTEGER PLS_INTEGER PLS_INTEGER PLS_INTEGER PLS_INTEGER PLS_INTEGER PLS_INTEGER := := := := := := := := := := := := 220; 331; 230; 257; 227; 250; 125; 150; 226; 221; 215; 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 filename remotepath filetype transfer_mode status error_message bytes_transmitted trans_start trans_end VARCHAR2(255), VARCHAR2(255), VARCHAR2(255), VARCHAR2(20), VARCHAR2(5), VARCHAR2(40), VARCHAR2(255), NUMBER, DATE, 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) := 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_c md)-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_pasv_cmd; --Host and port to

p_code p_reply

IN PLS_INTEGER, 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

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;

LOOP

v_host n_port n_bytes v_msg v_reply v_err_status BEGIN

VARCHAR2(20) := p_pasv_host; PLS_INTEGER := p_pasv_port; NUMBER; VARCHAR2(255); VARCHAR2(1000); VARCHAR2(20) := 'ERROR';

/** Initialize some of our OUT variables **/ v_status v_error_message n_bytes_transmitted := 'SUCCESS'; := ' '; := 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 + 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

n_bytes;

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 invalid_transfer BEGIN

NUMBER; EXCEPTION; --Assume the overall transfer

p_error_msg := 'FTP Successful'; 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 **/ I'); n_byte_count := UTL_TCP.WRITE_LINE(u_ctrl_con,'TYPE

FALSE

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

transfer **/

/** Get a Passive connection to use for data

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;

method.

WHEN invalid_transfer THEN p_files(i).status := 'ERROR'; p_files(i).error_message := 'Invalid transfer 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 RETURN BOOLEAN IS t_files t_ftp_rec; v_username VARCHAR2(30) v_password VARCHAR2(50) v_hostname VARCHAR2(100) n_port PLS_INTEGER v_err_msg VARCHAR2(255); b_ftp BOOLEAN; BEGIN t_files(1).localpath t_files(1).filename t_files(1).remotepath t_files(1).filetype t_files(1).transfer_mode

:= 'ASCII')

:= := := :=

p_username; p_password; p_hostname; p_port;

:= := := := :=

p_localpath; p_filename; p_remotepath; p_filetype; '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 := v_error_message := n_bytes_transmitted := d_trans_start := d_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;

t_files(1).status; t_files(1).error_message; t_files(1).bytes_transmitted; t_files(1).trans_start; t_files(1).trans_end;

/********************************************************************** ******* ** 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

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 stamp username osuser machine terminal operation objtype objname / dll_audit_log ( DATE, VARCHAR2(30), VARCHAR2(30), VARCHAR2(30), VARCHAR2(30), VARCHAR2(30), VARCHAR2(30), 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,

END; / show errors

ORA_DICT_OBJ_TYPE, ORA_DICT_OBJ_NAME );

-- 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 set serveroutput on size 1000000

for the current schema

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

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, L7)),'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 v_length v_textin v_key

varchar2(300); number(10); varchar2(300); 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] character of key continue with SELECT SUBSTR ( v_textin , SELECT SUBSTR ( v_textin , dbms_output.put_line('Step v_key ); first character of name as first remaining characters'); 1 , 1) into v_key from dual; 2 ) into v_textin from dual; 5 completed first character of 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 o o o o

Package to generate HTML-type documentation for Oracle objects 1) REATE OR REPLACE PACKAGE documentator AS /* **************************************

o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o

Set Dependencies * **************************************/ PROCEDURE getRefObjects( IN_OBJECT VARCHAR2, IN_OBJECT_TYPE VARCHAR2); /* ************************************** Get Table Dependencies * **************************************/ PROCEDURE getTblRefObjects( IN_OBJECT VARCHAR2, IN_OBJECT_TYPE VARCHAR2); /* ************************************** Get common object details * **************************************/ PROCEDURE getTopObjectDetails( IN_OBJECT VARCHAR2, IN_OBJECT_TYPE VARCHAR2); /* ************************************** Set Doc Header * **************************************/ PROCEDURE setDocHeader( IN_OBJECT VARCHAR2, IN_OBJECT_TYPE VARCHAR2); /* ************************************** Get all privileges * **************************************/ PROCEDURE getUnitAllPrivs(IN_OBJECT VARCHAR2); /* ************************************** Initialize variables * **************************************/ PROCEDURE initDoc(IN_OBJECT VARCHAR2); /* ************************************** Finish documents preparation and send emails * **************************************/ PROCEDURE finishDoc( IN_OBJECT VARCHAR2, IN_FILE_NAME VARCHAR2, IN_EMAIL VARCHAR2);

o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o

/* ************************************** Prepare document for Procedure/Function/Package * **************************************/ PROCEDURE getPkgDoc( IN_OBJECT VARCHAR2, IN_FILE_NAME VARCHAR2); /* ************************************** Prepare document for Table * **************************************/ PROCEDURE getTblDoc( IN_OBJECT VARCHAR2, IN_OBJECT_TYPE VARCHAR2, IN_FILE_NAME VARCHAR2); /* ************************************** Main procedure to generate document * **************************************/ PROCEDURE generateDocInfo( IN_OBJECT VARCHAR2, IN_FILE_NAME VARCHAR2, IN_EMAIL VARCHAR2); /* ************************************** 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); /* ************************************** 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); /* ************************************** Get list of procedures/functions with arguments * **************************************/ PROCEDURE getArgumentsList(IN_OBJECT VARCHAR2); /* **************************************

o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o

Get trigger columns * **************************************/ PROCEDURE getTriggerColumns(IN_OBJECT VARCHAR2); /* ************************************** Get trigger details * **************************************/ PROCEDURE getTriggerDetails(IN_OBJECT VARCHAR2); /* ************************************** Get PL/SQL unit Source * **************************************/ PROCEDURE getUnitSource( IN_OBJECT VARCHAR2, IN_OBJECT_TYPE VARCHAR2); /* ************************************** Get type status * **************************************/ PROCEDURE getTypeStatus( IN_OBJECT VARCHAR2, IN_OBJECT_TYPE VARCHAR2); /* ************************************** Get type attributes * **************************************/ PROCEDURE getTypeAttrs(IN_OBJECT VARCHAR2); /* ************************************** Get list of type methods * **************************************/ PROCEDURE getTypeMethods(IN_OBJECT VARCHAR2); /* ************************************** Get table columns * **************************************/ PROCEDURE getTblCols(IN_OBJECT VARCHAR2); /* ************************************** Get Table Constraints * **************************************/ PROCEDURE getTblConstraints(IN_OBJECT VARCHAR2); /* ************************************** Get all Table privileges

o o o o o o o o o o o o o o o o o o o o o o o o o

* **************************************/ PROCEDURE getTblPrivs(IN_OBJECT VARCHAR2); /* ************************************** Get Table Indexes * **************************************/ PROCEDURE getTblIndexes(IN_OBJECT VARCHAR2); 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); END documentator; / 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 Oracle 8i

Modified to use in

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

CREATE OR REPLACE PACKAGE BODY documentator AS docBody main_page ref_page gv_schema gv_db_name gv_user gv_version CLOB; CLOB; CLOB; VARCHAR2(30); VARCHAR2(30); VARCHAR2(30); 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 ln_offset lv_text ln_amount ln_total

PLS_INTEGER; PLS_INTEGER; VARCHAR2(32767); BINARY_INTEGER := 32767; 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 BEGIN

VARCHAR2(30);

-- 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="contenttype" 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); DBMS_LOB.WRITE(docBody, 1, 1, CHR(10)); -- initialize

-- 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 BEGIN

EXCEPTION;

-- 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 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)); BOOLEAN;

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 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)); BOOLEAN;

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 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)); BOOLEAN;

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 lb_function BEGIN BOOLEAN; BOOLEAN;

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',

reference

'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 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'', FROM all_tab_privs WHERE table_name = :1 AND grantee = :2 AND privilege = ''DELETE'') del, (SELECT DECODE(privilege, ''INSERT'', FROM all_tab_privs WHERE table_name = :3 AND grantee = :4 AND privilege = ''INSERT'') ins, (SELECT DECODE(privilege, ''SELECT'', FROM all_tab_privs WHERE table_name = :5 AND grantee = :6 AND privilege = ''SELECT'') sel, (SELECT DECODE(privilege, ''UPDATE'', FROM all_tab_privs WHERE table_name = :7 AND grantee = :8 AND privilege = ''UPDATE'') upd FROM DUAL'; privs_data_del privs_data_ins VARCHAR2(30); VARCHAR2(30);

''Y'')

''Y'')

''Y'')

''Y'')

privs_data_sel privs_data_upd BEGIN

VARCHAR2(30); VARCHAR2(30);

-- 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( +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( +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( +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( +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

docBody)

docBody)

docBody)

docBody)

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 o

Potrebbero piacerti anche