#!/usr/local/bin/oraperl 'di'; 'ig00'; ############################################################################### # @(#) unloadr : dump data from Oracle table to ASCII "merge" file # @(#) SunOS deep sun4c (jstander) # @(#) loc: /home/jstander/bin # @(#) $Revision 1.0 $ (jstander 07.06.93): new # # Name # unloadr # $Synopsis=<.. X # Description # dumps Oracle table as ASCII flat file in "merge" format, which has # each field comma-delimited and enclosed by quotes. # $Options=< records from the database -Cz compress output with 'zip' -Cc compress output with 'compress' Default is to direct output to .mrg No unloading is done. -w{whereclause} A SQL expression to restrict SELECTed records from table. Note only the expression should be passed, not "WHERE =", see example below. tablename Name of the table to be unloaded. The column defini- tions are extracted and a generic SQL statement is automatically constructed to SELECT all columns for unloading, subject only to the restrictions of the -w option. The user may supply their own SQL statement to select columns for unloading by writing the SQL statement into a file and giving the file name, preceeded by an '@' character, in place of the "tablename" argument. X # Note # Any field name ending in "COMMENT(S)" is right-trimmed before unloading. # Any embedded newlines in the COMMENT(S) field are converted to the # meta-char "\n" # # Example # Unload all data for cruise number RTMP92-02 from a table called # RTMP_OBS_VES_ENTRY. Output will be to the file RTMP9202.ves. # # unloadr -w "cruise_num='RTMP92-02'" -o RTMP9202.ves rtmp_obs_ves_entry # # Bugs # Need to change login to handle OPS$db.table or db.table for table name # Clusters need to be handled correctly when creating table create .sql file # Clustering ignored for now. # Constraints not extracted when creating dex or sql files. # Author # Jeff.Stander@ml.csiro.au # CSIRO Division Of Fisheries # Hobart, Tasmania 7001, Australia ############################################################################### require "getopts.pl"; require "pwd.pl"; require 'ctime.pl'; sub today { #------------------------------------------------------------------------------ # today returns the date in one of six formats, depending on the argument # 1 25 Jul 93 - dd mmm yy # 2 25 Jul 1993 - dd mmm yyyy # 3 25/06/93 - European/Australian format # 4 06/25/93 - U.S.A. format # 5 13:08 - hh:mm # 6 13:08:24 - hh:mm:ss #------------------------------------------------------------------------------ local($format) = @_ ? @_ : 1; local($sec, $min, $hour, $mday, $mon, $year, $wday, $YEAR, $tod, $month, $res); ($wday, $month, $mday, $tod, $YEAR) = split(' ',&ctime(time)); ($sec, $min, $hour, $mday, $mon, $year, $wday) = localtime(time); if ( $format == 1 ) { $res=sprintf("%02d %s %02d",$mday,$month,$year) } if ( $format == 2 ) { $res=sprintf("%02d %s %02d",$mday,$month,$YEAR) } if ( $format == 3 ) { $res=sprintf("%02d/%02d/%02d",$mday,$mon,$year) } if ( $format == 4 ) { $res=sprintf("%02d/%02d/%02d",$mon,$mday,$year) } if ( $format == 5 ) { $res=sprintf("%02d:%02d",$hour,$min) } if ( $format == 6 ) { $res=$tod } $res; } #----------------------------------------------------------------------------- # Print line withoffset, split at word boundary # Synopsis: printdex(offset,length,dex) # Arguments: # offset - offset from left margin in character spaces # length - total length of line including offset in character spaces # dex - line to print #------------------------------------------------------------------------------ sub printdex { local($offset,$width,$dex) = @_; $width = 80 if !$width; $offset = 0 if !$offset; local(@words) = split (/\s/,$dex); local($out,$leader,$result); $leader = sprintf("%${offset}s",""); foreach $word (@words) { if (length($out) + length($word) > $width-$offset) { $result .= $leader . $out . "\n"; $out = ""; } $out .= "$word "; } $result .= $leader . $out . "\n" if $out; $result; } #----------------------------------------------------------------------------- # Extract first line of output from oracle given SQL query #------------------------------------------------------------------------------ sub sqlx { local($sqlstmt,$passwd,$db) = @_; local($sqlx_lda,$nologon); ($user=$passwd)=~s/\/.*$//; $passwd=~s/^.*\///; $nologon = sprintf("%s",$user) =~ /^0x/ || 0; if ( $nologon ) { $sqlx_lda=$user; } else { $sqlx_lda = &ora_login($db,$user,$passwd) || die $ora_errstr; } $csr = &ora_open($sqlx_lda,$sqlstmt) || die $ora_errstr; @result = &ora_fetch($csr); &ora_close($csr); &ora_logoff($sqlx_lda) if !$nologon; @result; } $TABLE=FOP; sub openf { #------------------------------------------------------------------------------ # Subroutine to open a file in "r" or "w" mode and return error # message and status if unable to open file. # Synopsis: openf(,filehandle,name); #------------------------------------------------------------------------------ local($type,$handle,$name,$filter) = @_; if ($type eq "w") { open($handle,"> $name"); stat($name); print STDERR "$NAME: file not writeable: $name\n" if !-w_; -w_; } elsif ($type eq "r") { stat($name); print STDERR "$NAME: file not found: $name\n" if !-e_; -e_ && print STDERR "$NAME: file not readable: $name\n" if !-r_; -r_ && open($handle,"< $name"); -r_; } elsif ($type eq "p") { open($handle,"| $filter $name"); } else { die "$NAME: sub openf arg 1 must be r, w, or p.'\n"; 1; } } ($NAME=$0) =~ s/^.*\///; $Pager=$ENV{'PAGER'}; if ( length($Pager) == 0 ) { $Pager="more"; } $Printcmd=$ENV{'PRINTCMD'}; if ( length($Printcmd) == 0 ) { $Printcmd="/home/jstander/bin/printlp"; } #------------------------------------------------------------------------------ # Process command line arguments #------------------------------------------------------------------------------ &Getopts('LUuzdmlshHnpbqxSOF:D:f:o:w:C:') || exit ; if ( $opt_z || $#ARGV < 0 ) { # if no arguments, send usage $Synopsis =~ s/^[ \t]*//; print STDERR "Usage:\t$Synopsis"; print STDERR $Options if $opt_h; exit; } open(UNAME,"uname -snm |"); $uname = ; chop($uname); close(UNAME); $HOME = $ENV{'HOME'} ? $ENV{'HOME'} : (getpwuid($<))[7]; $USERNAME = $ENV{'USERNAME'} ? $ENV{'USERNAME'} : (getpwuid($<))[6]; $USER = $ENV{'USER'} ? $ENV{'USER'} : (getpwuid($<))[1]; $USERNAME_p="($USERNAME)"; if ( $opt_b ) { $ENV{'COLUMNS'}="132"; $out="| $Pager"; } $header = $opt_H; $xbox = $opt_x; $printout = $opt_p; $redirect = $opt_O; $dumpsql = $opt_S; $dfmt = $opt_D || "DD-MON-YYYY HH24:MI"; $fetchlimit = $opt_f; $where = $opt_w; $quiet = $opt_q; $opt_u = 1 if $opt_U; $opt_u = 1 if $opt_L; $sep = $opt_F ? $opt_F : ","; $quote = $opt_n ? "" : "\""; if ( $opt_u ) { $opt_d = 1; $redirect = 1; $quiet = 1; } if ( !$opt_d && !$opt_m && !$opt_l && !$opt_s ) { $opt_d=1; $opt_m=1; $opt_l=1; $opt_s=1; } #------------------------------------------------------------------------------ # Loop over command arguments: they are either tables or SQL statement files #------------------------------------------------------------------------------ while (!($#ARGV<0)) { ($table=$ARGV[0]) =~ tr/A-Z/a-z/; $table =~ s/^.*\.//; # this strips the tablespace name off, should be changed ($TABLE=$table) =~ tr/a-z/A-Z/; shift; if ( ($readsql=$table =~ s/^@//) ) { $file=$table; &openf("r",FILEIN,$file) || exit; } if ( $redirect ) { $out=""; $outname=""; $opt_o=0; } else { $outname=$opt_o || $table; } $lda = &ora_login("","","") || die $ora_errstr; if ( !$dumpsql && !$quiet && !$readsql ) { print STDERR "\n$NAME: extracting data from table: $table\n"; } if ( $readsql ) { #--------------------------------------------------------------------------- # This processes a user-provide SQL statement and extracts column names found # between the SELECT .. FROM keywords. # The SQL statement is preserved intact in the array @qh. # Note: This assumes the user provides a fairly vanilla SQL statement for # an unload operation, i.e. no column aliases and subquerys, but data # conversion (e.g. to_char(), decode(), etc., are ok.) #--------------------------------------------------------------------------- $qh=""; while () { # strip out comments $_ =~ s/;$//; $qh .= $_; $_ =~ tr/a-z/A-Z/; $_ =~ s/^[ \t]*//; $_ =~ s/[ \t]*$//; $_ =~ s/--.*$//; if ( $ok && $_ =~ /^FROM[ \t]*/ ) { $ok=0; $from++; s/FROM[ \t]*// } if ( $_ =~ s/^SELECT// ) { $ok=1; } if ( $from == 1 ) { $from++; if ( $_ =~ /^[a-zA-Z]/ ) { $_ =~ s/[ \t\n]//g; $TABLE = $_; $_ =~ tr/A-Z/a-z/; $table = $_ ; $outname = $table if !$outname; $from=0; next; } } $ok || next; # This mess extracts the column name, The "^" char. is a placeholder. $_ =~ s/\^//g; $_ =~ s/[ \t]*[^(,]*\(/^/g; $_ =~ s/,[^^]*\)//g; $_ =~ s/\),/,/g; $_ =~ s/\^//g; $_ =~ s/[ \t\n]//g; @_ = split(/,/,$_); for ( $i=0; $i<=$#_; $i++ ) { $column[$ncols++] = @_[$i]; } } if ( $opt_l ) { $in_expr="AND column_name IN ("; for ( $i=0; $i<=$#column; $i++ ) { $col = @column[$i]; $in_expr .= "\n\t\t'$col',"; } } $in_expr =~ s/.$/\n\t)/; print STDERR "\n$NAME: extracting data from table: $table\n" if !$quiet && !$dumpsql; } else { #------------------------------------------------------------------------------ # Extract column names from the table #------------------------------------------------------------------------------ $qh=< 0) { $qh .= "$item '$dfmt'" } $qh .= $item . " )"; } if ( $i<$ncols-1 ) { $qh .= ",\n"; } } $qh .= "\nFROM\t$TABLE"; if ( $where ) { $qh .= "\nWHERE $where"; } } #------------------------------------------------------------------------------ # if -S option, print SQL stmt and exit #------------------------------------------------------------------------------ if ( $dumpsql ) { print STDOUT "$qh\n"; exit; } if ( !$dumpsql ) { if ( $opt_C ) { if ( $opt_C eq "z" ) { if ( $quiet ) { $v = "-qq"; } else { print STDERR "$NAME: output will be compressed using zip." . " Restore with -p pipe option.\n"; } $filter = "zip $v $outname.zip -"; } else { if ( !$quiet ) { $v = "-v"; print STDERR "$NAME: output will be " . "compressed using compress\n"; } $filter = "compress $v"; } if ( !$quiet ) { } } } #------------------------------------------------------------------------------ # get storage data for this table #------------------------------------------------------------------------------ if ( $opt_d || $opt_s ) { ($tablespace_name,$cluster_name,$initial_extent,$next_extent,$pct_increase) = &sqlx(< 0 ) { print OUT_CTL ",\n"; } if ( $data_type eq "DATE" ) { print OUT_CTL " $column_name DATE '$dfmt'"; } else { print OUT_CTL " $column_name"; } } &ora_close($csr); print OUT_CTL "\n)\n"; close(OUT_CTL); $outname && !$quiet && print STDERR "$NAME: $outname.ctl written$outtype\n"; } #------------------------------------------------------------------------------ # Create the .sql file to (re)create the table #------------------------------------------------------------------------------ if ( $opt_s ) { $outname=$table if !$outname; print OUT_SQL <.. .ll +8 .ad .SH DESCRIPTION .I Unloadr dumps an Oracle table as an ASCII flat file in "merge" format: each field is comma-delimited and enclosed by quotes. If none of the .B -dmls options are set, assume all of these options are set. .SH OPTIONS .TP .B -d Create a file describing the structure of .IR table_name, including any stored table and column comments. Output is to .IB table_name .dex .PD .TP .B -m Create ASCII flat file containing all columns in .IB table_name. Output is to .IB table_name .mrg . .PD .TP .B -l Create an Oracle loader control file to reload the merge file. Output is to .IB table_name .ctl . .PD .TP .BI -s Create a file containing .B SQL statements to (re)create the table for the extracted data. Output is to .IB table_name .sql . .PD .TP .B -n Suppress enclosure of each field by quotation marks. (Normally .I unloadr will surround each field in the merge file with double quotes). .PD .TP .B -H First line of the output file holds field names, comma-separated. .PD .TP .B -h Print a brief help message. .PD .TP .B -p Send output to $PRINTCMD, your favorite spooler ("/home/jstander/bin/printw" if not defined). .PD .TP .B -b Browse mode, send output to $PAGER (default="more"). .PD .TP .BI -F c Set column separator in merge file to .IR c . Default separator is a comma. .PD .TP .BI -D dfmt set date format (default=DD-MON-YYYY HH:24MI). .PD .TP .BI -f fetchlimit fetch only records from the database. .PD .TP .BI -o outfile output is redirected to outfile if present. .PD .TP .B -O Output is redirected to stdout Default is to direct output to .mrg. .PD .TP .B -q Quiet mode. Don't report actions to STDERR. .PD .TP .B -S Dump SQL statement used for merge extraction. No unloading is done. .PD .TP .B -Cc Compress output files using .I compress utility (if present). .PD .TP .B -Cz Compress output files using .I zip utility (if present). .PD .TP .BI -w whereclause A SQL expression to restrict SELECTed records from table. Note only the expression should be passed, not "WHERE =", see example below. .PD .TP .I tablename Name of the table to be unloaded. The column definitions are extracted and a generic SQL statement is automatically constructed to SELECT all columns for unloading, subject only to the restrictions of the .B -w option. The user may supply their own SQL statement to select columns for unloading by writing the SQL statement into a file and giving the file name, preceeded by an "\fB@\fR" character, in place of the .I tablename argument. .PD .SH NOTE Any fieldname ending in "COMMENT(S)" is right-trimmed before unloading. Any embedded newlines in the COMMENT(S) field are converted to the meta-char "\fB\\n\fR" .SH EXAMPLE Unload all data for cruise number RTMP92-02 from a table called RTMP_OBS_VES_ENTRY. Output will be to the file RTMP9202.ves. .sp 1 .in+2 .na .B unloadr -w "cruise_num='RTMP92-02'" -o RTMP9202.ves rtmp_obs_ves_entry .in-2 .SH SEE ALSO unloader(1) .SH AUTHOR Jeff.Stander@ml.csiro.au .br