MayaChemTools

   1 #!/usr/bin/perl -w
   2 #
   3 # File: DBSchemaTablesToTextFiles.pl
   4 # Author: Manish Sud <msud@san.rr.com>
   5 #
   6 # Copyright (C) 2024 Manish Sud. All rights reserved.
   7 #
   8 # This file is part of MayaChemTools.
   9 #
  10 # MayaChemTools is free software; you can redistribute it and/or modify it under
  11 # the terms of the GNU Lesser General Public License as published by the Free
  12 # Software Foundation; either version 3 of the License, or (at your option) any
  13 # later version.
  14 #
  15 # MayaChemTools is distributed in the hope that it will be useful, but without
  16 # any warranty; without even the implied warranty of merchantability of fitness
  17 # for a particular purpose.  See the GNU Lesser General Public License for more
  18 # details.
  19 #
  20 # You should have received a copy of the GNU Lesser General Public License
  21 # along with MayaChemTools; if not, see <http://www.gnu.org/licenses/> or
  22 # write to the Free Software Foundation Inc., 59 Temple Place, Suite 330,
  23 # Boston, MA, 02111-1307, USA.
  24 #
  25 
  26 use strict;
  27 use FindBin; use lib "$FindBin::Bin/../lib";
  28 use Getopt::Long;
  29 use File::Basename;
  30 use Text::ParseWords;
  31 use Benchmark;
  32 use FileUtil;
  33 use TextUtil;
  34 use DBUtil;
  35 
  36 my($ScriptName, %Options, $StartTime, $EndTime, $TotalTime);
  37 
  38 # Autoflush STDOUT
  39 $| = 1;
  40 
  41 # Starting message...
  42 $ScriptName = basename($0);
  43 print "\n$ScriptName: Starting...\n\n";
  44 $StartTime = new Benchmark;
  45 
  46 # Get the options and setup script...
  47 SetupScriptUsage();
  48 if ($Options{help} || @ARGV < 1) {
  49   die GetUsageFromPod("$FindBin::Bin/$ScriptName");
  50 }
  51 
  52 my($DBDriver, $DBHost, $DBName, $DBUser, $DBPassword, $DBMode, $ExportDataLabels, $ExportLOBs, $OutDelim, $OutQuote, $ReplaceNullStr);
  53 ProcessOptions();
  54 
  55 # Connect to database...
  56 my($DBHandle);
  57 print "Connecting to $DBDriver:database=$DBName as $DBUser...\n";
  58 $DBHandle = DBConnect($DBDriver, $DBName, $DBHost, $DBUser, $DBPassword);
  59 
  60 # Collect input parameters information...
  61 print "Checking input parameter(s)...\n";
  62 my(@DBSchemaNames, @DBTableNames, @DBSQLStatements, @DBTextFiles, $SingleTextFileName);
  63 RetrieveDBInfo();
  64 
  65 if ($Options{numoutfiles} =~ /^single$/i ) {
  66   GenerateSingleTextFile();
  67 }
  68 else {
  69   GenerateMultipleTextFiles();
  70 }
  71 print "\nDisconnecting from  $DBDriver:database=$DBName...\n";
  72 DBDisconnect($DBHandle);
  73 
  74 print "$ScriptName:Done...\n\n";
  75 
  76 $EndTime = new Benchmark;
  77 $TotalTime = timediff ($EndTime, $StartTime);
  78 print "Total time: ", timestr($TotalTime), "\n";
  79 
  80 ###############################################################################
  81 
  82 # Generate multiple text files...
  83 sub GenerateMultipleTextFiles {
  84   my($Index, $TextFile, $SQL);
  85   if (@DBTextFiles > 1) {
  86     print "Generating text files...\n";
  87   }
  88  TEXTFILE: for $Index (0 .. $#DBTextFiles) {
  89     $TextFile = $DBTextFiles[$Index];
  90     $SQL = $DBSQLStatements[$Index];
  91 
  92     if (@DBTextFiles > 1) {
  93       print "\nGenerating text file $TextFile...\n";
  94     }
  95     else {
  96       print "Generating text file $TextFile...\n";
  97     }
  98 
  99     if (!open TEXTFILE, ">$TextFile") {
 100       warn "Warning: Abandoning $TextFile generation: Couldn't open it: $! \n";
 101       next TEXTFILE;
 102     }
 103     print "Processing SQL statement \"$SQL\"...\n";
 104 
 105     if (DBSQLToTextFile($DBHandle, $SQL, \*TEXTFILE, $OutDelim, $OutQuote, $ExportDataLabels, $ExportLOBs, $ReplaceNullStr)) {
 106       warn "Warning: Abandoning $TextFile generation...\n";
 107       next TEXTFILE;
 108     }
 109     close TEXTFILE;
 110   }
 111 }
 112 
 113 # Generate single text file...
 114 sub GenerateSingleTextFile {
 115   my($Index, $TextFile, $SQL, $SchemaName, $TableName);
 116 
 117   open TEXTFILE, ">$SingleTextFileName" or die "Couldn't open $SingleTextFileName: $! \n";
 118   print "Generating text file $SingleTextFileName...\n";
 119 
 120   SQL: for $Index (0 .. $#DBSQLStatements) {
 121     $SchemaName = $DBSchemaNames[$Index];
 122     $TableName = $DBTableNames[$Index];
 123     $SQL = $DBSQLStatements[$Index];
 124 
 125     $TableName = qq($SchemaName.$TableName);
 126     $TableName = QuoteAWord($TableName, $OutQuote);
 127     print TEXTFILE "\n\n$TableName\n";
 128 
 129     if (DBSQLToTextFile($DBHandle, $SQL, \*TEXTFILE, $OutDelim, $OutQuote, $ExportDataLabels, $ExportLOBs, $ReplaceNullStr)) {
 130       warn "Warning: Abandoning table $TableName ...\n";
 131       next SQL;
 132     }
 133   }
 134   close TEXTFILE;
 135 }
 136 
 137 # Collect input parameters information...
 138 sub RetrieveDBInfo {
 139   my($FileExt, $UserFileName, $FileDBPrefix);
 140 
 141   # Setup out file ext...
 142   $FileExt = ($Options{outdelim} =~ /^tab$/i) ? "tsv" : "csv";
 143   $FileDBPrefix = ($DBMode =~ /^exportdata$/i) ? "Export" : "Describe";
 144 
 145   # Get user specified information...
 146   $UserFileName = "";
 147   if ($Options{root} && (@ARGV == 1)) {
 148     my($RootFileDir, $RootFileName, $RootFileExt) = ParseFileName($Options{root});
 149     if ($RootFileName && $RootFileExt) {
 150       $UserFileName = $RootFileName;
 151     }
 152     else {
 153       $UserFileName = $Options{root};
 154     }
 155   }
 156   $SingleTextFileName = "";
 157   if ($Options{numoutfiles} =~ /^single$/i) {
 158     $SingleTextFileName = $UserFileName ? $UserFileName : ("$FileDBPrefix" . "SchemaTables");
 159     $SingleTextFileName .= ".$FileExt";
 160   }
 161 
 162   # For each input schema name: collect all the table names, set up appropriate
 163   # SQL statements, and output file names...
 164   #
 165   my($SchemaName, $SQL, $FileName, @SchemaTableNames, $TableName);
 166   @DBSchemaNames = ();
 167   @DBTableNames = ();
 168   @DBSQLStatements = ();
 169   @DBTextFiles = ();
 170   @SchemaTableNames = ();
 171   SCHEMANAME: for $SchemaName (@ARGV) {
 172     $SchemaName = ($DBDriver =~ /^(mysql|Oracle)$/i) ? uc($SchemaName) : $SchemaName;
 173     if (!(@SchemaTableNames = DBFetchSchemaTableNames($DBDriver, $DBHandle, $SchemaName))) {
 174       warn "Warning: Ignoring schema $SchemaName...\n";
 175       next SCHEMANAME;
 176     }
 177     # Prepare SQL statement for each table.
 178     for $TableName (@SchemaTableNames) {
 179       push @DBSchemaNames, $SchemaName;
 180       push @DBTableNames, $TableName;
 181       $SQL = ($DBMode =~ /^exportdata$/i) ? DBSetupSelectSQL($DBDriver, $TableName, $SchemaName) : DBSetupDescribeSQL($DBDriver, $TableName, $SchemaName);
 182       push @DBSQLStatements, $SQL;
 183       if ($Options{numoutfiles} =~ /^multiple$/i) {
 184         $FileName = $UserFileName ? ("$UserFileName" . "$TableName") : ("$FileDBPrefix" . "$SchemaName" . "$TableName");
 185         $FileName .= ".$FileExt";
 186         if (!$Options{overwrite}) {
 187           if (-e $FileName) {
 188             die "Error: The file $FileName already exists.\n";
 189           }
 190         }
 191         push @DBTextFiles, $FileName;
 192       }
 193     }
 194   }
 195 }
 196 
 197 # Process option values...
 198 sub ProcessOptions {
 199 
 200   $DBDriver = $Options{dbdriver} ? $Options{dbdriver} : (exists $ENV{DBI_DRIVER} ? $ENV{DBI_DRIVER} : "") ;
 201   if ($DBDriver) {
 202     if ($DBDriver =~ /^oracle$/i) {
 203       $DBDriver = "Oracle";
 204     }
 205     elsif ($DBDriver =~ /^mysql$/i) {
 206       $DBDriver = "mysql";
 207     }
 208     elsif ($DBDriver =~ /^(Pg|Postgres)$/i) {
 209       $DBDriver = "Pg";
 210     }
 211     else {
 212       if ($Options{dbdriver}) {
 213         die "Error: The value specified, $DBDriver, for option \"-d --dbdriver\" is not valid. Allowed values: MySQL, Oracle, Postgres or Pg\n";
 214       }
 215       else {
 216         die "Error: The value specified, $DBDriver, using environment variable DBI_DRIVER not valid. Allowed values: MySQL, Oracle, Postgres or Pg\n";
 217       }
 218     }
 219   }
 220   else {
 221     $DBDriver = "mysql";
 222   }
 223   $DBHost = $Options{dbhost} ? $Options{dbhost} : (exists $ENV{DBI_HOST} ? $ENV{DBI_HOST} : "127.0.0.1");
 224   $DBName = $Options{dbname} ? $Options{dbname} : (exists $ENV{DBI_NAME} ? $ENV{DBI_NAME} : "");
 225   if (!$DBName) {
 226     if ($DBDriver =~ /^mysql$/i) {
 227       $DBName = "mysql";
 228     }
 229   }
 230   $DBUser = $Options{dbusername} ? $Options{dbusername} : (exists $ENV{DBI_USER} ? $ENV{DBI_USER} : "") ;
 231   if (!$DBUser) {
 232     die "Error: No database username specified. Use \"--dbusername\" option or environment variable DBI_USER to enter a valid value.\n";
 233   }
 234   $DBPassword = $Options{dbpassword} ? $Options{dbpassword} : (exists $ENV{DBI_PASS} ? $ENV{DBI_PASS} : "") ;
 235   if (!$DBPassword) {
 236     die "Error: No database password specified. Use \"--dbpassword\" option or environment variable DBI_PASS to enter a valid value.\n";
 237   }
 238   $DBMode = $Options{mode};
 239   $ExportLOBs = ($Options{exportlobs} =~ /^yes$/i) ? 1 : 0;
 240   $ExportDataLabels = ($DBMode =~ /^describetable$/i) ? 1 : (($Options{exportdatalabels} =~ /^yes$/i) ? 1 : 0);
 241 
 242   $OutDelim = ($Options{outdelim} =~ /^tab$/i ) ? "\t" : (($Options{outdelim} =~ /^semicolon$/i) ? "\;" : "\,");
 243   $OutQuote = ($Options{quote} =~ /^yes$/i) ? 1 : 0;
 244 
 245   $ReplaceNullStr = (defined($Options{replacenullstr}) && length($Options{replacenullstr})) ? $Options{replacenullstr} : "";
 246 }
 247 
 248 # Setup script usage  and retrieve command line arguments specified using various options...
 249 sub SetupScriptUsage {
 250 
 251   # Retrieve all the options...
 252   %Options = ();
 253   $Options{mode} = "exportdata";
 254   $Options{exportlobs} = "no";
 255   $Options{exportdatalabels} = "yes";
 256   $Options{numoutfiles} = "single";
 257   $Options{outdelim} = "comma";
 258   $Options{quote} = "yes";
 259 
 260   if (!GetOptions(\%Options, "dbdriver|d=s", "dbhost=s", "dbname=s", "dbpassword=s", "dbusername=s", "exportdatalabels=s", "exportlobs=s", "help|h", "mode|m=s", "numoutfiles|n=s", "outdelim=s", "overwrite|o", "quote|q=s", "root|r=s", "replacenullstr=s", "workingdir|w=s")) {
 261     die "\nTo get a list of valid options and their values, use \"$ScriptName -h\" or\n\"perl -S $ScriptName -h\" command and try again...\n";
 262   }
 263   if ($Options{workingdir}) {
 264     if (! -d $Options{workingdir}) {
 265       die "Error: The value specified, $Options{workingdir}, for option \"-w --workingdir\" is not a directory name.\n";
 266     }
 267     chdir $Options{workingdir} or die "Error: Couldn't chdir $Options{workingdir}: $! \n";
 268   }
 269   if ($Options{exportdatalabels} !~ /^(yes|no)$/i) {
 270     die "Error: The value specified, $Options{exportlobs}, for option \"--exportdatalabels\" is not valid. Allowed values: yes or no\n";
 271   }
 272   if ($Options{exportlobs} !~ /^(yes|no)$/i) {
 273     die "Error: The value specified, $Options{exportlobs}, for option \"--exportlobs\" is not valid. Allowed values: yes or no\n";
 274   }
 275   if ($Options{numoutfiles} !~ /^(single|multiple)$/i) {
 276     die "Error: The value specified, $Options{mode}, for option \"-n --numoutfiles\" is not valid. Allowed values: single or multiple\n";
 277   }
 278   if ($Options{mode} !~ /^(exportdata|describetable)$/i) {
 279     die "Error: The value specified, $Options{mode}, for option \"-m --mode\" is not valid. Allowed values: exportdata or describetable\n";
 280   }
 281   if ($Options{outdelim} !~ /^(comma|semicolon|tab)$/i) {
 282     die "Error: The value specified, $Options{outdelim}, for option \"--outdelim\" is not valid. Allowed values: comma, tab, or semicolon\n";
 283   }
 284   if ($Options{quote} !~ /^(yes|no)$/i) {
 285     die "Error: The value specified, $Options{quote}, for option \"-q --quote\" is not valid. Allowed values: yes or no\n";
 286   }
 287 }
 288