#!/usr/bin/env perl # Copyright (c) 2000, 2003, 2006 MySQL AB, 2009 Sun Microsystems, Inc. # Use is subject to license terms. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; version 2 # of the License. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free # Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, # MA 02110-1335 USA # # start initialition # $VER = "1.0"; use Getopt::Long; use Cwd; use DBI; $max_row_length=500000; # Don't create bigger SQL rows that this $opt_lock=1; # lock tables $pwd = cwd(); $pwd = "." if ($pwd eq ''); require "$pwd/server-cfg" || die "Can't read Configuration file: $!\n"; $|=1; $opt_from_server= $opt_to_server= "mysql"; $opt_from_host= $opt_to_host= "localhost"; $opt_from_db= $opt_to_db= "test"; $opt_from_user=$opt_from_password=$opt_to_user=$opt_to_password=""; $opt_help=$opt_verbose=$opt_debug=0; GetOptions("from-server=s","to-server=s","from-host=s","to-host=s","from-db=s", "to-db=s", "help", "verbose","debug") || usage(); usage() if ($opt_help || ($opt_from_server eq $opt_to_server && $opt_from_db eq $opt_to_db && $opt_from_host eq $opt_to_host)); #### #### Usage #### sub usage { print <connect() || die "Can't connect to source server $opt_from_server on host $opt_from_host using db $opt_from_db"; $opt_user=$opt_to_user; $opt_password=$opt_to_password; $to_dbh=$to_server->connect() || die "Can't connect to source server $opt_to_server on host $opt_to_host using db $opt_to_db"; #### #### Copy data #### foreach $table (@ARGV) { print "- querying $table\n" if ($opt_verbose); $sth=$from_dbh->prepare("select * from $table") || die "Can't prepare query to get $table; $DBI::errstr"; $sth->execute || die "Can't execute query to get data from $table; $DBI::errstr"; if (!table_exists($to_server,$to_dbh,$table)) { print "- creating $table\n" if ($opt_verbose); $table_def=get_table_definition($from_server,$from_dbh,$sth); do_many($to_dbh,$to_server->create($table,$table_def,[])); } if ($opt_lock && $to_server->{'lock_tables'}) { print "- locking $table\n" if ($opt_verbose); $to_dbh->do("lock tables $table WRITE"); } $columns=$sth->{NUM_OF_FIELDS}; $columns_to_quote=get_columns_to_quote($sth); $insert_multi_value=$sth->{'insert_multi_value'}; $query="insert into $table values"; $result=""; print "- copying $table\n" if ($opt_verbose); while (($row = $sth->fetchrow_arrayref)) { $tmp="("; for ($i=0 ; $i < $columns ; $i++) { if ($columns_to_quote->[$i]) { $tmp.= $to_dbh->quote($row->[$i]) . ","; } else { $tmp.= $row->[$i] . ","; } } substr($tmp,-1)=")"; # Remove last ',' if ($insert_multi_value) { $to_dbh->do($query . $tmp) || die "Can't insert row: $DBI::errstr"; } elsif (length($result)+length($tmp) >= $max_row_length && $result) { $to_dbh->do($query . $result) || die "Can't insert row: $DBI::errstr"; $result=""; } elsif (length($result)) { $result.= ",$tmp"; } else { $result=$tmp; } } if (length($result)) { $to_dbh->do($query . $result) || die "Can't insert row: $DBI::errstr"; } if ($opt_lock && $to_server->{'lock_tables'}) { $to_dbh->do("unlock tables"); } } sub get_table_definition { my ($server,$dbh,$sth)=@_; my ($i,$names,$types,$scale,$precision,$nullable,@res); $names=$sth->{NAME}; $types=$sth->{TYPE}; $nullable=$sth->{NULLABLE}; if (0) { # The following doesn't yet work $scale=$sth->{SCALE}; $precision=$sth->{PRECISION}; } else { my (@tmp); @tmp= (undef()) x $sth->{NUM_OF_FIELDS}; $precision= $scale= \@tmp; } for ($i = 0; $i < $sth->{NUM_OF_FIELDS} ; $i++) { push(@res,$names->[$i] . " " . odbc_to_sql($server,$types->[$i],$precision->[$i],$scale->[$i]) . ($nullable->[$i] ? "" : " NOT NULL")); } return \@res; } sub odbc_to_sql { my ($server,$type,$precision,$scale)=@_; if ($type == DBI::SQL_CHAR()) { return defined($precision) ? "char($precision)" : "varchar(255)"; } if ($type == DBI::SQL_NUMERIC()) { $precision=15 if (!defined($precision)); $scale=6 if (!defined($scale)); return "numeric($precision,$scale)"; } if ($type == DBI::SQL_DECIMAL()) { $precision=15 if (!defined($precision)); $scale=6 if (!defined($scale)); return "decimal($precision,$scale)"; } if ($type == DBI::SQL_INTEGER()) { return "integer" if (!defined($precision)); return "integer($precision)"; } if ($type == DBI::SQL_SMALLINT()) { return "smallint" if (!defined($precision)); return "smallint($precision)"; } if ($type == DBI::SQL_FLOAT()) { $precision=12 if (!defined($precision)); $scale=2 if (!defined($scale)); return "float($precision,$scale)"; } if ($type == DBI::SQL_REAL()) { $precision=12 if (!defined($precision)); $scale=2 if (!defined($scale)); return "float($precision,$scale)"; } if ($type == DBI::SQL_DOUBLE()) { $precision=22 if (!defined($precision)); $scale=2 if (!defined($scale)); return "double($precision,$scale)"; } if ($type == DBI::SQL_VARCHAR()) { $precision=255 if (!defined($precision)); return "varchar($precision)"; } return "date" if ($type == DBI::SQL_DATE()); return "time" if ($type == DBI::SQL_TIME()); return "timestamp" if ($type == DBI::SQL_TIMESTAMP()); return $server->{'text'} if ($type == DBI::SQL_LONGVARCHAR()); return $server->{'blob'} if ($type == DBI::SQL_LONGVARBINARY()); if ($type == DBI::SQL_BIGINT()) { return "bigint" if (!defined($precision)); return "bigint($precision)"; } if ($type == DBI::SQL_TINYINT()) { return "tinyint" if (!defined($precision)); return "tinyint($precision)"; } die "Can't covert type '$type' to a ODBC type\n"; } # # return an array with 1 for all coumns that we have to quote # sub get_columns_to_quote($sth) { my ($sth)=@_; my ($i,@res,$type,$tmp); @res=(); for ($i = 0; $i < $sth->{NUM_OF_FIELDS} ; $i++) { $type=$sth->{TYPE}->[$i]; $tmp=1; # String by default if ($type == DBI::SQL_NUMERIC() || $type == DBI::SQL_DECIMAL() || $type == DBI::SQL_INTEGER() || $type == DBI::SQL_SMALLINT() || $type == DBI::SQL_SMALLINT() || $type == DBI::SQL_FLOAT() || $type == DBI::SQL_REAL() || $type == DBI::SQL_DOUBLE() || $type == DBI::SQL_BIGINT() || $type == DBI::SQL_TINYINT()) { $tmp=0; } push (@res,$tmp); } return \@res; } # # Check if table exists; Return 1 if table exists # sub table_exists { my ($server,$dbh,$table)=@_; if ($server->{'limits'}->{'group_functions'}) { return !safe_query($dbh,"select count(*) from $table"); } if ($server->{'limits'}->{'limit'}) { return !safe_query($dbh,"select * from $table limit 1"); } die "Don't know how to check if table '$table' exists in destination server\n"; } # # execute query; return 0 if query is ok # sub safe_query { my ($dbh,$query)=@_; my ($sth); print "query: $query\n" if ($opt_debug); if (!($sth= $dbh->prepare($query))) { print "error: $DBI::errstr\n" if ($opt_debug); return 1; } if (!$sth->execute) { print "error: $DBI::errstr\n" if ($opt_debug); return 1 } while ($sth->fetchrow_arrayref) { } $sth->finish; undef($sth); return 0; } # # execute an array of queries # sub do_many { my ($dbh,@statements)=@_; my ($statement,$sth); foreach $statement (@statements) { print "query: $statement\n" if ($opt_debug); if (!($sth=$dbh->do($statement))) { die "Can't execute command '$statement'\nError: $DBI::errstr\n"; } } }