From e5f562c4483b763dc1bb9df9506278f00b1fbcd7 Mon Sep 17 00:00:00 2001
From: Glenn Proctor <gp1@sanger.ac.uk>
Date: Wed, 9 Aug 2006 12:10:49 +0000
Subject: [PATCH] Slight change in procedure: a 'master' database must now be
 specified. The external_dbs.txt file is loaded into this, and this in turn is
 compared (id and name) with the contents of the external_db table in each of
 the databases to be updated.

If the database to be updated has any extra rows, a warning is given and the database is skipped.

This should help avoid unilateral changes to external_db which haven't been commited to the external_dbs.txt file in CVS being silently overwritten and causing problems down the line.
---
 .../external_db/update_external_dbs.pl        | 107 ++++++++++++++----
 1 file changed, 88 insertions(+), 19 deletions(-)

diff --git a/misc-scripts/external_db/update_external_dbs.pl b/misc-scripts/external_db/update_external_dbs.pl
index 96da96b55c..9793c32dea 100644
--- a/misc-scripts/external_db/update_external_dbs.pl
+++ b/misc-scripts/external_db/update_external_dbs.pl
@@ -9,22 +9,27 @@ use Getopt::Long;
 use DBI;
 use IO::File;
 
-my ( $host, $user, $pass, $port,@dbnames, $file, $release_num);
-
-GetOptions( "host=s", \$host,
-	    "user=s", \$user,
-	    "pass=s", \$pass,
-	    "port=i", \$port,
-	    "file=s", \$file,
-            "dbnames=s@", \@dbnames,
-	    "release_num=i", \$release_num
+my ( $host, $user, $pass, $port,@dbnames, $file, $release_num, $master);
+
+GetOptions( "host=s",        \$host,
+	    "user=s",        \$user,
+	    "pass=s",        \$pass,
+	    "port=i",        \$port,
+	    "file=s",        \$file,
+            "dbnames=s@",    \@dbnames,
+	    "release_num=i", \$release_num,
+	    "master=s",      \$master
 	  );
 
 #both host and file are required
 usage() if(!$host || !$file);
+
 #release num XOR dbname are required
 usage() if(($release_num && @dbnames) || (!$release_num && !@dbnames));
 
+# master database is required
+usage() if (!$master);
+
 $port ||= 3306;
 
 my $dsn = "DBI:mysql:host=$host;port=$port";
@@ -33,7 +38,7 @@ my $db = DBI->connect( $dsn, $user, $pass, {RaiseError => 1} );
 
 if($release_num) {
   @dbnames = map {$_->[0] } @{ $db->selectall_arrayref( "show databases" ) };
-  
+
   #
   # filter out all non-core databases
   #
@@ -44,13 +49,13 @@ if($release_num) {
 #
 # make sure the user wishes to continue
 #
-print STDERR "The following databases will be external_db updated:\n  ";
+print STDERR "The following databases will have their external_db tables updated if necessary:\n  ";
 print join("\n  ", @dbnames);
-print "\ncontinue with update (yes/no)>  ";
+print "\nContinue with update (yes/no)>  ";
 
 my $input = lc(<STDIN>);
 chomp($input);
-if($input ne 'yes') {
+if ($input ne 'yes') {
   print "external_db conversion aborted\n";
   exit();
 }
@@ -59,10 +64,10 @@ if($input ne 'yes') {
 # read all of the new external_db entries from the file
 #
 my $fh = IO::File->new();
-$fh->open($file) or die("could not open input file $file");
+$fh->open($file) or die("Could not open input file $file");
 my @rows;
 my $row;
-while($row = <$fh>) {
+while ($row = <$fh>) {
   chomp($row);
   my @a = split(/\t/, $row);
   push @rows, {'external_db_id'         => $a[0],
@@ -76,8 +81,38 @@ while($row = <$fh>) {
 }
 $fh->close();
 
+# Load into master database
+load_database($db, $master, @rows);
+
+# Check each other database in turn
+# Load if no extra rows in db that aren't in master
+# Warn and skip if there are
+
 foreach my $dbname (@dbnames) {
-  print STDERR "updating $dbname\n";
+
+  print STDERR "Looking at $dbname ... \n";
+
+  if (compare_external_db($db, $master, $dbname)) {
+
+    print STDERR "$dbname has no additional rows. Overwriting external_db table from $file\n";
+    load_database($db, $dbname, @rows);
+
+  } else {
+
+    print STDERR "$dbname has extra rows that are not in $file, skipping\n";
+
+  }
+
+}
+
+print STDERR "Updates complete\n";
+
+
+
+sub load_database {
+
+  my ($db, $dbname, @rows) = @_;
+
   $db->do("use $dbname");
   my $sth = $db->prepare('DELETE FROM external_db');
   $sth->execute();
@@ -101,10 +136,40 @@ foreach my $dbname (@dbnames) {
   }
 
   $sth->finish();
+
 }
 
-print STDERR "updates complete\n";
 
+# return true if the tables are the same, undef if not
+sub compare_external_db {
+
+  my ($db, $master, $dbname) = @_;
+
+  my $same = 1;
+
+  # check each row in $dbname against each row in $master
+  # only compare ID and name since we're only aiming to catch extra rows in $dbname
+  $db->do("use $dbname");
+
+  my $sth = $db->prepare(qq {SELECT d.external_db_id, d.db_name
+			     FROM $dbname.external_db d
+			     LEFT JOIN $master.external_db m
+			     ON (d.external_db_id=m.external_db_id AND d.db_name=m.db_name)
+			     WHERE m.external_db_id IS NULL OR m.db_name IS NULL });
+  $sth->execute();
+
+  while (my ($id, $external_db_name) = $sth->fetchrow_array) {
+
+    print "$dbname has external_db entry for $external_db_name (ID $id) which is not present in $master\n";
+    $same = undef;
+
+  }
+
+  $sth->finish();
+
+  return $same;
+
+}
 
 sub usage {
   print STDERR <<EOF
@@ -114,6 +179,7 @@ sub usage {
                     -user username 
                     -pass password 
                     -port port_of_server optional
+                    -master the name of the master database to load the file into
                     -release the release of the database to update used to 
                              match database names.  e.g. 13
                     -file the path of the file containing the insert statements
@@ -127,12 +193,15 @@ sub usage {
 
  E.g.:
 
-  #update 2 databases
+  # update 2 databases
   perl update_external_dbs.pl -host ecs1c -file external_dbs.txt -user ensadmin -pass secret -dbnames homo_sapiens_core_14_33 -dbnames mus_musculus_core_14_30
 
-  #update all core databases for release 14
+  # update all core databases for release 14
   perl update_external_dbs.pl -host ecs2d -file external_dbs.txt -user ensadmin -pass secret -release 14
 
+  If the databases to be updated contain rows that are not in the file, a warning will
+  be given and the database in question skipped.
+
 EOF
 ;
   exit;
-- 
GitLab