<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-5882766990283082528</id><updated>2012-01-16T22:41:34.411-08:00</updated><category term='string'/><category term='REGEXP_LIKE'/><category term='delimiter'/><category term='dbms_metadata'/><category term='parsestring'/><category term='sql'/><category term='OWA_PATTERN'/><category term='Regular'/><category term='Expression'/><category term='REGEXP_REPLACE'/><category term='parse'/><category term='pl/sql'/><category term='db link'/><category term='ddl'/><category term='9i'/><category term='REGEXP'/><category term='parsing'/><category term='Oracle'/><category term='database link'/><category term='9iR2'/><title type='text'>Phil's Oracle SQL and PL/SQL tips</title><subtitle type='html'>A Blog dedicated to making Oracle SQL and PL/SQL development easier and more fun.  This is the place for helpful data modeling, development, administration, and architecture tips.

I plan to learn from the blog as well - so if you have helpful tips - please feel free to share them in your comments!</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://phil-sqltips.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5882766990283082528/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://phil-sqltips.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Phil</name><uri>http://www.blogger.com/profile/01851424617108510453</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>5</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-5882766990283082528.post-7159423709347969483</id><published>2010-06-22T12:12:00.001-07:00</published><updated>2011-02-01T13:33:52.109-08:00</updated><title type='text'>Tom Kyte's Pro*C "array_flat" CSV file Unloader - with enhancements</title><content type='html'>Hi all,&lt;br /&gt;&lt;br /&gt;It's been a long time since I've posted (I've been a lazy blogger).&lt;br /&gt;&lt;br /&gt;Recently I had the need to unload a CSV file from a rather complex query in Oracle 10g Release 2.  I just could not find a SQL*PLus solution that suited me, so I stumbled upon Tom Kyte's "array_flat" solution - found here in its orginal glory:&lt;br /&gt;&lt;a href="http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:459020243348"&gt;http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:459020243348&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Tom's solution uses Pro*C code to connect to Oracle and then dump the output of a query to the stdout, while dumping the query text and column headings to the stderr.&lt;br /&gt;&lt;br /&gt;Tom's solution of course is the bomb, but I wanted to improve upon it by adding the ability to change the delimiter character, and optionally specify an enclosure character (such as double-quote for example), and also have the ability to designate what should be output in the case of NULL values.&lt;br /&gt;&lt;br /&gt;I'm VERY new to Pro*C programming, and I suspect others are too - so I'm posting in hopes that this helps some other poor souls that are Pro*C newbs also.&lt;br /&gt;&lt;br /&gt;Ok - so here's the modified code - &lt;strong&gt;shamelessly&lt;/strong&gt; taken from Tom's code found in the URL above - and modified to add the enhancements mentioned earlier:&lt;br /&gt;&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;#include &amp;lt;string.h&amp;gt;&lt;br /&gt;#include &amp;lt;ctype.h&amp;gt;&lt;br /&gt;#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;&lt;br /&gt;#define MAX_VNAME_LEN     30&lt;br /&gt;#define MAX_INAME_LEN     30&lt;br /&gt;&lt;br /&gt;static char *   USERID = NULL;&lt;br /&gt;static char *   SQLSTMT = NULL;&lt;br /&gt;static char *   ARRAY_SIZE = &amp;quot;10&amp;quot;;&lt;br /&gt;static char *   DELIMITER = &amp;quot;,&amp;quot;;&lt;br /&gt;static char *   ENCLOSURE = &amp;quot;\&amp;quot;&amp;quot;;&lt;br /&gt;static char *   NULL_STRING = NULL;&lt;br /&gt;&lt;br /&gt;#define vstrcpy( a, b ) \&lt;br /&gt;(strcpy( a.arr, b ), a.len = strlen( a.arr ), a.arr)&lt;br /&gt;&lt;br /&gt;EXEC SQL INCLUDE sqlca;&lt;br /&gt;EXEC SQL INCLUDE sqlda;&lt;br /&gt;&lt;br /&gt;extern SQLDA *sqlald();&lt;br /&gt;extern void   sqlclu();&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;static void die( char * msg )&lt;br /&gt;{&lt;br /&gt;    fprintf( stderr, &amp;quot;%s\n&amp;quot;, msg );&lt;br /&gt;    exit(1);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;/*&lt;br /&gt;    this array contains a default mapping&lt;br /&gt;    I am using to constrain the&lt;br /&gt;       lengths of returned columns.  It is mapping,&lt;br /&gt;    for example, the Oracle&lt;br /&gt;       NUMBER type (type code = 2) to be 45 characters&lt;br /&gt;    long in a string.&lt;br /&gt;*/&lt;br /&gt;&lt;br /&gt;static int lengths[] =&lt;br /&gt;{ -1, 0, 45, 0, 0, 0, 0, 0, 2000, 0, 0,&lt;br /&gt; 18, 25, 0, 0, 0, 0, 0, 0, 0, 0,&lt;br /&gt;  0, 0, 512, 2000 };&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;static void process_parms( argc, argv )&lt;br /&gt;int    argc;&lt;br /&gt;char *    argv[];&lt;br /&gt;{&lt;br /&gt;int    i;&lt;br /&gt;&lt;br /&gt;    for( i = 1; i &amp;lt; argc; i++ )&lt;br /&gt;    {&lt;br /&gt;        if ( !strncmp( argv[i], &amp;quot;userid=&amp;quot;, 7 ) )&lt;br /&gt;              USERID = argv[i]+7;&lt;br /&gt;        else&lt;br /&gt;        if ( !strncmp( argv[i], &amp;quot;sqlstmt=&amp;quot;, 8 ) )&lt;br /&gt;              SQLSTMT = argv[i]+8;&lt;br /&gt;        else&lt;br /&gt;        if ( !strncmp( argv[i], &amp;quot;arraysize=&amp;quot;, 10 ) )&lt;br /&gt;              ARRAY_SIZE = argv[i]+10;&lt;br /&gt;        else&lt;br /&gt;        if ( !strncmp( argv[i], &amp;quot;delimiter=&amp;quot;, 10 ) )&lt;br /&gt;              DELIMITER = argv[i]+10;&lt;br /&gt;        else&lt;br /&gt;        if ( !strncmp( argv[i], &amp;quot;enclosure=&amp;quot;, 10 ) )&lt;br /&gt;              ENCLOSURE = argv[i]+10;&lt;br /&gt;        else&lt;br /&gt;        if ( !strncmp( argv[i], &amp;quot;null_string=&amp;quot;, 12 ) )&lt;br /&gt;              NULL_STRING = argv[i]+12;&lt;br /&gt;        else&lt;br /&gt;        {&lt;br /&gt;            fprintf( stderr,&lt;br /&gt;                    &amp;quot;usage: %s %s %s %s %s %s\n&amp;quot;,&lt;br /&gt;                     argv[0],&lt;br /&gt;                    &amp;quot;userid=xxx/xxx sqlstmt=query &amp;quot;,&lt;br /&gt;                    &amp;quot;arraysize=&amp;lt;NN&amp;gt; &amp;quot;,&lt;br /&gt;                    &amp;quot;delimiter=x &amp;quot;,&lt;br /&gt;                    &amp;quot;enclosure=x &amp;quot;,&lt;br /&gt;                    &amp;quot;null_string=x &amp;quot;);&lt;br /&gt;            exit(1);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    if ( USERID == NULL  &amp;#124;&amp;#124; SQLSTMT == NULL )&lt;br /&gt;    {&lt;br /&gt;        fprintf( stderr,&lt;br /&gt;                &amp;quot;usage: %s %s %s %s %s %s\n&amp;quot;,&lt;br /&gt;                 argv[0],&lt;br /&gt;                &amp;quot;userid=xxx/xxx sqlstmt=query &amp;quot;,&lt;br /&gt;                &amp;quot;arraysize=&amp;lt;NN&amp;gt; &amp;quot;,&lt;br /&gt;                &amp;quot;delimiter=x &amp;quot;,&lt;br /&gt;                &amp;quot;enclosure=x &amp;quot;,&lt;br /&gt;                &amp;quot;null_string=x &amp;quot;);&lt;br /&gt;        exit(1);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;static void sqlerror_hard()&lt;br /&gt;{&lt;br /&gt;    EXEC SQL WHENEVER SQLERROR CONTINUE;&lt;br /&gt;&lt;br /&gt;    fprintf(stderr,&amp;quot;\nORACLE error detected:&amp;quot;);&lt;br /&gt;    fprintf(stderr,&amp;quot;\n% .70s \n&amp;quot;, sqlca.sqlerrm.sqlerrmc);&lt;br /&gt;&lt;br /&gt;    EXEC SQL ROLLBACK WORK RELEASE;&lt;br /&gt;    exit(1);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;static SQLDA * process_1(char * sqlstmt, int array_size, char * delimiter, char * enclosure )&lt;br /&gt;{&lt;br /&gt;SQLDA *    select_dp;&lt;br /&gt;int     i;&lt;br /&gt;int        j;&lt;br /&gt;int        null_ok;&lt;br /&gt;int        precision;&lt;br /&gt;int        scale;&lt;br /&gt;int        size = 10;&lt;br /&gt;&lt;br /&gt;    fprintf( stderr, &amp;quot;Unloading '%s'\n&amp;quot;, sqlstmt );&lt;br /&gt;    fprintf( stderr, &amp;quot;Array size = %d\n&amp;quot;, array_size );&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    EXEC SQL WHENEVER SQLERROR DO sqlerror_hard();&lt;br /&gt;       EXEC SQL PREPARE S FROM :sqlstmt;&lt;br /&gt;       EXEC SQL DECLARE C CURSOR FOR S;&lt;br /&gt;&lt;br /&gt;    if ((select_dp = sqlald(size,MAX_VNAME_LEN,MAX_INAME_LEN))&lt;br /&gt;                   == NULL )&lt;br /&gt;        die( &amp;quot;Cannot allocate  memory for select descriptor.&amp;quot; );&lt;br /&gt;&lt;br /&gt;    select_dp-&amp;gt;N = size;&lt;br /&gt;    EXEC SQL DESCRIBE SELECT LIST FOR S INTO select_dp;&lt;br /&gt;    if ( !select_dp-&amp;gt;F ) return NULL;&lt;br /&gt;&lt;br /&gt;    if (select_dp-&amp;gt;F &amp;lt; 0)&lt;br /&gt;    {&lt;br /&gt;        size = -select_dp-&amp;gt;F;&lt;br /&gt;        sqlclu( select_dp );&lt;br /&gt;        if ((select_dp =&lt;br /&gt;                sqlald (size, MAX_VNAME_LEN, MAX_INAME_LEN))&lt;br /&gt;                      == NULL )&lt;br /&gt;        die( &amp;quot;Cannot allocate  memory for descriptor.&amp;quot; );&lt;br /&gt;        EXEC SQL DESCRIBE SELECT LIST FOR S INTO select_dp;&lt;br /&gt;    }&lt;br /&gt;    select_dp-&amp;gt;N = select_dp-&amp;gt;F;&lt;br /&gt;&lt;br /&gt;    for (i = 0; i &amp;lt; select_dp-&amp;gt;N; i++)&lt;br /&gt;        select_dp-&amp;gt;I[i] = (short *) malloc(sizeof(short) *&lt;br /&gt;                                                array_size );&lt;br /&gt;&lt;br /&gt;    for (i = 0; i &amp;lt; select_dp-&amp;gt;F; i++)&lt;br /&gt;    {&lt;br /&gt;        sqlnul (&amp;amp;(select_dp-&amp;gt;T[i]),&lt;br /&gt;                &amp;amp;(select_dp-&amp;gt;T[i]), &amp;amp;null_ok);&lt;br /&gt;        if ( select_dp-&amp;gt;T[i] &amp;lt;&lt;br /&gt;                     sizeof(lengths)/sizeof(lengths[0]) )&lt;br /&gt;        {&lt;br /&gt;            if ( lengths[select_dp-&amp;gt;T[i]] )&lt;br /&gt;                 select_dp-&amp;gt;L[i]  = lengths[select_dp-&amp;gt;T[i]];&lt;br /&gt;            else select_dp-&amp;gt;L[i] += 5;&lt;br /&gt;        }&lt;br /&gt;        else select_dp-&amp;gt;L[i] += 5;&lt;br /&gt;&lt;br /&gt;        select_dp-&amp;gt;T[i] = 5;&lt;br /&gt;        select_dp-&amp;gt;V[i] = (char *)malloc( select_dp-&amp;gt;L[i] *&lt;br /&gt;                                               array_size );&lt;br /&gt;&lt;br /&gt;        for( j = MAX_VNAME_LEN-1;&lt;br /&gt;             j &amp;gt; 0 &amp;amp;&amp;amp; select_dp-&amp;gt;S[i][j] == ' ';&lt;br /&gt;             j--);&lt;br /&gt;        fprintf (stderr,&lt;br /&gt;                &amp;quot;%s%.*s&amp;quot;, i?&amp;quot;,&amp;quot;:&amp;quot;&amp;quot;, j+1, select_dp-&amp;gt;S[i]);&lt;br /&gt;    }&lt;br /&gt;    fprintf( stderr, &amp;quot;\n&amp;quot; );&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    EXEC SQL OPEN C;&lt;br /&gt;    return select_dp;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;static void process_2( SQLDA * select_dp, int array_size, char * delimiter, char * enclosure, char * null_string )&lt;br /&gt;{&lt;br /&gt;int    last_fetch_count;&lt;br /&gt;int        row_count = 0;&lt;br /&gt;short    ind_value;&lt;br /&gt;char    * char_ptr;&lt;br /&gt;int    i,&lt;br /&gt;       j;&lt;br /&gt;&lt;br /&gt;    for ( last_fetch_count = 0;&lt;br /&gt;          ;&lt;br /&gt;          last_fetch_count = sqlca.sqlerrd[2] )&lt;br /&gt;    {&lt;br /&gt;        EXEC SQL FOR :array_size FETCH C&lt;br /&gt;                      USING DESCRIPTOR select_dp;&lt;br /&gt;&lt;br /&gt;        for( j=0; j &amp;lt; sqlca.sqlerrd[2]-last_fetch_count; j++ )&lt;br /&gt;        {&lt;br /&gt;            for (i = 0; i &amp;lt; select_dp-&amp;gt;F; i++)&lt;br /&gt;            {&lt;br /&gt;                ind_value = *(select_dp-&amp;gt;I[i]+j);&lt;br /&gt;                char_ptr  = select_dp-&amp;gt;V[i] +&lt;br /&gt;                                  (j*select_dp-&amp;gt;L[i]);&lt;br /&gt;&lt;br /&gt;                printf( &amp;quot;%s%s%s%s&amp;quot;, i?delimiter:&amp;quot;&amp;quot;,&lt;br /&gt;                                    enclosure,&lt;br /&gt;                                    ind_value?null_string:char_ptr,&lt;br /&gt;                                    enclosure );&lt;br /&gt;            }&lt;br /&gt;            row_count++;&lt;br /&gt;            printf( &amp;quot;\n&amp;quot; );&lt;br /&gt;        }&lt;br /&gt;        if ( sqlca.sqlcode &amp;gt; 0 ) break;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    sqlclu(select_dp);&lt;br /&gt;&lt;br /&gt;    EXEC SQL CLOSE C;&lt;br /&gt;&lt;br /&gt;    EXEC SQL COMMIT WORK;&lt;br /&gt;    fprintf( stderr, &amp;quot;%d rows extracted\n&amp;quot;, row_count );&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;main( argc, argv )&lt;br /&gt;int    argc;&lt;br /&gt;char *    argv[];&lt;br /&gt;{&lt;br /&gt;EXEC SQL BEGIN DECLARE SECTION;&lt;br /&gt;VARCHAR   oracleid[50];&lt;br /&gt;EXEC SQL END DECLARE SECTION;&lt;br /&gt;SQLDA    * select_dp;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    process_parms( argc, argv );&lt;br /&gt;&lt;br /&gt;    /* Connect to ORACLE. */&lt;br /&gt;    vstrcpy( oracleid, USERID );&lt;br /&gt;&lt;br /&gt;    EXEC SQL WHENEVER SQLERROR DO sqlerror_hard();&lt;br /&gt;&lt;br /&gt;    EXEC SQL CONNECT :oracleid;&lt;br /&gt;    fprintf(stderr, &amp;quot;\nConnected to ORACLE as user: %s\n\n&amp;quot;,&lt;br /&gt;             oracleid.arr);&lt;br /&gt;&lt;br /&gt;    EXEC SQL ALTER SESSION&lt;br /&gt;      SET NLS_DATE_FORMAT = 'DD-MON-YYYY HH24:MI:SS';&lt;br /&gt;&lt;br /&gt;    select_dp = process_1( SQLSTMT, atoi(ARRAY_SIZE), DELIMITER, ENCLOSURE );&lt;br /&gt;    process_2( select_dp , atoi(ARRAY_SIZE), DELIMITER, ENCLOSURE, NULL_STRING );&lt;br /&gt;&lt;br /&gt;    /* Disconnect from ORACLE. */&lt;br /&gt;    EXEC SQL COMMIT WORK RELEASE;&lt;br /&gt;    exit(0);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Ok - now you may ask - how the heck do I compile the thing?  This is where I had my trouble as well.&lt;br /&gt;&lt;br /&gt;Well - Here are the steps I took to get it to compile and working:&lt;br /&gt;1) Save the code to a file - in my case I chose "unloader.pc" &lt;br /&gt;2) Create a folder in your home directory on the UNIX server where you are going to compile this code.  For example: "mkdir /home/phil/unloader".&lt;br /&gt;3) FTP the file in ascii mode to the UNIX server directory created in step 2 above.  Make sure that the server has the Oracle client installed.  In my case I used Oracle 10g Release 2 client (10.2.0.3).  Make sure you've run ". oraenv" to have all of your Oracle environment variables set up!&lt;br /&gt;4) Copy the contents of directory $ORACLE_HOME/precomp/demo/proc/ to my directory created in step 2 - example unix command:&lt;br /&gt;"cp $ORACLE_HOME/precomp/demo/proc/*.* /home/phil/unloader"&lt;br /&gt;5) Modify your personal copy of the "demo_proc.mk" file - by adding our new Pro*C file to the list defined by the keyword "SAMPLES" - like so:&lt;br /&gt;&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;&lt;br /&gt;# SAMPLES is a list of the c proc sample programs.&lt;br /&gt;# CPPSAMPLES is a list of the c++ proc sample programs.&lt;br /&gt;# OBJECT_SAMPLES is a list of the proc sample programs using the new&lt;br /&gt;# type features.  You must install ott in addition to proc to run some&lt;br /&gt;# of those samples.&lt;br /&gt;SAMPLES=&lt;em&gt;&lt;strong&gt;unloader&lt;/strong&gt;&lt;/em&gt; sample1 sample2 sample3 sample4 sample6 sample7 sample8 \&lt;br /&gt;        sample9 sample10 sample11 sample12 oraca sqlvcp cv_demo \&lt;br /&gt;        ansidyn1 ansidyn2 cpdemo1 cpdemo2 scdemo1 scdemo2&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;6) Set your "LD_LIBRARY_PATH" environment variable to include: "$ORACLE_HOME/lib" (if it doesn't already have it) - you can do that like this:&lt;br /&gt;export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$ORACLE_HOME/lib&lt;br /&gt;&lt;br /&gt;7) Now you're ready to compile - just issue this command:&lt;br /&gt;make -f ./demo_proc.mk unloader&lt;br /&gt;&lt;br /&gt;After it completes (you may get warnings - but this is ok) - you should have an executable file called "unloader" with no extension.  If you have this file - your compile worked - and you should be ready for execution.&lt;br /&gt;-------------------------------&lt;br /&gt;&lt;br /&gt;Now for some execution examples:&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Example 1 - Unload a simple query against the dual table:&lt;/strong&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;&lt;br /&gt;[phil@unixserver:/home/phil/unloader]&lt;br /&gt;$ ./unloader userid=&amp;quot;scott/tiger@db&amp;quot; sqlstmt=&amp;quot;select * from dual&amp;quot; arraysize=100 \&lt;br /&gt;&amp;gt; delimiter=&amp;quot;&amp;#124;&amp;quot; enclosure='&amp;quot;' null_string=&amp;quot;(null)&amp;quot;&lt;br /&gt;&lt;br /&gt;Connected to ORACLE as user: scott/tiger@db&lt;br /&gt;&lt;br /&gt;Unloading 'select * from dual'&lt;br /&gt;Array size = 100&lt;br /&gt;DUMMY&lt;br /&gt;&amp;quot;X&amp;quot;&lt;br /&gt;1 rows extracted&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Example 2 - Unload a bigger SQL Query from a file - send the data to one file - and the query and column headings to another file:&lt;/strong&gt;&lt;br /&gt;Step 1) Store our query in a file - like so:&lt;br /&gt;echo "SELECT 1 AS num_field, 'ABC' AS text_field FROM dual" &gt; ./query.sql&lt;br /&gt;Step 2) Run this command:&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;&lt;br /&gt;[phil@unixserver:/home/phil/unloader]&lt;br /&gt;$ ./unloader userid=&amp;quot;scott/tiger@db&amp;quot; sqlstmt=&amp;quot;`cat ./query.sql`&amp;quot; \&lt;br /&gt;&amp;gt; arraysize=100 delimiter=&amp;quot;&amp;#124;&amp;quot; enclosure='&amp;quot;' null_string=&amp;quot;(null)&amp;quot; 1&amp;gt;./stdout.csv 2&amp;gt;./stderr.txt&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Here is the output from stdout.csv:&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;[phil@unixserver:/home/phil/unloader]&lt;br /&gt;$ cat stdout.csv&lt;br /&gt;&amp;quot;1&amp;quot;&amp;#124;&amp;quot;ABC&amp;quot;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Here is the output from stderr.txt:&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;[phil@unixserver:/home/phil/unloader]&lt;br /&gt;$ cat stderr.txt&lt;br /&gt;&lt;br /&gt;Connected to ORACLE as user: scott/tiger@db&lt;br /&gt;&lt;br /&gt;Unloading 'SELECT 1 AS num_field, 'ABC' AS text_field FROM dual'&lt;br /&gt;Array size = 100&lt;br /&gt;NUM_FIELD,TEXT_FIELD&lt;br /&gt;1 rows extracted&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;-------------------&lt;br /&gt;&lt;br /&gt;I hope this has been useful.  I know that Pro*C can be intimidating - hopefully this example can make it a little easier for you.  I am still very new, and I know there is probably a WAY better way to compile the Pro*C code - but hey - this worked!&lt;br /&gt;&lt;br /&gt;This solution was tested on an HP-UX 64-bit Itanium server - and worked with both Oracle 9.2 and 10.2 Databases.&lt;br /&gt;&lt;br /&gt;Good luck, and God bless!.&lt;br /&gt;&lt;br /&gt;Phil&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5882766990283082528-7159423709347969483?l=phil-sqltips.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://phil-sqltips.blogspot.com/feeds/7159423709347969483/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://phil-sqltips.blogspot.com/2010/06/tom-kytes-proc-arrayflat-csv-file.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5882766990283082528/posts/default/7159423709347969483'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5882766990283082528/posts/default/7159423709347969483'/><link rel='alternate' type='text/html' href='http://phil-sqltips.blogspot.com/2010/06/tom-kytes-proc-arrayflat-csv-file.html' title='Tom Kyte&apos;s Pro*C &quot;array_flat&quot; CSV file Unloader - with enhancements'/><author><name>Phil</name><uri>http://www.blogger.com/profile/01851424617108510453</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5882766990283082528.post-4730506282676243152</id><published>2009-07-22T07:39:00.000-07:00</published><updated>2009-07-22T07:52:13.468-07:00</updated><title type='text'>How to get the name of the Partition within a table that a ROWID comes from...</title><content type='html'>Oracle's partitioning feature is extremely handy for allowing you to break up a huge table into smaller, more manageable pieces.&lt;br /&gt;&lt;br /&gt;I use it extensively for Datawarehousing - where it allows one to add and remove fact data partitioned by time/date very efficiently.&lt;br /&gt;&lt;br /&gt;What is hard, however - is to find out which partition a particular row comes from within a table.  Well - not any more.  Just create this handy little function to get the name of the partition that any row comes from (using the "ROWID" pseudocolumn).&lt;br /&gt;&lt;br /&gt;Here goes:&lt;br /&gt;&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;CREATE OR REPLACE FUNCTION get_partition_name_for_rowid (&lt;br /&gt;   p_rowid   IN   ROWID)&lt;br /&gt;   RETURN VARCHAR2 AS&lt;br /&gt;   -- Variables&lt;br /&gt;   l_all_objects_rec                  all_objects%ROWTYPE;&lt;br /&gt;   l_partition_name                   all_tab_partitions.partition_name%TYPE;&lt;br /&gt;   -- Custom Exceptions&lt;br /&gt;   rowid_not_from_a_partition         EXCEPTION;&lt;br /&gt;BEGIN&lt;br /&gt;   SELECT /*+ FIRST_ROWS */&lt;br /&gt;          *&lt;br /&gt;     INTO l_all_objects_rec&lt;br /&gt;     FROM all_objects&lt;br /&gt;    WHERE data_object_id = dbms_mview.pmarker (p_rowid);&lt;br /&gt;&lt;br /&gt;   IF l_all_objects_rec.object_type = 'TABLE PARTITION' THEN&lt;br /&gt;      l_partition_name := l_all_objects_rec.subobject_name;&lt;br /&gt;   ELSE&lt;br /&gt;      RAISE rowid_not_from_a_partition;&lt;br /&gt;   END IF;&lt;br /&gt;&lt;br /&gt;   RETURN l_partition_name;&lt;br /&gt;EXCEPTION&lt;br /&gt;   WHEN rowid_not_from_a_partition THEN&lt;br /&gt;      raise_application_error (-20101&lt;br /&gt;                             , build_string (p_input_string      =&amp;gt; 'ROWID: %s is in table: %s - which is not partitioned.'&lt;br /&gt;                                           , str1                =&amp;gt; p_rowid&lt;br /&gt;                                           , str2                =&amp;gt; l_all_objects_rec.object_name));&lt;br /&gt;   WHEN OTHERS THEN&lt;br /&gt;      RAISE;&lt;br /&gt;END get_partition_name_for_rowid;&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Just run this SQL to test it:&lt;br /&gt;&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;CREATE TABLE part_table&lt;br /&gt;  (partition_key INTEGER&lt;br /&gt; , some_other_column VARCHAR2 (30)&lt;br /&gt;  )&lt;br /&gt;PARTITION BY RANGE (partition_key)&lt;br /&gt; ( PARTITION p1 VALUES LESS THAN (50)&lt;br /&gt; , PARTITION p2 VALUES LESS THAN (MAXVALUE)&lt;br /&gt; )&lt;br /&gt;;&lt;br /&gt;&lt;br /&gt;INSERT INTO part_table&lt;br /&gt;(partition_key, some_other_column)&lt;br /&gt;SELECT ROWNUM&lt;br /&gt;     , TO_CHAR (ROWNUM)&lt;br /&gt;  FROM all_objects&lt;br /&gt; WHERE ROWNUM &amp;lt;= 100;&lt;br /&gt; &lt;br /&gt;SELECT get_partition_name_for_rowid (ROWID) AS partition_name&lt;br /&gt;     , partition_key&lt;br /&gt;     , some_other_column&lt;br /&gt;  FROM part_table&lt;br /&gt; WHERE partition_key BETWEEN 47 AND 52;&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Semper Fidelis&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5882766990283082528-4730506282676243152?l=phil-sqltips.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://phil-sqltips.blogspot.com/feeds/4730506282676243152/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://phil-sqltips.blogspot.com/2009/07/how-to-get-name-of-partition-within.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5882766990283082528/posts/default/4730506282676243152'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5882766990283082528/posts/default/4730506282676243152'/><link rel='alternate' type='text/html' href='http://phil-sqltips.blogspot.com/2009/07/how-to-get-name-of-partition-within.html' title='How to get the name of the Partition within a table that a ROWID comes from...'/><author><name>Phil</name><uri>http://www.blogger.com/profile/01851424617108510453</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5882766990283082528.post-1889686646618429041</id><published>2009-07-10T19:50:00.000-07:00</published><updated>2009-07-10T20:03:32.384-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sql'/><category scheme='http://www.blogger.com/atom/ns#' term='pl/sql'/><category scheme='http://www.blogger.com/atom/ns#' term='parsing'/><category scheme='http://www.blogger.com/atom/ns#' term='string'/><category scheme='http://www.blogger.com/atom/ns#' term='Oracle'/><category scheme='http://www.blogger.com/atom/ns#' term='parsestring'/><category scheme='http://www.blogger.com/atom/ns#' term='parse'/><category scheme='http://www.blogger.com/atom/ns#' term='delimiter'/><title type='text'>Handy Function for parsing strings in PL/SQL!</title><content type='html'>One common function needed for PL/SQL is some sort of string parsing solution. For instance, if you want to parse out the "3rd" position of a string delimited by an "/" character.&lt;br /&gt;&lt;br /&gt;My buddy Todd had written a handy function in Visual Basic for Applications, and even converted it for use in Oracle. After I saw Tom Kyte use a combination of INSTR and SUBSTR to parse strings, I rewrote Todd's approach to perform a little better.&lt;br /&gt;&lt;br /&gt;Here's the code in case you are interested in this handy little string function:&lt;br /&gt;&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;CREATE OR REPLACE FUNCTION parsestring (&lt;br /&gt;   p_string      IN   VARCHAR2&lt;br /&gt; , p_delimiter   IN   VARCHAR2&lt;br /&gt; , p_position    IN   INTEGER&lt;br /&gt;)&lt;br /&gt;/**************************************************************&lt;br /&gt;  Function: parsestring&lt;br /&gt;&lt;br /&gt;  Version:  2.0&lt;br /&gt;&lt;br /&gt;  Author:  Philip Moore - (version 2.0)&lt;br /&gt;&lt;br /&gt;  Original Author: Todd Ward - (version 1.0)&lt;br /&gt;  &lt;br /&gt;  Date: 23-JAN-2006&lt;br /&gt;  &lt;br /&gt;  Description: This function's purpose is to return&lt;br /&gt;               the &amp;quot;p_position&amp;quot;th portion of the string&lt;br /&gt;               &amp;quot;p_string&amp;quot;, delimited by &amp;quot;p_delimiter&amp;quot;.&lt;br /&gt;               &lt;br /&gt;  Example call: parsestring('a/b/c', '/', 2) Returns: 'b'&lt;br /&gt;**************************************************************/&lt;br /&gt;RETURN VARCHAR2     &lt;br /&gt;AUTHID CURRENT_USER&lt;br /&gt;PARALLEL_ENABLE&lt;br /&gt;DETERMINISTIC&lt;br /&gt;AS&lt;br /&gt;-- Variables&lt;br /&gt;   v_txt      VARCHAR2 (32767);&lt;br /&gt;BEGIN&lt;br /&gt;   v_txt := p_delimiter &amp;#124;&amp;#124; p_string &amp;#124;&amp;#124; p_delimiter;&lt;br /&gt;   RETURN SUBSTR (v_txt&lt;br /&gt;                , INSTR (v_txt&lt;br /&gt;                       , p_delimiter&lt;br /&gt;                       , 1&lt;br /&gt;                       , p_position&lt;br /&gt;                        ) + LENGTH (p_delimiter)&lt;br /&gt;                , INSTR (v_txt&lt;br /&gt;                       , p_delimiter&lt;br /&gt;                       , 1&lt;br /&gt;                       , p_position + 1&lt;br /&gt;                        ) - INSTR (v_txt&lt;br /&gt;                                 , p_delimiter&lt;br /&gt;                                 , 1&lt;br /&gt;                                 , p_position&lt;br /&gt;                                  ) - LENGTH (p_delimiter)&lt;br /&gt;                 );&lt;br /&gt;EXCEPTION&lt;br /&gt;   WHEN OTHERS THEN&lt;br /&gt;      RETURN NULL;&lt;br /&gt;END parsestring;&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;SHOW ERRORS;&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now - note that this will not perform as well as using the SUBSTR and INSTR combination of functions shown in the source code itself - because of the PL/SQL context switches when run in the SQL engine.  But I personally think that it is MUCH easier to remember the syntax for calling "PARSESTRING" than for calling the complex SUBSTR/INSTR combination.&lt;br /&gt;&lt;br /&gt;I've used this function many thousands of times over the years - and I believe that you will find it handy as well.&lt;br /&gt;&lt;br /&gt;Good luck!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5882766990283082528-1889686646618429041?l=phil-sqltips.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://phil-sqltips.blogspot.com/feeds/1889686646618429041/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://phil-sqltips.blogspot.com/2009/07/handy-function-for-parsing-strings-in.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5882766990283082528/posts/default/1889686646618429041'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5882766990283082528/posts/default/1889686646618429041'/><link rel='alternate' type='text/html' href='http://phil-sqltips.blogspot.com/2009/07/handy-function-for-parsing-strings-in.html' title='Handy Function for parsing strings in PL/SQL!'/><author><name>Phil</name><uri>http://www.blogger.com/profile/01851424617108510453</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5882766990283082528.post-3805899847404165805</id><published>2009-06-23T13:10:00.000-07:00</published><updated>2009-06-25T11:48:29.177-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='REGEXP_REPLACE'/><category scheme='http://www.blogger.com/atom/ns#' term='Expression'/><category scheme='http://www.blogger.com/atom/ns#' term='9iR2'/><category scheme='http://www.blogger.com/atom/ns#' term='Regular'/><category scheme='http://www.blogger.com/atom/ns#' term='REGEXP'/><category scheme='http://www.blogger.com/atom/ns#' term='OWA_PATTERN'/><category scheme='http://www.blogger.com/atom/ns#' term='REGEXP_LIKE'/><category scheme='http://www.blogger.com/atom/ns#' term='9i'/><category scheme='http://www.blogger.com/atom/ns#' term='Oracle'/><title type='text'>REGEXP_REPLACE / REGEXP_LIKE for Oracle 9i</title><content type='html'>I really love using the REGEXP_REPLACE function and REGEXP_LIKE operator in Oracle 10G - and have been spoiled by their ease of use as a result.&lt;br /&gt;&lt;br /&gt;One of my clients, however - is sort of "stuck" on Oracle 9iR2 due to regression testing cost requirements of an upgrade. The problem is that we still have to do development on the 9iR2 database - and I REALLY want my regular expressions!&lt;br /&gt;&lt;br /&gt;So - I figured that I would take a stab at writing my own REGEXP_REPLACE and REGEXP_LIKE functions.&lt;br /&gt;&lt;br /&gt;Here's the source code - in case you want the same functionality in Oracle 9iR2:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;create or replace&lt;br /&gt;FUNCTION regexp_replace (&lt;br /&gt;   source_char       IN   VARCHAR2&lt;br /&gt; , pattern           IN   VARCHAR2&lt;br /&gt; , replace_string    IN   VARCHAR2&lt;br /&gt; , POSITION          IN   PLS_INTEGER DEFAULT 1&lt;br /&gt; , occurrence        IN   PLS_INTEGER DEFAULT 0&lt;br /&gt; , match_parameter   IN   VARCHAR2 DEFAULT NULL)&lt;br /&gt;   RETURN VARCHAR2 AS&lt;br /&gt;/*************************************************************************&lt;br /&gt; * Program  : regexp_replace&lt;br /&gt; * Version  : 1.0&lt;br /&gt; * Author   : Philip Moore&lt;br /&gt; * Date     : 20-JUN-2009 Anno Domini&lt;br /&gt; * Purpopse : This provides a pseudo &amp;quot;REGEXP_REPLACE&amp;quot; function for Oracle 9iR2&lt;br /&gt; * Warnings : Do NOT compile in an Oracle 10GR2 database (or above)!&lt;br /&gt; *************************************************************************/&lt;br /&gt;   -- Variables&lt;br /&gt;   l_source_string                    VARCHAR2 (32767);&lt;br /&gt;   l_temp_string                      VARCHAR2 (32767);&lt;br /&gt;   l_flags                            VARCHAR2 (10);&lt;br /&gt;   l_occurrence                       PLS_INTEGER;&lt;br /&gt;   l_end_of_pattern_pos               PLS_INTEGER;&lt;br /&gt;   l_string_pos                       PLS_INTEGER;&lt;br /&gt;BEGIN&lt;br /&gt;   -- Substr the source_char to start at the position specified&lt;br /&gt;   l_source_string := SUBSTR (source_char, POSITION);&lt;br /&gt;&lt;br /&gt;   -- Set up the flags argument&lt;br /&gt;   IF occurrence = 0 THEN&lt;br /&gt;      l_flags := 'g';&lt;br /&gt;   ELSE&lt;br /&gt;      l_flags := NULL;&lt;br /&gt;   END IF;&lt;br /&gt;&lt;br /&gt;   l_flags := l_flags &amp;#124;&amp;#124; match_parameter;&lt;br /&gt;&lt;br /&gt;   -- Now replace the regular expression pattern globally if &amp;quot;g&amp;quot;&lt;br /&gt;   IF INSTR (l_flags, 'g') &amp;gt; 0 THEN&lt;br /&gt;      owa_pattern.CHANGE (line          =&amp;gt; l_source_string&lt;br /&gt;                        , from_str      =&amp;gt; pattern&lt;br /&gt;                        , to_str        =&amp;gt; replace_string&lt;br /&gt;                        , flags         =&amp;gt; l_flags);&lt;br /&gt;   -- Not a global replace - loop until the &amp;quot;occurrence&amp;quot;th occurrence is replaced...&lt;br /&gt;   ELSE&lt;br /&gt;      l_string_pos := 0;&lt;br /&gt;      l_occurrence := 0;&lt;br /&gt;      WHILE l_string_pos &amp;lt; LENGTH (l_source_string) AND l_occurrence &amp;lt; occurrence LOOP&lt;br /&gt;         l_string_pos := l_string_pos + 1;&lt;br /&gt;&lt;br /&gt;         l_end_of_pattern_pos := owa_pattern.amatch (line          =&amp;gt; l_source_string&lt;br /&gt;                                                   , from_loc      =&amp;gt; l_string_pos&lt;br /&gt;                                                   , pat           =&amp;gt; pattern&lt;br /&gt;                                                   , flags         =&amp;gt; match_parameter);&lt;br /&gt;&lt;br /&gt;         IF l_end_of_pattern_pos != 0 THEN&lt;br /&gt;            l_occurrence := l_occurrence + 1;&lt;br /&gt;         END IF;&lt;br /&gt;      END LOOP;&lt;br /&gt;&lt;br /&gt;      IF l_occurrence = occurrence THEN&lt;br /&gt;         l_temp_string := SUBSTR (l_source_string&lt;br /&gt;                                , l_string_pos&lt;br /&gt;                                , (l_end_of_pattern_pos - l_string_pos));&lt;br /&gt;         owa_pattern.CHANGE (line          =&amp;gt; l_temp_string&lt;br /&gt;                           , from_str      =&amp;gt; pattern&lt;br /&gt;                           , to_str        =&amp;gt; replace_string&lt;br /&gt;                           , flags         =&amp;gt; l_flags);&lt;br /&gt;         l_source_string := SUBSTR (l_source_string&lt;br /&gt;                                  , 1&lt;br /&gt;                                  , l_string_pos - 1) &amp;#124;&amp;#124; l_temp_string &amp;#124;&amp;#124; SUBSTR (l_source_string, l_end_of_pattern_pos);&lt;br /&gt;      END IF;&lt;br /&gt;   END IF;&lt;br /&gt;&lt;br /&gt;   -- Piece the string back together if needed...&lt;br /&gt;   IF POSITION &amp;gt; 1 THEN&lt;br /&gt;      l_source_string := SUBSTR (source_char&lt;br /&gt;                               , 1&lt;br /&gt;                               , (POSITION - 1)) &amp;#124;&amp;#124; l_source_string;&lt;br /&gt;   END IF;&lt;br /&gt;&lt;br /&gt;   RETURN l_source_string;&lt;br /&gt;END regexp_replace;&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;CREATE OR REPLACE FUNCTION regexp_like (source_char     IN VARCHAR2&lt;br /&gt;                                      , pattern         IN VARCHAR2&lt;br /&gt;                                      , match_parameter IN VARCHAR2 DEFAULT NULL)&lt;br /&gt;RETURN INTEGER&lt;br /&gt;AS&lt;br /&gt;/*************************************************************************&lt;br /&gt; * Program  : regexp_like&lt;br /&gt; * Version  : 1.0&lt;br /&gt; * Author   : Philip Moore&lt;br /&gt; * Date     : 20-JUN-2009 Anno Domini&lt;br /&gt; * Purpopse : This provides a pseudo &amp;quot;REGEXP_LIKE&amp;quot; operator for Oracle 9iR2&lt;br /&gt; * Warnings : Do NOT compile in an Oracle 10GR2 database (or above)!&lt;br /&gt; *************************************************************************/&lt;br /&gt;  -- Variables&lt;br /&gt;  l_return INTEGER;&lt;br /&gt;BEGIN&lt;br /&gt;   IF owa_pattern.match (line  =&amp;gt; source_char&lt;br /&gt;                       , pat   =&amp;gt; pattern&lt;br /&gt;                       , flags =&amp;gt; match_parameter) THEN&lt;br /&gt;      l_return := 1;&lt;br /&gt;   ELSE&lt;br /&gt;      l_return := 0;&lt;br /&gt;   END IF;&lt;br /&gt;&lt;br /&gt;   RETURN l_return;&lt;br /&gt;END regexp_like;&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:Courier New;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style="font-family:arial;"&gt;The Functions in Action:&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;Here's a demo of my new Oracle 9iR2 REGEXP_REPLACE function:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;SELECT regexp_replace ('5138675309', '(\d{3})(\d{3})(\d{4})', '(\1) \2-\3') AS phone &lt;br /&gt;FROM v$version &lt;br /&gt;WHERE banner = 'Oracle9i Enterprise Edition Release 9.2.0.5.0 - 64bit Production'; &lt;br /&gt;&lt;br /&gt;Result: &lt;br /&gt;PHONE &lt;br /&gt;---------------- &lt;br /&gt;(513) 867-5309 &lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;&lt;strong&gt;&lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;&lt;strong&gt;Some Watchouts: &lt;/strong&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;The "OWA_PATTERN" package isn't as powerful as the built-in REGEXP% functions that Oracle 10g Provides - for example - it doesn't support the "" OR operator. It is also much slower than those natively built-in 10g functions - so don't use these to find the last digit in pi :)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;I hope these are helpful in your "stuck in Oracle 9i" endeavors!&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Special thanks &lt;/strong&gt;to Greg Houston's &lt;a href="http://formatmysourcecode.blogspot.com/"&gt;http://formatmysourcecode.blogspot.com/&lt;/a&gt; for helping me format the code for blogger!&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5882766990283082528-3805899847404165805?l=phil-sqltips.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://phil-sqltips.blogspot.com/feeds/3805899847404165805/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://phil-sqltips.blogspot.com/2009/06/regexpreplace-regexplike-for-oracle-9i.html#comment-form' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5882766990283082528/posts/default/3805899847404165805'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5882766990283082528/posts/default/3805899847404165805'/><link rel='alternate' type='text/html' href='http://phil-sqltips.blogspot.com/2009/06/regexpreplace-regexplike-for-oracle-9i.html' title='REGEXP_REPLACE / REGEXP_LIKE for Oracle 9i'/><author><name>Phil</name><uri>http://www.blogger.com/profile/01851424617108510453</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5882766990283082528.post-1429710275069515728</id><published>2009-06-18T09:50:00.001-07:00</published><updated>2009-06-25T11:54:12.257-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sql'/><category scheme='http://www.blogger.com/atom/ns#' term='pl/sql'/><category scheme='http://www.blogger.com/atom/ns#' term='ddl'/><category scheme='http://www.blogger.com/atom/ns#' term='db link'/><category scheme='http://www.blogger.com/atom/ns#' term='database link'/><category scheme='http://www.blogger.com/atom/ns#' term='dbms_metadata'/><title type='text'>DBMS_METADATA Across Database Links!</title><content type='html'>As a Data Modeler and Developer, I've always wanted a very easy way to get Table/Function/Procedure/View DDL SQL from a remote database - via Database Link.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The DBMS_METADATA.GET_DDL function is quite handy for getting DDL in the SAME database - but it isn't quite so easy to use to get DDL from a remote database.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;So - in the spirit of the USMC - I chose to adapt, improvise, and overcome - by writing my own interface to DBMS_METADATA - called "REMOTE_DBMS_METADATA".&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;If you want the source code - here goes:&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;create or replace PACKAGE boolean_pkg&lt;br /&gt;IS&lt;br /&gt;   FUNCTION bool_to_str (boolean_in IN BOOLEAN)&lt;br /&gt;      RETURN VARCHAR2;&lt;br /&gt;&lt;br /&gt;   FUNCTION str_to_bool (string_in IN VARCHAR2)&lt;br /&gt;      RETURN BOOLEAN;&lt;br /&gt;&lt;br /&gt;   FUNCTION true_value&lt;br /&gt;      RETURN VARCHAR2;&lt;br /&gt;&lt;br /&gt;   FUNCTION false_value&lt;br /&gt;      RETURN VARCHAR2;&lt;br /&gt;END boolean_pkg;&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;SHOW ERRORS;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;create or replace PACKAGE BODY boolean_pkg&lt;br /&gt;IS&lt;br /&gt;   c_true    CONSTANT VARCHAR2 (5) := 'TRUE';&lt;br /&gt;   c_false   CONSTANT VARCHAR2 (5) := 'FALSE';&lt;br /&gt;&lt;br /&gt;/***************************************************/&lt;br /&gt;   FUNCTION bool_to_str (boolean_in IN BOOLEAN)&lt;br /&gt;      RETURN VARCHAR2&lt;br /&gt;   IS&lt;br /&gt;   BEGIN&lt;br /&gt;      IF boolean_in&lt;br /&gt;      THEN&lt;br /&gt;         RETURN c_true;&lt;br /&gt;      ELSIF NOT boolean_in&lt;br /&gt;      THEN&lt;br /&gt;         RETURN c_false;&lt;br /&gt;      ELSE&lt;br /&gt;         RETURN NULL;&lt;br /&gt;      END IF;&lt;br /&gt;   END bool_to_str;&lt;br /&gt;&lt;br /&gt;/***************************************************/&lt;br /&gt;   FUNCTION str_to_bool (string_in IN VARCHAR2)&lt;br /&gt;      RETURN BOOLEAN&lt;br /&gt;   IS&lt;br /&gt;   BEGIN&lt;br /&gt;      IF string_in = c_true&lt;br /&gt;      THEN&lt;br /&gt;         RETURN TRUE;&lt;br /&gt;      ELSIF string_in = c_false&lt;br /&gt;      THEN&lt;br /&gt;         RETURN FALSE;&lt;br /&gt;      ELSE&lt;br /&gt;         RETURN NULL;&lt;br /&gt;      END IF;&lt;br /&gt;   END str_to_bool;&lt;br /&gt;&lt;br /&gt;/***************************************************/&lt;br /&gt;   FUNCTION true_value&lt;br /&gt;      RETURN VARCHAR2&lt;br /&gt;   IS&lt;br /&gt;   BEGIN&lt;br /&gt;      RETURN c_true;&lt;br /&gt;   END true_value;&lt;br /&gt;&lt;br /&gt;   FUNCTION false_value&lt;br /&gt;      RETURN VARCHAR2&lt;br /&gt;   IS&lt;br /&gt;   BEGIN&lt;br /&gt;      RETURN c_false;&lt;br /&gt;   END false_value;&lt;br /&gt;/***************************************************/&lt;br /&gt;&lt;br /&gt;END boolean_pkg;&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;SHOW ERRORS;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;create or replace PACKAGE REMOTE_DBMS_METADATA AS&lt;br /&gt;/*************************************************************************&lt;br /&gt; * Program  : remote_dbms_metadata&lt;br /&gt; * Version  : 1.0&lt;br /&gt; * Author   : Philip Moore&lt;br /&gt; * Date     : 20-JUN-2009 Anno Domini&lt;br /&gt; * Purpopse : This package provides access to DDL for objects in a remote&lt;br /&gt;              Oracle database via Database Links.&lt;br /&gt; * Warnings : This package has only been tested in Oracle 9iR2 and 10gR2.&lt;br /&gt; *************************************************************************/&lt;br /&gt;&lt;br /&gt;-- Types&lt;br /&gt;   TYPE session_transform_param_rec IS RECORD (parameter_name VARCHAR2 (50)&lt;br /&gt;                                             , value_datatype PLS_INTEGER&lt;br /&gt;                                             , varchar2_value VARCHAR2 (50)&lt;br /&gt;                                             , boolean_value  BOOLEAN&lt;br /&gt;                                             , number_value   NUMBER);&lt;br /&gt;&lt;br /&gt;   TYPE session_transform_params_type IS TABLE OF session_transform_param_rec INDEX BY VARCHAR2 (50);&lt;br /&gt;   TYPE db_link_session_transform_type IS TABLE OF session_transform_params_type INDEX BY all_db_links.db_link%TYPE;&lt;br /&gt;&lt;br /&gt;-- Global Constants&lt;br /&gt;   c_varchar2_type CONSTANT PLS_INTEGER := 1;&lt;br /&gt;   c_boolean_type CONSTANT PLS_INTEGER := 2;&lt;br /&gt;   c_number_type CONSTANT PLS_INTEGER := 3;&lt;br /&gt;&lt;br /&gt;-- Global Variables&lt;br /&gt;   g_session_transform_params db_link_session_transform_type;&lt;br /&gt;&lt;br /&gt;-- Custom Exceptions&lt;br /&gt;   handle_not_open                    EXCEPTION;&lt;br /&gt;   PRAGMA EXCEPTION_INIT (handle_not_open, -31600);&lt;br /&gt;&lt;br /&gt;-- Procedures and Functions&lt;br /&gt;   FUNCTION OPEN (&lt;br /&gt;      db_link       IN   VARCHAR2 DEFAULT NULL&lt;br /&gt;    , object_type   IN   VARCHAR2&lt;br /&gt;    , version       IN   VARCHAR2 DEFAULT 'COMPATIBLE'&lt;br /&gt;    , model         IN   VARCHAR2 DEFAULT 'ORACLE'&lt;br /&gt;   )&lt;br /&gt;      RETURN NUMBER;&lt;br /&gt;&lt;br /&gt;   PROCEDURE dbms_metadata_close (&lt;br /&gt;      db_link   IN   VARCHAR2 DEFAULT NULL&lt;br /&gt;    , handle    IN   NUMBER&lt;br /&gt;   );&lt;br /&gt;&lt;br /&gt;   PROCEDURE set_count (&lt;br /&gt;      db_link   IN   VARCHAR2 DEFAULT NULL&lt;br /&gt;    , handle    IN   NUMBER&lt;br /&gt;    , value     IN   NUMBER&lt;br /&gt;   );&lt;br /&gt;&lt;br /&gt;   PROCEDURE set_filter (&lt;br /&gt;      db_link   IN   VARCHAR2 DEFAULT NULL&lt;br /&gt;    , handle    IN   NUMBER&lt;br /&gt;    , name      IN   VARCHAR2&lt;br /&gt;    , value     IN   VARCHAR2&lt;br /&gt;   );&lt;br /&gt;&lt;br /&gt;   PROCEDURE set_filter (&lt;br /&gt;      db_link   IN   VARCHAR2 DEFAULT NULL&lt;br /&gt;    , handle    IN   NUMBER&lt;br /&gt;    , name      IN   VARCHAR2&lt;br /&gt;    , value     IN   BOOLEAN&lt;br /&gt;   );&lt;br /&gt;&lt;br /&gt;   FUNCTION add_transform (&lt;br /&gt;      db_link    IN   VARCHAR2 DEFAULT NULL&lt;br /&gt;    , handle     IN   NUMBER&lt;br /&gt;    , name       IN   VARCHAR2&lt;br /&gt;    , encoding   IN   VARCHAR2 DEFAULT NULL&lt;br /&gt;   )&lt;br /&gt;      RETURN NUMBER;&lt;br /&gt;&lt;br /&gt;   PROCEDURE set_transform_param (&lt;br /&gt;      db_link            IN   VARCHAR2 DEFAULT NULL&lt;br /&gt;    , transform_handle   IN   NUMBER&lt;br /&gt;    , name               IN   VARCHAR2&lt;br /&gt;    , value              IN   VARCHAR2&lt;br /&gt;   );&lt;br /&gt;&lt;br /&gt;   PROCEDURE set_transform_param (&lt;br /&gt;      db_link            IN   VARCHAR2 DEFAULT NULL&lt;br /&gt;    , transform_handle   IN   NUMBER&lt;br /&gt;    , name               IN   VARCHAR2&lt;br /&gt;    , value              IN   BOOLEAN DEFAULT TRUE&lt;br /&gt;   );&lt;br /&gt;&lt;br /&gt;   FUNCTION fetch_ddl_text (&lt;br /&gt;      db_link   IN       VARCHAR2 DEFAULT NULL&lt;br /&gt;    , handle    IN       NUMBER&lt;br /&gt;    , partial   OUT      NUMBER&lt;br /&gt;   )&lt;br /&gt;      RETURN VARCHAR2;&lt;br /&gt;&lt;br /&gt;   FUNCTION fetch_clob (&lt;br /&gt;      db_link   IN   VARCHAR2 DEFAULT NULL&lt;br /&gt;    , handle    IN   NUMBER&lt;br /&gt;   )&lt;br /&gt;      RETURN CLOB;&lt;br /&gt;   &lt;br /&gt;   FUNCTION fetch_ddl_clob (&lt;br /&gt;         db_link   IN   VARCHAR2 DEFAULT NULL&lt;br /&gt;       , handle    IN   NUMBER&lt;br /&gt;      )&lt;br /&gt;      RETURN CLOB;&lt;br /&gt;&lt;br /&gt;   FUNCTION fetch_ddl (&lt;br /&gt;         db_link   IN   VARCHAR2 DEFAULT NULL&lt;br /&gt;       , handle    IN   NUMBER&lt;br /&gt;      )&lt;br /&gt;      RETURN ku$_ddls;&lt;br /&gt;&lt;br /&gt;   PROCEDURE set_default_table_transforms (&lt;br /&gt;      db_link                IN   VARCHAR2 DEFAULT NULL&lt;br /&gt;    , table_transform_handle IN   NUMBER&lt;br /&gt;   );&lt;br /&gt;   &lt;br /&gt;   PROCEDURE set_default_index_transforms (&lt;br /&gt;      db_link                IN   VARCHAR2 DEFAULT NULL&lt;br /&gt;    , index_transform_handle IN   NUMBER&lt;br /&gt;   );&lt;br /&gt;&lt;br /&gt;   FUNCTION get_ddl (db_link   IN       VARCHAR2 DEFAULT NULL&lt;br /&gt;                   , object_type     IN  VARCHAR2&lt;br /&gt;                   , name            IN  VARCHAR2&lt;br /&gt;                   , schema          IN  VARCHAR2 DEFAULT NULL&lt;br /&gt;                   , version         IN  VARCHAR2 DEFAULT 'COMPATIBLE'&lt;br /&gt;                   , model           IN  VARCHAR2 DEFAULT 'ORACLE'&lt;br /&gt;                   , transform       IN  VARCHAR2 DEFAULT 'DDL') &lt;br /&gt;     RETURN CLOB;&lt;br /&gt;&lt;br /&gt;   FUNCTION get_dependent_ddl (&lt;br /&gt;         db_link              IN   VARCHAR2 DEFAULT NULL&lt;br /&gt;       , object_type          IN   VARCHAR2&lt;br /&gt;       , base_object_name     IN   VARCHAR2&lt;br /&gt;       , base_object_schema   IN   VARCHAR2 DEFAULT NULL&lt;br /&gt;       , version              IN   VARCHAR2 DEFAULT 'COMPATIBLE'&lt;br /&gt;       , model                IN   VARCHAR2 DEFAULT 'ORACLE'&lt;br /&gt;       , transform            IN   VARCHAR2 DEFAULT 'DDL'&lt;br /&gt;       , object_count         IN   NUMBER DEFAULT 10000)&lt;br /&gt;      RETURN CLOB;&lt;br /&gt;      &lt;br /&gt;END REMOTE_DBMS_METADATA;&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;SHOW ERRORS;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;create or replace&lt;br /&gt;PACKAGE BODY remote_dbms_metadata AS&lt;br /&gt;/*************************************************************************&lt;br /&gt; * Program  : remote_dbms_metadata&lt;br /&gt; * Version  : 1.0&lt;br /&gt; * Author   : Philip Moore&lt;br /&gt; * Date     : 20-JUN-2009 Anno Domini&lt;br /&gt; * Purpopse : This package provides access to DDL for objects in a remote&lt;br /&gt;              Oracle database via Database Links.&lt;br /&gt; * Warnings : This package has only been tested in Oracle 9iR2 and 10gR2.&lt;br /&gt; *************************************************************************/&lt;br /&gt;&lt;br /&gt;-- SubProcedures&lt;br /&gt;/* ------------------------------------- */&lt;br /&gt;   FUNCTION build_db_link_string (&lt;br /&gt;      p_db_link   IN   VARCHAR2&lt;br /&gt;   )&lt;br /&gt;      RETURN VARCHAR2 IS&lt;br /&gt;      -- Variables&lt;br /&gt;      l_return                           VARCHAR2 (100);&lt;br /&gt;   BEGIN&lt;br /&gt;      IF p_db_link IS NOT NULL THEN&lt;br /&gt;         l_return := '@' &amp;#124;&amp;#124; LTRIM (p_db_link, '@');&lt;br /&gt;      END IF;&lt;br /&gt;&lt;br /&gt;      RETURN l_return;&lt;br /&gt;   END build_db_link_string;&lt;br /&gt;&lt;br /&gt;/* ------------------------------------- */&lt;br /&gt;   FUNCTION open (&lt;br /&gt;      db_link       IN   VARCHAR2 DEFAULT NULL&lt;br /&gt;    , object_type   IN   VARCHAR2&lt;br /&gt;    , version       IN   VARCHAR2 DEFAULT 'COMPATIBLE'&lt;br /&gt;    , model         IN   VARCHAR2 DEFAULT 'ORACLE'&lt;br /&gt;   )&lt;br /&gt;      RETURN NUMBER IS&lt;br /&gt;      -- Variables&lt;br /&gt;      l_plsql_call                       VARCHAR2 (4000);&lt;br /&gt;      l_return                           NUMBER;&lt;br /&gt;      l_db_link                          VARCHAR2 (100) := build_db_link_string (p_db_link      =&amp;gt; db_link);&lt;br /&gt;   BEGIN&lt;br /&gt;      l_plsql_call :=&lt;br /&gt;            'BEGIN '&lt;br /&gt;         &amp;#124;&amp;#124; '     :l_return := DBMS_METADATA.OPEN'&lt;br /&gt;         &amp;#124;&amp;#124; l_db_link&lt;br /&gt;         &amp;#124;&amp;#124; ' (object_type =&amp;gt; :object_type '&lt;br /&gt;         &amp;#124;&amp;#124; ', version     =&amp;gt; :version '&lt;br /&gt;         &amp;#124;&amp;#124; ', model       =&amp;gt; :model '&lt;br /&gt;         &amp;#124;&amp;#124; ' ); '&lt;br /&gt;         &amp;#124;&amp;#124; 'END;';&lt;br /&gt;&lt;br /&gt;      EXECUTE IMMEDIATE l_plsql_call&lt;br /&gt;                  USING OUT    l_return&lt;br /&gt;                      , IN     object_type&lt;br /&gt;                      , IN     version&lt;br /&gt;                      , IN     model;&lt;br /&gt;&lt;br /&gt;      RETURN l_return;&lt;br /&gt;   END open;&lt;br /&gt;&lt;br /&gt;/* ------------------------------------- */&lt;br /&gt;   PROCEDURE dbms_metadata_close (&lt;br /&gt;      db_link   IN   VARCHAR2 DEFAULT NULL&lt;br /&gt;    , handle    IN   NUMBER&lt;br /&gt;   ) IS&lt;br /&gt;      -- Variables&lt;br /&gt;      l_plsql_call                       VARCHAR2 (4000);&lt;br /&gt;      l_db_link                          VARCHAR2 (100) := build_db_link_string (p_db_link      =&amp;gt; db_link);&lt;br /&gt;   BEGIN&lt;br /&gt;      l_plsql_call := 'BEGIN DBMS_METADATA.CLOSE' &amp;#124;&amp;#124; l_db_link &amp;#124;&amp;#124; ' (handle =&amp;gt; :handle); END;';&lt;br /&gt;&lt;br /&gt;      EXECUTE IMMEDIATE l_plsql_call&lt;br /&gt;                  USING IN handle;&lt;br /&gt;   END dbms_metadata_close;&lt;br /&gt;&lt;br /&gt;/* ------------------------------------- */&lt;br /&gt;   PROCEDURE set_count (&lt;br /&gt;      db_link   IN   VARCHAR2 DEFAULT NULL&lt;br /&gt;    , handle    IN   NUMBER&lt;br /&gt;    , value     IN   NUMBER&lt;br /&gt;   ) IS&lt;br /&gt;      -- Variables&lt;br /&gt;      l_plsql_call                       VARCHAR2 (4000);&lt;br /&gt;      l_db_link                          VARCHAR2 (100) := build_db_link_string (p_db_link      =&amp;gt; db_link);&lt;br /&gt;   BEGIN&lt;br /&gt;      l_plsql_call := 'BEGIN DBMS_METADATA.SET_COUNT' &amp;#124;&amp;#124; l_db_link &amp;#124;&amp;#124; ' (handle =&amp;gt; :handle, value  =&amp;gt; :value); END;';&lt;br /&gt;&lt;br /&gt;      EXECUTE IMMEDIATE l_plsql_call&lt;br /&gt;                  USING IN handle, IN value;&lt;br /&gt;   END set_count;&lt;br /&gt;&lt;br /&gt;/* ------------------------------------- */&lt;br /&gt;   PROCEDURE set_filter (&lt;br /&gt;      db_link   IN   VARCHAR2 DEFAULT NULL&lt;br /&gt;    , handle    IN   NUMBER&lt;br /&gt;    , name      IN   VARCHAR2&lt;br /&gt;    , value     IN   VARCHAR2&lt;br /&gt;   ) IS&lt;br /&gt;      -- Variables&lt;br /&gt;      l_plsql_call                       VARCHAR2 (4000);&lt;br /&gt;      l_db_link                          VARCHAR2 (100) := build_db_link_string (p_db_link      =&amp;gt; db_link);&lt;br /&gt;   BEGIN&lt;br /&gt;      l_plsql_call :=&lt;br /&gt;            'BEGIN '&lt;br /&gt;         &amp;#124;&amp;#124; '     DBMS_METADATA.SET_FILTER'&lt;br /&gt;         &amp;#124;&amp;#124; l_db_link&lt;br /&gt;         &amp;#124;&amp;#124; ' (handle =&amp;gt; :handle '&lt;br /&gt;         &amp;#124;&amp;#124; ', name   =&amp;gt; :name '&lt;br /&gt;         &amp;#124;&amp;#124; ', value  =&amp;gt; :value '&lt;br /&gt;         &amp;#124;&amp;#124; '); '&lt;br /&gt;         &amp;#124;&amp;#124; 'END;';&lt;br /&gt;&lt;br /&gt;      EXECUTE IMMEDIATE l_plsql_call&lt;br /&gt;                  USING IN handle&lt;br /&gt;                      , IN name&lt;br /&gt;                      , IN value;&lt;br /&gt;   END set_filter;&lt;br /&gt;&lt;br /&gt;/* ------------------------------------- */&lt;br /&gt;   PROCEDURE set_filter (&lt;br /&gt;      db_link   IN   VARCHAR2 DEFAULT NULL&lt;br /&gt;    , handle    IN   NUMBER&lt;br /&gt;    , name      IN   VARCHAR2&lt;br /&gt;    , value     IN   BOOLEAN&lt;br /&gt;   ) IS&lt;br /&gt;      -- Variables&lt;br /&gt;      l_plsql_call                       VARCHAR2 (4000);&lt;br /&gt;      l_db_link                          VARCHAR2 (100) := build_db_link_string (p_db_link      =&amp;gt; db_link);&lt;br /&gt;   BEGIN&lt;br /&gt;      l_plsql_call :=&lt;br /&gt;            'BEGIN '&lt;br /&gt;         &amp;#124;&amp;#124; '     DBMS_METADATA.SET_FILTER'&lt;br /&gt;         &amp;#124;&amp;#124; l_db_link&lt;br /&gt;         &amp;#124;&amp;#124; ' (handle =&amp;gt; :handle '&lt;br /&gt;         &amp;#124;&amp;#124; ', name   =&amp;gt; :name '&lt;br /&gt;         &amp;#124;&amp;#124; ', value  =&amp;gt; '&lt;br /&gt;         &amp;#124;&amp;#124; boolean_pkg.bool_to_str (boolean_in      =&amp;gt; value)&lt;br /&gt;         &amp;#124;&amp;#124; '); '&lt;br /&gt;         &amp;#124;&amp;#124; 'END;';&lt;br /&gt;&lt;br /&gt;      EXECUTE IMMEDIATE l_plsql_call&lt;br /&gt;                  USING IN handle, IN name;&lt;br /&gt;   END set_filter;&lt;br /&gt;&lt;br /&gt;/* ------------------------------------- */&lt;br /&gt;   FUNCTION add_transform (&lt;br /&gt;      db_link    IN   VARCHAR2 DEFAULT NULL&lt;br /&gt;    , handle     IN   NUMBER&lt;br /&gt;    , name       IN   VARCHAR2&lt;br /&gt;    , encoding   IN   VARCHAR2 DEFAULT NULL&lt;br /&gt;   )&lt;br /&gt;      RETURN NUMBER IS&lt;br /&gt;      -- Variables&lt;br /&gt;      l_plsql_call                       VARCHAR2 (4000);&lt;br /&gt;      l_return                           NUMBER;&lt;br /&gt;      l_db_link                          VARCHAR2 (100) := build_db_link_string (p_db_link      =&amp;gt; db_link);&lt;br /&gt;   BEGIN&lt;br /&gt;      l_plsql_call :=&lt;br /&gt;            'BEGIN '&lt;br /&gt;         &amp;#124;&amp;#124; '   :l_return := DBMS_METADATA.ADD_TRANSFORM'&lt;br /&gt;         &amp;#124;&amp;#124; l_db_link&lt;br /&gt;         &amp;#124;&amp;#124; ' (handle =&amp;gt; :handle '&lt;br /&gt;         &amp;#124;&amp;#124; ', name   =&amp;gt; :name '&lt;br /&gt;         &amp;#124;&amp;#124; ', encoding =&amp;gt; :encoding '&lt;br /&gt;         &amp;#124;&amp;#124; '); '&lt;br /&gt;         &amp;#124;&amp;#124; 'END;';&lt;br /&gt;&lt;br /&gt;      EXECUTE IMMEDIATE l_plsql_call&lt;br /&gt;                  USING OUT    l_return&lt;br /&gt;                      , IN     handle&lt;br /&gt;                      , IN     name&lt;br /&gt;                      , IN     encoding;&lt;br /&gt;&lt;br /&gt;      RETURN l_return;&lt;br /&gt;   END add_transform;&lt;br /&gt;&lt;br /&gt;/* ------------------------------------- */&lt;br /&gt;   PROCEDURE set_transform_param (&lt;br /&gt;      db_link            IN   VARCHAR2 DEFAULT NULL&lt;br /&gt;    , transform_handle   IN   NUMBER&lt;br /&gt;    , name               IN   VARCHAR2&lt;br /&gt;    , value              IN   VARCHAR2&lt;br /&gt;   ) IS&lt;br /&gt;      -- Variables&lt;br /&gt;      l_plsql_call                       VARCHAR2 (4000);&lt;br /&gt;      l_db_link                          VARCHAR2 (100) := build_db_link_string (p_db_link      =&amp;gt; db_link);&lt;br /&gt;      l_session_transform_param_rec      session_transform_param_rec;&lt;br /&gt;   BEGIN&lt;br /&gt;      IF transform_handle = DBMS_METADATA.session_transform THEN&lt;br /&gt;         /* Since we don't hold a session open for a remote database - we have to&lt;br /&gt;            store the session transformation parameters in a local global structure */&lt;br /&gt;         l_session_transform_param_rec.parameter_name := UPPER (TRIM (name));&lt;br /&gt;         l_session_transform_param_rec.value_datatype := c_varchar2_type;&lt;br /&gt;         l_session_transform_param_rec.varchar2_value := value;&lt;br /&gt;         g_session_transform_params (db_link) (l_session_transform_param_rec.parameter_name) := l_session_transform_param_rec;&lt;br /&gt;      ELSE&lt;br /&gt;         l_plsql_call :=&lt;br /&gt;               'BEGIN '&lt;br /&gt;            &amp;#124;&amp;#124; '   DBMS_METADATA.SET_TRANSFORM_PARAM'&lt;br /&gt;            &amp;#124;&amp;#124; l_db_link&lt;br /&gt;            &amp;#124;&amp;#124; ' (transform_handle =&amp;gt; :transform_handle '&lt;br /&gt;            &amp;#124;&amp;#124; ', name             =&amp;gt; :name '&lt;br /&gt;            &amp;#124;&amp;#124; ', value            =&amp;gt; :value '&lt;br /&gt;            &amp;#124;&amp;#124; '); '&lt;br /&gt;            &amp;#124;&amp;#124; 'END;';&lt;br /&gt;&lt;br /&gt;         EXECUTE IMMEDIATE l_plsql_call&lt;br /&gt;                     USING IN transform_handle&lt;br /&gt;                         , IN name&lt;br /&gt;                         , IN value;&lt;br /&gt;      END IF;&lt;br /&gt;   END set_transform_param;&lt;br /&gt;&lt;br /&gt;/* ------------------------------------- */&lt;br /&gt;   PROCEDURE set_transform_param (&lt;br /&gt;      db_link            IN   VARCHAR2 DEFAULT NULL&lt;br /&gt;    , transform_handle   IN   NUMBER&lt;br /&gt;    , name               IN   VARCHAR2&lt;br /&gt;    , value              IN   BOOLEAN DEFAULT TRUE&lt;br /&gt;   ) IS&lt;br /&gt;      -- Variables&lt;br /&gt;      l_plsql_call                       VARCHAR2 (4000);&lt;br /&gt;      l_db_link                          VARCHAR2 (100) := build_db_link_string (p_db_link      =&amp;gt; db_link);&lt;br /&gt;      l_session_transform_param_rec      session_transform_param_rec;&lt;br /&gt;   BEGIN&lt;br /&gt;      IF transform_handle = DBMS_METADATA.session_transform THEN&lt;br /&gt;         /* Since we don't hold a session open for a remote database - we have to&lt;br /&gt;            store the session transformation parameters in a local global structure */&lt;br /&gt;         l_session_transform_param_rec.parameter_name := UPPER (TRIM (name));&lt;br /&gt;         l_session_transform_param_rec.value_datatype := c_boolean_type;&lt;br /&gt;         l_session_transform_param_rec.boolean_value := value;&lt;br /&gt;         g_session_transform_params (db_link) (l_session_transform_param_rec.parameter_name) := l_session_transform_param_rec;&lt;br /&gt;      ELSE&lt;br /&gt;         l_plsql_call :=&lt;br /&gt;               'BEGIN '&lt;br /&gt;            &amp;#124;&amp;#124; '   DBMS_METADATA.SET_TRANSFORM_PARAM'&lt;br /&gt;            &amp;#124;&amp;#124; l_db_link&lt;br /&gt;            &amp;#124;&amp;#124; ' (transform_handle =&amp;gt; :transform_handle '&lt;br /&gt;            &amp;#124;&amp;#124; ', name             =&amp;gt; :name '&lt;br /&gt;            &amp;#124;&amp;#124; ', value            =&amp;gt; '&lt;br /&gt;            &amp;#124;&amp;#124; boolean_pkg.bool_to_str (boolean_in      =&amp;gt; value)&lt;br /&gt;            &amp;#124;&amp;#124; '); '&lt;br /&gt;            &amp;#124;&amp;#124; 'END;';&lt;br /&gt;&lt;br /&gt;         EXECUTE IMMEDIATE l_plsql_call&lt;br /&gt;                     USING IN transform_handle, IN name;&lt;br /&gt;      END IF;&lt;br /&gt;   END set_transform_param;&lt;br /&gt;&lt;br /&gt;/* ------------------------------------- */&lt;br /&gt;   PROCEDURE set_transform_param (&lt;br /&gt;      db_link            IN   VARCHAR2 DEFAULT NULL&lt;br /&gt;    , transform_handle   IN   NUMBER&lt;br /&gt;    , name               IN   VARCHAR2&lt;br /&gt;    , value              IN   NUMBER&lt;br /&gt;   ) IS&lt;br /&gt;      -- Variables&lt;br /&gt;      l_plsql_call                       VARCHAR2 (4000);&lt;br /&gt;      l_db_link                          VARCHAR2 (100) := build_db_link_string (p_db_link      =&amp;gt; db_link);&lt;br /&gt;      l_session_transform_param_rec      session_transform_param_rec;&lt;br /&gt;   BEGIN&lt;br /&gt;      IF transform_handle = DBMS_METADATA.session_transform THEN&lt;br /&gt;         /* Since we don't hold a session open for a remote database - we have to&lt;br /&gt;            store the session transformation parameters in a local global structure */&lt;br /&gt;         l_session_transform_param_rec.parameter_name := UPPER (TRIM (name));&lt;br /&gt;         l_session_transform_param_rec.value_datatype := c_number_type;&lt;br /&gt;         l_session_transform_param_rec.number_value := value;&lt;br /&gt;         g_session_transform_params (db_link) (l_session_transform_param_rec.parameter_name) := l_session_transform_param_rec;&lt;br /&gt;      ELSE&lt;br /&gt;         l_plsql_call :=&lt;br /&gt;               'BEGIN '&lt;br /&gt;            &amp;#124;&amp;#124; '   DBMS_METADATA.SET_TRANSFORM_PARAM'&lt;br /&gt;            &amp;#124;&amp;#124; l_db_link&lt;br /&gt;            &amp;#124;&amp;#124; ' (transform_handle =&amp;gt; :transform_handle '&lt;br /&gt;            &amp;#124;&amp;#124; ', name             =&amp;gt; :name '&lt;br /&gt;            &amp;#124;&amp;#124; ', value            =&amp;gt; :value '&lt;br /&gt;            &amp;#124;&amp;#124; '); '&lt;br /&gt;            &amp;#124;&amp;#124; 'END;';&lt;br /&gt;&lt;br /&gt;         EXECUTE IMMEDIATE l_plsql_call&lt;br /&gt;                     USING IN transform_handle&lt;br /&gt;                         , IN name&lt;br /&gt;                         , IN value;&lt;br /&gt;      END IF;&lt;br /&gt;   END set_transform_param;&lt;br /&gt;&lt;br /&gt;/* ------------------------------------- */&lt;br /&gt;   FUNCTION fetch_ddl_text (&lt;br /&gt;      db_link   IN       VARCHAR2 DEFAULT NULL&lt;br /&gt;    , handle    IN       NUMBER&lt;br /&gt;    , partial   OUT      NUMBER&lt;br /&gt;   )&lt;br /&gt;      RETURN VARCHAR2 IS&lt;br /&gt;      -- Variables&lt;br /&gt;      l_plsql_call                       VARCHAR2 (4000);&lt;br /&gt;      l_db_link                          VARCHAR2 (100) := build_db_link_string (p_db_link      =&amp;gt; db_link);&lt;br /&gt;      l_return                           VARCHAR2 (32767);&lt;br /&gt;   BEGIN&lt;br /&gt;      l_plsql_call := 'BEGIN :l_return := DBMS_METADATA.fetch_ddl_text' &amp;#124;&amp;#124; l_db_link &amp;#124;&amp;#124; ' (handle =&amp;gt; :handle, partial =&amp;gt; :partial); END;';&lt;br /&gt;&lt;br /&gt;      EXECUTE IMMEDIATE l_plsql_call&lt;br /&gt;                  USING OUT    l_return&lt;br /&gt;                      , IN     handle&lt;br /&gt;                      , OUT    partial;&lt;br /&gt;&lt;br /&gt;      RETURN l_return;&lt;br /&gt;   END fetch_ddl_text;&lt;br /&gt;&lt;br /&gt;/* ------------------------------------- */&lt;br /&gt;   FUNCTION fetch_clob (&lt;br /&gt;      db_link   IN   VARCHAR2 DEFAULT NULL&lt;br /&gt;    , handle    IN   NUMBER&lt;br /&gt;   )&lt;br /&gt;      RETURN CLOB IS&lt;br /&gt;      -- Variables&lt;br /&gt;      l_partial                          PLS_INTEGER;&lt;br /&gt;      l_return                           CLOB;&lt;br /&gt;   BEGIN&lt;br /&gt;      -- Loop until the partial flag is 0&lt;br /&gt;      LOOP&lt;br /&gt;         l_return := l_return &amp;#124;&amp;#124; fetch_ddl_text (db_link      =&amp;gt; db_link&lt;br /&gt;                                               , handle       =&amp;gt; handle&lt;br /&gt;                                               , partial      =&amp;gt; l_partial&lt;br /&gt;                                                );&lt;br /&gt;         EXIT WHEN l_partial = 0;&lt;br /&gt;      END LOOP;&lt;br /&gt;&lt;br /&gt;      RETURN l_return;&lt;br /&gt;   END fetch_clob;&lt;br /&gt;&lt;br /&gt;/* ------------------------------------- */&lt;br /&gt;   FUNCTION fetch_ddl_clob (&lt;br /&gt;      db_link   IN   VARCHAR2 DEFAULT NULL&lt;br /&gt;    , handle    IN   NUMBER&lt;br /&gt;   )&lt;br /&gt;      RETURN CLOB IS&lt;br /&gt;      -- Variables&lt;br /&gt;      l_return                           CLOB;&lt;br /&gt;   BEGIN&lt;br /&gt;&lt;br /&gt;      -- Keep fetching until we get an error...&lt;br /&gt;      &amp;lt;&amp;lt;fetch_loop&amp;gt;&amp;gt;&lt;br /&gt;      LOOP&lt;br /&gt;         BEGIN&lt;br /&gt;            l_return := l_return &amp;#124;&amp;#124; fetch_clob (db_link      =&amp;gt; db_link, handle =&amp;gt; handle);&lt;br /&gt;         EXCEPTION&lt;br /&gt;            WHEN DBMS_METADATA.invalid_argval THEN&lt;br /&gt;               EXIT fetch_loop;&lt;br /&gt;         END;&lt;br /&gt;      END LOOP fetch_loop;&lt;br /&gt;&lt;br /&gt;      RETURN l_return;&lt;br /&gt;   END fetch_ddl_clob;&lt;br /&gt;&lt;br /&gt;/* ------------------------------------- */&lt;br /&gt;   FUNCTION fetch_ddl (&lt;br /&gt;      db_link   IN   VARCHAR2 DEFAULT NULL&lt;br /&gt;    , handle    IN   NUMBER&lt;br /&gt;   )&lt;br /&gt;      RETURN ku$_ddls IS&lt;br /&gt;      -- Variables&lt;br /&gt;      l_temp_clob                        CLOB;&lt;br /&gt;      l_return                           ku$_ddls := ku$_ddls ();&lt;br /&gt;   BEGIN&lt;br /&gt;&lt;br /&gt;      -- Keep fetching until we get an error...&lt;br /&gt;      &amp;lt;&amp;lt;fetch_loop&amp;gt;&amp;gt;&lt;br /&gt;      LOOP&lt;br /&gt;         BEGIN&lt;br /&gt;            l_temp_clob := fetch_clob (db_link      =&amp;gt; db_link, handle =&amp;gt; handle);&lt;br /&gt;            l_return.EXTEND;&lt;br /&gt;            l_return (l_return.COUNT).ddltext := l_temp_clob;&lt;br /&gt;         EXCEPTION&lt;br /&gt;            WHEN DBMS_METADATA.invalid_argval THEN&lt;br /&gt;               EXIT fetch_loop;&lt;br /&gt;         END;&lt;br /&gt;      END LOOP fetch_loop;&lt;br /&gt;&lt;br /&gt;      RETURN l_return;&lt;br /&gt;   END fetch_ddl;&lt;br /&gt;&lt;br /&gt;/* ------------------------------------- */&lt;br /&gt;   PROCEDURE set_default_table_transforms (&lt;br /&gt;      db_link                  IN   VARCHAR2 DEFAULT NULL&lt;br /&gt;    , table_transform_handle   IN   NUMBER&lt;br /&gt;   ) IS&lt;br /&gt;   BEGIN&lt;br /&gt;      set_transform_param (db_link               =&amp;gt; db_link&lt;br /&gt;                         , transform_handle      =&amp;gt; table_transform_handle&lt;br /&gt;                         , name                  =&amp;gt; 'PRETTY'&lt;br /&gt;                         , value                 =&amp;gt; TRUE&lt;br /&gt;                          );&lt;br /&gt;      set_transform_param (db_link               =&amp;gt; db_link&lt;br /&gt;                         , transform_handle      =&amp;gt; table_transform_handle&lt;br /&gt;                         , name                  =&amp;gt; 'SQLTERMINATOR'&lt;br /&gt;                         , value                 =&amp;gt; TRUE&lt;br /&gt;                          );&lt;br /&gt;      set_transform_param (db_link               =&amp;gt; db_link&lt;br /&gt;                         , transform_handle      =&amp;gt; table_transform_handle&lt;br /&gt;                         , name                  =&amp;gt; 'SIZE_BYTE_KEYWORD'&lt;br /&gt;                         , value                 =&amp;gt; FALSE&lt;br /&gt;                          );&lt;br /&gt;      set_transform_param (db_link               =&amp;gt; db_link&lt;br /&gt;                         , transform_handle      =&amp;gt; table_transform_handle&lt;br /&gt;                         , name                  =&amp;gt; 'SEGMENT_ATTRIBUTES'&lt;br /&gt;                         , value                 =&amp;gt; TRUE&lt;br /&gt;                          );&lt;br /&gt;      set_transform_param (db_link               =&amp;gt; db_link&lt;br /&gt;                         , transform_handle      =&amp;gt; table_transform_handle&lt;br /&gt;                         , name                  =&amp;gt; 'STORAGE'&lt;br /&gt;                         , value                 =&amp;gt; FALSE&lt;br /&gt;                          );&lt;br /&gt;      set_transform_param (db_link               =&amp;gt; db_link&lt;br /&gt;                         , transform_handle      =&amp;gt; table_transform_handle&lt;br /&gt;                         , name                  =&amp;gt; 'TABLESPACE'&lt;br /&gt;                         , value                 =&amp;gt; TRUE&lt;br /&gt;                          );&lt;br /&gt;      set_transform_param (db_link               =&amp;gt; db_link&lt;br /&gt;                         , transform_handle      =&amp;gt; table_transform_handle&lt;br /&gt;                         , name                  =&amp;gt; 'CONSTRAINTS_AS_ALTER'&lt;br /&gt;                         , value                 =&amp;gt; TRUE&lt;br /&gt;                          );&lt;br /&gt;   END set_default_table_transforms;&lt;br /&gt;&lt;br /&gt;/* ------------------------------------- */&lt;br /&gt;   PROCEDURE set_default_index_transforms (&lt;br /&gt;      db_link                  IN   VARCHAR2 DEFAULT NULL&lt;br /&gt;    , index_transform_handle   IN   NUMBER&lt;br /&gt;   ) IS&lt;br /&gt;   BEGIN&lt;br /&gt;      set_transform_param (db_link               =&amp;gt; db_link&lt;br /&gt;                         , transform_handle      =&amp;gt; index_transform_handle&lt;br /&gt;                         , name                  =&amp;gt; 'PRETTY'&lt;br /&gt;                         , value                 =&amp;gt; TRUE&lt;br /&gt;                          );&lt;br /&gt;      set_transform_param (db_link               =&amp;gt; db_link&lt;br /&gt;                         , transform_handle      =&amp;gt; index_transform_handle&lt;br /&gt;                         , name                  =&amp;gt; 'SQLTERMINATOR'&lt;br /&gt;                         , value                 =&amp;gt; TRUE&lt;br /&gt;                          );&lt;br /&gt;   END set_default_index_transforms;&lt;br /&gt;&lt;br /&gt;/* ------------------------------------- */&lt;br /&gt;   PROCEDURE apply_session_transform_params (&lt;br /&gt;      db_link            IN   VARCHAR2&lt;br /&gt;    , transform_handle   IN   NUMBER&lt;br /&gt;   ) IS&lt;br /&gt;      -- Variables&lt;br /&gt;      l_array_index                      VARCHAR2 (50);&lt;br /&gt;   BEGIN&lt;br /&gt;      IF db_link IS NOT NULL THEN&lt;br /&gt;         -- Apply any global session trasformation parameters which have been set prior to this call...&lt;br /&gt;         l_array_index := g_session_transform_params (db_link).FIRST;&lt;br /&gt;&lt;br /&gt;         WHILE l_array_index IS NOT NULL LOOP&lt;br /&gt;            CASE g_session_transform_params (db_link) (l_array_index).value_datatype&lt;br /&gt;               WHEN c_varchar2_type THEN&lt;br /&gt;                  set_transform_param (db_link               =&amp;gt; db_link&lt;br /&gt;                                     , transform_handle      =&amp;gt; transform_handle&lt;br /&gt;                                     , name                  =&amp;gt; g_session_transform_params (db_link) (l_array_index).parameter_name&lt;br /&gt;                                     , value                 =&amp;gt; g_session_transform_params (db_link) (l_array_index).varchar2_value&lt;br /&gt;                                      );&lt;br /&gt;               WHEN c_boolean_type THEN&lt;br /&gt;                  set_transform_param (db_link               =&amp;gt; db_link&lt;br /&gt;                                     , transform_handle      =&amp;gt; transform_handle&lt;br /&gt;                                     , name                  =&amp;gt; g_session_transform_params (db_link) (l_array_index).parameter_name&lt;br /&gt;                                     , value                 =&amp;gt; g_session_transform_params (db_link) (l_array_index).boolean_value&lt;br /&gt;                                      );&lt;br /&gt;               WHEN c_number_type THEN&lt;br /&gt;                  set_transform_param (db_link               =&amp;gt; db_link&lt;br /&gt;                                     , transform_handle      =&amp;gt; transform_handle&lt;br /&gt;                                     , name                  =&amp;gt; g_session_transform_params (db_link) (l_array_index).parameter_name&lt;br /&gt;                                     , value                 =&amp;gt; g_session_transform_params (db_link) (l_array_index).number_value&lt;br /&gt;                                      );&lt;br /&gt;            END CASE;&lt;br /&gt;&lt;br /&gt;            l_array_index := g_session_transform_params (db_link).NEXT (l_array_index);&lt;br /&gt;         END LOOP;&lt;br /&gt;      END IF;&lt;br /&gt;   &lt;br /&gt;   EXCEPTION&lt;br /&gt;      WHEN NO_DATA_FOUND THEN&lt;br /&gt;         NULL;&lt;br /&gt;   END apply_session_transform_params;&lt;br /&gt;&lt;br /&gt;/* ------------------------------------- */&lt;br /&gt;   FUNCTION get_ddl (&lt;br /&gt;      db_link       IN   VARCHAR2 DEFAULT NULL&lt;br /&gt;    , object_type   IN   VARCHAR2&lt;br /&gt;    , name          IN   VARCHAR2&lt;br /&gt;    , SCHEMA        IN   VARCHAR2 DEFAULT NULL&lt;br /&gt;    , version       IN   VARCHAR2 DEFAULT 'COMPATIBLE'&lt;br /&gt;    , model         IN   VARCHAR2 DEFAULT 'ORACLE'&lt;br /&gt;    , transform     IN   VARCHAR2 DEFAULT 'DDL'&lt;br /&gt;   )&lt;br /&gt;      RETURN CLOB IS&lt;br /&gt;      -- Variables&lt;br /&gt;      l_return                           CLOB;&lt;br /&gt;      l_handle                           NUMBER;&lt;br /&gt;      l_transform                        NUMBER;&lt;br /&gt;&lt;br /&gt;      -- Private Sub-Procedure&lt;br /&gt;      PROCEDURE cleanup IS&lt;br /&gt;      BEGIN&lt;br /&gt;         dbms_metadata_close (db_link      =&amp;gt; db_link, handle =&amp;gt; l_handle);&lt;br /&gt;      EXCEPTION&lt;br /&gt;         WHEN handle_not_open THEN&lt;br /&gt;            NULL;&lt;br /&gt;      END cleanup;&lt;br /&gt;   BEGIN&lt;br /&gt;      l_handle := remote_dbms_metadata.open (db_link          =&amp;gt; db_link, object_type =&amp;gt; object_type);&lt;br /&gt;      set_count (db_link      =&amp;gt; db_link&lt;br /&gt;               , handle       =&amp;gt; l_handle&lt;br /&gt;               , value        =&amp;gt; 1&lt;br /&gt;                );&lt;br /&gt;      set_filter (db_link      =&amp;gt; db_link&lt;br /&gt;                , handle       =&amp;gt; l_handle&lt;br /&gt;                , name         =&amp;gt; 'NAME'&lt;br /&gt;                , value        =&amp;gt; name&lt;br /&gt;                 );&lt;br /&gt;      set_filter (db_link      =&amp;gt; db_link&lt;br /&gt;                , handle       =&amp;gt; l_handle&lt;br /&gt;                , name         =&amp;gt; 'SCHEMA'&lt;br /&gt;                , value        =&amp;gt; SCHEMA&lt;br /&gt;                 );&lt;br /&gt;      l_transform := add_transform (db_link      =&amp;gt; db_link&lt;br /&gt;                                  , handle       =&amp;gt; l_handle&lt;br /&gt;                                  , name         =&amp;gt; transform&lt;br /&gt;                                   );&lt;br /&gt;      apply_session_transform_params (db_link               =&amp;gt; db_link, transform_handle =&amp;gt; l_transform);&lt;br /&gt;      l_return := fetch_ddl_clob (db_link      =&amp;gt; db_link, handle =&amp;gt; l_handle);&lt;br /&gt;      cleanup;&lt;br /&gt;      RETURN l_return;&lt;br /&gt;   EXCEPTION&lt;br /&gt;      WHEN OTHERS THEN&lt;br /&gt;         cleanup;&lt;br /&gt;         RAISE;&lt;br /&gt;   END get_ddl;&lt;br /&gt;&lt;br /&gt;/* ------------------------------------- */&lt;br /&gt;   FUNCTION get_dependent_ddl (&lt;br /&gt;      db_link              IN   VARCHAR2 DEFAULT NULL&lt;br /&gt;    , object_type          IN   VARCHAR2&lt;br /&gt;    , base_object_name     IN   VARCHAR2&lt;br /&gt;    , base_object_schema   IN   VARCHAR2 DEFAULT NULL&lt;br /&gt;    , version              IN   VARCHAR2 DEFAULT 'COMPATIBLE'&lt;br /&gt;    , model                IN   VARCHAR2 DEFAULT 'ORACLE'&lt;br /&gt;    , transform            IN   VARCHAR2 DEFAULT 'DDL'&lt;br /&gt;    , object_count         IN   NUMBER DEFAULT 10000&lt;br /&gt;   )&lt;br /&gt;      RETURN CLOB IS&lt;br /&gt;      -- Variables&lt;br /&gt;      l_return                           CLOB;&lt;br /&gt;      l_handle                           NUMBER;&lt;br /&gt;      l_transform                        NUMBER;&lt;br /&gt;&lt;br /&gt;      -- Private Sub-Procedure&lt;br /&gt;      PROCEDURE cleanup IS&lt;br /&gt;      BEGIN&lt;br /&gt;         dbms_metadata_close (db_link      =&amp;gt; db_link, handle =&amp;gt; l_handle);&lt;br /&gt;      EXCEPTION&lt;br /&gt;         WHEN handle_not_open THEN&lt;br /&gt;            NULL;&lt;br /&gt;      END cleanup;&lt;br /&gt;   BEGIN&lt;br /&gt;      l_handle := remote_dbms_metadata.open (db_link          =&amp;gt; db_link, object_type =&amp;gt; object_type);&lt;br /&gt;      set_filter (db_link      =&amp;gt; db_link&lt;br /&gt;                , handle       =&amp;gt; l_handle&lt;br /&gt;                , name         =&amp;gt; 'BASE_OBJECT_NAME'&lt;br /&gt;                , value        =&amp;gt; base_object_name&lt;br /&gt;                 );&lt;br /&gt;      set_filter (db_link      =&amp;gt; db_link&lt;br /&gt;                , handle       =&amp;gt; l_handle&lt;br /&gt;                , name         =&amp;gt; 'BASE_OBJECT_SCHEMA'&lt;br /&gt;                , value        =&amp;gt; base_object_schema&lt;br /&gt;                 );&lt;br /&gt;      set_count (db_link      =&amp;gt; db_link&lt;br /&gt;               , handle       =&amp;gt; l_handle&lt;br /&gt;               , value        =&amp;gt; object_count&lt;br /&gt;                );&lt;br /&gt;      l_transform := remote_dbms_metadata.add_transform (db_link      =&amp;gt; db_link&lt;br /&gt;                                                       , handle       =&amp;gt; l_handle&lt;br /&gt;                                                       , name         =&amp;gt; transform&lt;br /&gt;                                                        );&lt;br /&gt;      apply_session_transform_params (db_link               =&amp;gt; db_link, transform_handle =&amp;gt; l_transform);&lt;br /&gt;      l_return := fetch_ddl_clob (db_link      =&amp;gt; db_link, handle =&amp;gt; l_handle);&lt;br /&gt;      cleanup;&lt;br /&gt;      RETURN l_return;&lt;br /&gt;   EXCEPTION&lt;br /&gt;      WHEN OTHERS THEN&lt;br /&gt;         cleanup;&lt;br /&gt;         RAISE;&lt;br /&gt;   END get_dependent_ddl;&lt;br /&gt;END remote_dbms_metadata;&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;SHOW ERRORS&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family:arial;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;&lt;strong&gt;Watchouts: &lt;/strong&gt;Remember - since PL/SQL Packages require DIRECT granted permissions (not through a role) - the schema you compile this package in requiresj EXECUTE privileges on the SYS.DBMS_METADATA package.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:Arial;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:Arial;"&gt;Also - using DBMS_METADATA.GET_DDL requires the "SELECT_CATALOG_ROLE" if you are using it to get DDL in a schema other than your own - so the user you have defined your database link to use MUST have that privilege in the remote database for this to work.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:Arial;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:Arial;"&gt;&lt;strong&gt;How to use: &lt;/strong&gt;Just call it from SQL!&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:Arial;"&gt;Example call:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:Arial;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"&gt;&lt;code&gt;select remote_dbms_metadata.get_ddl ('PROD_LINK'&lt;br /&gt;                                   , 'TABLE'&lt;br /&gt;                                   , 'EMP'&lt;br /&gt;                                   , 'SCOTT') AS table_ddl&lt;br /&gt;     , remote_dbms_metadata.get_dependent_ddl ('PROD_LINK'&lt;br /&gt;                                             , 'INDEX'&lt;br /&gt;                                             , 'EMP'&lt;br /&gt;                                             , 'SCOTT') AS index_ddl&lt;br /&gt;FROM dual; &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style="font-family:Arial;"&gt;&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;Notes:&lt;/strong&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;This package has been tested in Oracle 9iR2 and 10GR2.&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Good luck...&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Special thanks &lt;/strong&gt;to Greg Houston's &lt;a href="http://formatmysourcecode.blogspot.com/"&gt;http://formatmysourcecode.blogspot.com/&lt;/a&gt; for helping me format the code for blogger!&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5882766990283082528-1429710275069515728?l=phil-sqltips.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://phil-sqltips.blogspot.com/feeds/1429710275069515728/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://phil-sqltips.blogspot.com/2009/06/dbmsmetadata-across-database-links.html#comment-form' title='10 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5882766990283082528/posts/default/1429710275069515728'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5882766990283082528/posts/default/1429710275069515728'/><link rel='alternate' type='text/html' href='http://phil-sqltips.blogspot.com/2009/06/dbmsmetadata-across-database-links.html' title='DBMS_METADATA Across Database Links!'/><author><name>Phil</name><uri>http://www.blogger.com/profile/01851424617108510453</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>10</thr:total></entry></feed>
