schema_patcher.pl 24.1 KB
Newer Older
1
#!/usr/bin/env perl
2
# Copyright [1999-2015] Wellcome Trust Sanger Institute and the EMBL-European Bioinformatics Institute
Tiago Grego's avatar
Tiago Grego committed
3
# Copyright [2016-2019] EMBL-European Bioinformatics Institute
4 5 6 7 8 9 10 11 12 13 14 15 16
# 
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# 
#      http://www.apache.org/licenses/LICENSE-2.0
# 
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

17 18 19 20 21

use strict;
use warnings;

use DBI qw( :sql_types );
22
use File::Spec::Functions qw/:ALL/;
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
use Getopt::Long qw( :config no_ignore_case auto_version );
use IO::Dir;

my $rcsid = '$Revision$';
our ($VERSION) = $rcsid =~ /(\d+\.\d+)/;

sub usage {
  my $indent = ' ' x length($0);

  print <<USAGE_END;
Usage:

  $0 --host=dbhost [ --port=dbport ] \\
  $indent --user=dbuser [ --pass=dbpass ] \\
  $indent --type=schema-type | --database=dbname \\
38
  $indent [ --release=new-release ] [ --from=old-release ] \\
39
  $indent [ --species=dbspecies ] \\
40
  $indent [ --gitdir=/some/path ] \\
41
  $indent [ --dryrun ] \\
42
  $indent [ --interactive 0|1 ]\\
43
  $indent [ --verbose ] [ --quiet ] \\
44
  $indent [ --mysql=optional_path ]  \\
45 46
  $indent [ --fix ] \\
  $indent [ --fixlast ]
47 48 49 50 51 52 53 54 55 56 57

  $0 --help | --about

  $0 --version

  --host / -h\tdatabase host name (required)
  --port / -P\tdatabase port (optional, default=3306)
  --user / -u\tdatabase user (required)
  --pass / -p\tdatabase user password (optional, no default)

  --type / -t   restrict to database schema type
58
                (i.e. core, compara, funcgen, variation, production or ontology)
59 60 61 62 63
                (required if --database is not specified)

  --database / -d   full name of database, or database name pattern
                    (required if --type is not specified)

64 65
  --release / -r    release number (optional, default is the latest
                    release that we can find patches for)
66 67 68 69 70 71

  --from / -f       only consider databases from this release
                    (optional, no default)

  --species / -s    restrict to species (optional, no default)

72
  --gitdir          the directory where the relevant Ensembl Git repositories
73
                    have been checked out (optional, default=misc-scripts/../..)
74 75 76 77 78 79 80 81

  --dryrun / -n     do not actually modify databases
                    (optional, default=not set)

  --verbose / -v    display extra information

  --quiet / -q      do not display warnings

82 83
  --fix             also go through all old patches to find any missing
                    patch (patching starts at release equal to the
84
                    oldest patch in the database) >>USE WITH CAUTION<<
85
                    
86 87 88 89
  --oldest          used in conjunction with --fix, this option allows control
                    over how many releases are included in the fix. This option
                    exists for users who have incomplete meta entries and
                    wish to bring their database automatically up to date.
90
                    
91 92
  --fixlast         an extension of B<--oldest> and B<--fix>. This combines
                    to patch the current and last release only, giving an easy
93 94
                    way to patch a database post-handover without worrying 
                    about ancient patches.
95 96 97
  
  --mysql           specify the location of the mysql binary if it is not on
                    \$PATH. Otherwise we default this to mysql
98 99 100
  
  --nointeractive   specify if you want an non-interactive patching environment
                    (default false). >>USE WITH CAUTION<<
101

102 103 104 105 106 107 108 109 110 111 112 113
  --help        display this text
  --about       display further information
  --version     display version and quit

USAGE_END
} ## end sub usage

sub about {
  print <<ABOUT_END;

    This script patches one or several Ensembl databases from older
    releases to the release specified by the user on the command line
114 115 116 117 118 119 120
    using the --release=NN command line switch, or to the latest release
    for which the script is able to find a patch if the --release=NN
    switch is not used.  To only patch databases from a particular
    Ensembl release, the user may use the --from=NN command line
    switch.  In this case, the script will use the value from the
    'schema_version' meta key or, failing that, from the database name,
    to determine what databases should be or shouldn't be patched.
121 122

    The script is able to patch databases that have Ensembl Core
123 124
    schemas, Ensembl Compara schemas, Ensembl Regulation schemas, 
    Ensembl Variation schemas, Ensembl Production and Ontology schemas
125 126 127 128
    provided that the appropriate Git repositories have been checked out
    and are available to this script.  The Git root directory where
    all Ensembl Git repositories are located may be specified using the
    --gitdir=/some/path command line switch if the script is unable to
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
    determine it by itself.

    The user has to specify either a particular database to patch (using
    --database=XXX) or by giving a schema type (using --type=XXX).  In
    the case where a single database is provided, the script will try to
    figure out what schema type the database has from the value of the
    'schema_type' meta key or, failing that, from the database name.

    If the user gives only a schema type, the script will look for
    databases with that schema type, again using the value of the
    'schema_type' meta key or, failing that, using the database names.

    The --database=XXX command line switch may also be used to specify a
    pattern to match database names with.

    To further restrict the set of databases that will be patched,
    the --species=XXX command line switch may be used.  The value
    will be used to match against the various values of the meta key
    'species.alias' or, failing that, against the database name.

149 150 151 152 153 154 155 156 157
    When the --fix command line switch is used, the script will also
    make it possible to apply older patches that might have been
    skipped.  For example: A database at schema version 65, with patches
    since release 40, will not only be patched to the appropriate
    release (specified with --release=NN), but all patches since release
    40 (inclusively) will be tested.  If the patch identifier for an old
    patch is missing from the meta table of the database, that patch
    will be applied.

158 159 160 161 162 163 164
    Examples

      The release coordinator patches all Ensembl Core-like databases
      from release 65 to release 66:

        $0 -h host -u user -p password \\
          -t core -f 65 -r 66
165 166 167 168 169 170
          
      A genebuilder wishes to patch the same set as specified above but
      without being prompted to apply patches
      
        $0 -h host -u user -p password \\
          -t core -f 65 -r 66 --nointeractive
171 172 173 174 175 176 177 178 179

      A genebuilder patches one of her databases to release 66, and
      wants to look at what the script proposes to do before actually
      running it for real:

        $0 -h host -u user -p password \\
          -d my_database_66 -r 66 --dryrun

      The release coordinator patches all mouse Core-like databases to
180 181
      the latest release.  She has checked out the 'ensembl' Git repo
      in her ~/src directory:
182 183

        $0 -h host -u user -p password \\
184
          -t core -s mouse --gitdir=~/src
185 186

      A genebuilder (username 'my') patches all her human databases to
187
      the latest release.
188 189

        $0 -h host -u user -p password \\
190
          -s homo_sapiens -d 'my_%'
191

192
      A genebuilder makes sure that all patches up to and including
193 194 195
      those for release 66 are included in her database, without
      actually applying any patch (any missing patches needs to be
      manually checked!):
196 197

        $0 -h host -u user -p password \\
198
          -r 66 -d my_database --fix --dryrun
199
          
200
      The genebuilder above has an evil twin who has mislaid their meta tables. 
201 202 203 204 205 206
      --fix threatens to apply ancient patches but they know their database 
      is correct until halfway through release 64. They wish to apply any
      missing patches between release 64 and 66.
      
        $0 -h host -u user -p password -r 66 \\
        -d my_database --fix --oldest 64
207 208 209 210 211 212 213 214
      
      The genebuilder above also has a doppleganger who decided they
      wanted to patch for the last and current release of Ensembl alone. This
      is useful for applying late patches. In this situation we will apply  
      patches for the current release (67) and the previous release (66).

        $0 -h host -u user -p password -r 67 \\
        -d my_database --fixlast 
215

216 217 218 219 220 221 222 223
ABOUT_END
} ## end sub about

my ( $opt_host, $opt_port ) = ( undef, '3306' );
my ( $opt_user, $opt_pass ) = ( undef, undef );
my ( $opt_species, $opt_type, $opt_release ) = ( undef, undef, undef );
my $opt_database;

224
my $opt_gitdir;
225 226 227

my $opt_dryrun;
my $opt_from;
228
my $opt_fix;
229
my $opt_oldest;
230
my $opt_fixlast;
231
my $opt_mysql = 'mysql';
232
my $opt_interactive = 1;
233 234 235 236 237 238 239 240 241 242 243 244

my ( $opt_verbose, $opt_quiet );

if ( !GetOptions( 'host|h=s'     => \$opt_host,
                  'port|P=i'     => \$opt_port,
                  'user|u=s'     => \$opt_user,
                  'pass|p=s'     => \$opt_pass,
                  'species|s=s'  => \$opt_species,
                  'type|t=s'     => \$opt_type,
                  'from|f=i'     => \$opt_from,
                  'release|r=i'  => \$opt_release,
                  'database|d=s' => \$opt_database,
245
                  'gitdir=s'     => \$opt_gitdir,
246
                  'dryrun|n!'    => \$opt_dryrun,
247
                  'fix!'         => \$opt_fix,
248
                  'fixlast!'     => \$opt_fixlast,
249
                  'oldest=i'     => \$opt_oldest,
250 251
                  'mysql=s'      => \$opt_mysql,
                  'interactive|i!' => \$opt_interactive,
252 253 254 255 256 257 258 259 260 261 262 263 264 265
                  'verbose|v!'   => \$opt_verbose,
                  'quiet|q!'     => \$opt_quiet,
                  'help!'        => sub { usage(); exit(0); },
                  'about!'       => sub { about(); exit(0); } ) ||
     !defined($opt_host) ||
     !defined($opt_user) ||
     ( !defined($opt_database) && !defined($opt_type) ) )
{
  usage();
  exit(1);
}

if ( defined($opt_type) &&
     $opt_type ne 'core' &&
266
     $opt_type ne 'compara' && 
267
     $opt_type ne 'funcgen' &&
268 269 270
     $opt_type ne 'variation' &&
     $opt_type ne 'production' &&
     $opt_type ne 'ontology' )
271 272 273 274
{
  die( sprintf( "Unknown schema type: %s\n", $opt_type ) );
}

275 276 277
# turn on autoflush
$| = 1;

278
my $latest_release;
279 280 281 282
my %patches;

# Get available patches.

Andy Yates's avatar
Andy Yates committed
283 284
foreach my $thing ( [ 'ensembl',               'core',        'table.sql'   ],
                    [ 'ensembl-compara',       'compara',     'table.sql'   ], 
ilavidas's avatar
ilavidas committed
285
                    [ 'ensembl-funcgen',       'funcgen',     'table.sql'   ],
Andy Yates's avatar
Andy Yates committed
286
                    [ 'ensembl-variation',     'variation',   'table.sql'   ],
287
                    [ 'ensembl-production',    'production',  'table.sql'  ],
288
                    [ 'ols-ensembl-loader',    'ontology',    'tables.sql'  ] )
289
{
290
  my ($git_repo, $schema_type, $schema_file) = @{$thing};
291 292 293

  if ( defined($opt_type) && $schema_type ne $opt_type ) { next }

294
  my $sql_dir = _sql_dir($git_repo, $schema_type, $schema_file);
295 296
  if(! defined $sql_dir) {
    if ( !$opt_quiet ) {
297
      warn(sprintf("No SQL directory found for Git repo %s, %s schema type\n", $git_repo, $schema_type));
298 299 300
    }
    next;
  }
301 302 303 304 305 306 307 308 309 310
  my $dh = IO::Dir->new($sql_dir);

  if ( !defined($dh) ) {
    if ( !$opt_quiet ) {
      warn(sprintf( "Unable to find SQL directory '%s'\n", $sql_dir ) );
    }
    next;
  }

  while ( my $file_name = $dh->read() ) {
311
    if ( $file_name =~ /^patch_\d+_(\d+)_?[a-z]+?\.sql$/ ) {
312 313
      my $patch_release = $1;

314 315 316 317 318 319
      if ( !defined($latest_release) ||
           $latest_release < $patch_release )
      {
        $latest_release = $patch_release;
      }

320 321
      if ($opt_verbose) {
        printf( "Found %s patch file '%s' for release %d\n",
Andy Yates's avatar
Andy Yates committed
322
                $schema_type, $file_name, $patch_release ) if ! $opt_quiet;
323 324 325 326 327 328 329 330 331 332 333
      }

      my $full_file_name = catfile( $sql_dir, $file_name );

      push( @{ $patches{$schema_type}{$patch_release} },
            { 'patch' => $file_name, 'path' => $full_file_name } );
    }
  }

} ## end foreach my $thing ( [ 'ensembl'...])

334

335
if ( defined($opt_release) && $opt_release > $latest_release ) {
336 337 338 339 340
  die( sprintf( "Release %d is too new, " .
                  "last release with patches is release %d\n",
                $opt_release, $latest_release ) );
}

341 342 343 344 345 346 347 348
if ( !defined($opt_release) ) {
  if ($opt_verbose) {
    printf( "Latest release with patches is release %d\n",
            $latest_release );
  }
  $opt_release = $latest_release;
}

349 350 351 352 353
my $dsn = sprintf( "DBI:mysql:host=%s;port=%d", $opt_host, $opt_port );

my $dbh = DBI->connect( $dsn, $opt_user, $opt_pass,
                        { 'RaiseError' => 0, 'PrintError' => 0 } );

354 355 356 357 358 359
if(! $dbh) {
  my $pass = ($opt_pass) ? 'with a' : 'with no';
  warn(sprintf(q{Cannot connect to DSN '%s' with user %s %s password. Check your settings}, $dsn, $opt_user, $pass));
  exit 1;  
}

360 361 362 363
# Loop through the databases on the server, patch the ones we want to
# patch and filter out the ones that we don't want to patch.

my $sth;
364
my $found_databases = 0;
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379

if ( defined($opt_database) ) {
  $sth = $dbh->prepare("SHOW DATABASES LIKE ?");
  $sth->bind_param( 1, $opt_database, SQL_VARCHAR );
}
else { $sth = $dbh->prepare("SHOW DATABASES") }

$sth->execute();

my $database;
$sth->bind_col( 1, \$database );

DATABASE:
while ( $sth->fetch() ) {

380 381
  if ( $database =~ /^(?:information_schema|mysql)$/ ) { next }
  
382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427
  # Figure out schema version, schema type, and species name from the
  # database by querying its meta table.

  my $sth2 = $dbh->prepare(
            sprintf(
              "SELECT meta_key, meta_value FROM %s WHERE meta_key IN " .
                "('schema_version', 'schema_type', " .
                "'species.alias', 'species.common_name', 'patch')",
              $dbh->quote_identifier( undef, $database, 'meta' ) ) );

  $sth2->execute();

  my ( $key, $value );
  $sth2->bind_columns( \( $key, $value ) );

  my ( $schema_version_ok, $schema_type_ok, $species_ok );
  my ( $schema_version,    $schema_type,    $species );
  my %dbpatches;

  while ( $sth2->fetch() ) {
    if ( $key eq 'schema_version' ) {
      $schema_version = $value;
      if ( defined($opt_from) ) {
        if   ( $schema_version eq $opt_from ) { $schema_version_ok = 1 }
        else                                  { $schema_version_ok = 0 }
      }
      else { $schema_version_ok = 1 }
    }
    elsif ( $key eq 'schema_type' ) {
      $schema_type = $value;
      if ( defined($opt_type) ) {
        if   ( $schema_type eq $opt_type ) { $schema_type_ok = 1 }
        else                               { $schema_type_ok = 0 }
      }
      else { $schema_type_ok = 1 }
    }
    elsif ( $key eq 'species.alias' ) {
      if ( defined($opt_species) ) {
        if ( $value eq $opt_species ) { $species_ok = 1 }
      }
      else { $species_ok = 1 }
    }
    elsif ( $key eq 'species.common_name' ) {
      $species = $value;
    }
    elsif ( $key eq 'patch' ) {
428 429 430 431
      if(index($value, "\n") > -1) {
        warn "The patch value '$value' in database '$database' has line-breaks. Remove them to silence this message";
        $value =~ s/\n/ /g;
      } 
432
      if($value =~ /^(patch_\d+_(\d+)_?[a-z]+?\.sql)\|(.*)$/) {
433 434 435 436 437 438 439 440
        my $patch_ident   = $1;
        my $patch_release = $2;
        my $patch_info    = $3;
        $dbpatches{$patch_release}{$patch_ident} = $patch_info;
      }
      else {
        warn "The patch value $value from database $database does not conform to the pattern of 'patch_from_to_tag|description'. Please fix";
      }
441 442 443 444 445 446
    }
  } ## end while ( $sth2->fetch() )

  # If we haven't yet found out the schema version, schema type, or
  # species, look to the database name to provide clues.

447 448 449 450

  if ( ! $schema_version ) {
	#remove defined as version maybe empty string

451 452
    if ( $database =~ /^ensembl.+?(\d+)$/ or # this captures compara|eg|ontology|production naming conventions
	 $database =~ /_(\d+)_\w+$/) {
453

454
      $schema_version = $1;
455

456 457 458 459
      if ( defined($opt_from) ) {
        if   ( $schema_version == $opt_from ) { $schema_version_ok = 1 }
        else                                  { $schema_version_ok = 0 }
      }
460 461
      else {
		$schema_version_ok = 1 }
462
    }
463 464 465
    elsif ( ! $opt_quiet ) {
	  $schema_version_ok = 0;
	  warn( sprintf( "Can not determine schema version from '%s'\n",
466 467 468
                     $database ) );
    }
  }
469

470
  if ( !defined($schema_type) ) {
471
    if ( $database =~ /_(core|funcgen|variation|compara|production|ontology)_/ ) {
472 473 474 475 476 477 478 479 480 481 482 483 484
      $schema_type = $1;
      if ( defined($opt_type) ) {
        if   ( $schema_type eq $opt_type ) { $schema_type_ok = 1 }
        else                               { $schema_type_ok = 0 }
      }
      else { $schema_type_ok = 1 }
    }
    elsif ( !$opt_quiet ) {
      warn( sprintf( "Can not determine schema type from '%s'\n",
                     $database ) );
    }
  }
  if ( !defined($species) ) {
485 486
    if ($database =~ /compara_([a-z][a-z_]+[a-z])?_\d+_\d+/ or # EG case, e.g. ensembl_compara_fungi_18_71
	$database =~ /ensembl[a-z]?_(?:compara|production|ontology)_/ or 
487
        $database =~ /_test_db_([a-z_]+)_([a-z])_/ or
488 489 490 491
	$database =~ /([a-z][a-z_]+[a-z])_(?:core|funcgen|variation)_/) 
      {
	$species = $1;
	$species = 'multi' unless defined $species;
492

493 494 495 496
	if ( defined($opt_species) ) {
	  if ( $species eq $opt_species ) { $species_ok = 1 }
	}
	else { $species_ok = 1 }
497
      }
498
    elsif ( $opt_species && !$opt_quiet ) {
499
      warn( sprintf( "Can not determine species from '%s'\n", $database ) );
500 501
    }
  }
502
  
503
  #Quick check if fix-last is active. If so we will hard-code some values
504
  $opt_fix = 1 if $opt_fixlast and defined $schema_version;
505
  
506 507
  if ( $schema_version_ok &&
       $schema_type_ok &&
508 509
       ( !defined($opt_species) ||
         ( defined($opt_species) && $species_ok ) ) &&
510 511
       ( ( !$opt_fix && $schema_version < $opt_release ) ||
         ( $opt_fix && $schema_version <= $opt_release ) ) )
512
  {
513
    $found_databases = 1;
514 515 516 517
    print( '-' x ( $ENV{COLUMNS} || 80 ), "\n" );
    printf( "Considering '%s' [%s,%s,%d]\n",
            $database, defined($species) ? $species : 'unknown',
            $schema_type, $schema_version );
518 519 520
    if ($opt_fixlast) {
      $opt_oldest = ($schema_version == $latest_release) ? $latest_release : $latest_release - 1;

521 522 523 524
      if ($schema_version < $opt_oldest) {
        printf("Cannot use --fixlast with a schema release too far from the latest release; oldest allowed is $opt_oldest. Skipping $database");
        next;
      }
525 526
      printf("--fixlast is active. Will apply patches for version %d and up (if available)\n", $opt_oldest);
    }
527

528
  }
529 530
  else { 
    if($opt_verbose) {
531
      printf("Skipping database %s (type: %s | version: %d)\n", $database, ($schema_type||'-'), ($schema_version || 0));
532 533
      if( $schema_type_ok && $schema_version_ok && $schema_version == $opt_release) {
        if (defined($opt_type) && $opt_type eq $schema_type) {
534
        
535 536
          my $release_patches = join(q{, }, sort map { $_->{patch} } @{$patches{$schema_type}{$schema_version}});
          my $db_patches = join(q{, }, sort keys %{$dbpatches{$schema_version}});
537
        
538 539 540 541
          if($release_patches ne $db_patches) {
            printf("\t%s patches [%s] are not the same as release %i patches [%s]; rerun with --fix and --dryrun\n", 
              $database, $db_patches, $opt_release, $release_patches);
          }
542
        }
543
      }
544
      if($schema_type_ok && ! exists $patches{$schema_type}) {
545
        printf("\t%s patches could not be found. Check your --gitdir option and try again\n", $schema_type);
546
      }
547 548 549
    }
    next; 
  }
550 551 552

  # Now figure out what patches we need to apply to this database.

553
  my $start_version;
554
  
555
  if ($opt_fix) {
556
    $start_version = $opt_oldest || ( sort { $a <=> $b } keys %dbpatches )[0];
557 558 559 560 561 562
    if ( !defined($start_version) ) {
      warn( sprintf( "No patches in database, " .
                       "beginning fix from release %d\n",
                     $schema_version ) );
      $start_version = $schema_version;
    }
563
    else {
564 565 566
      printf( "Earliest patch in database '%s' is from release %d\n",
              $database, $start_version );
    }
567 568 569
  }
  else { $start_version = $schema_version + 1 }

570
  my @apply_these;
571
  my $schema_version_warning = 0;
572
  
573
  for ( my $r = $start_version; $r <= $opt_release; ++$r ) {
574
    next unless exists $patches{$schema_type}{$r};
575 576 577 578 579 580
    foreach my $entry ( sort { $a->{'patch'} cmp $b->{'patch'} }
                        @{ $patches{$schema_type}{$r} } )
    {
      my $patch = $entry->{'patch'};
      my $path  = $entry->{'path'};

581
      if ( exists( $dbpatches{$r}{$patch} ) ) {
582 583 584 585 586 587
        if ($opt_verbose) {
          printf( "Patch '%s' (%s) already applied\n",
                  $patch, $schema_type );
        }
      }
      else {
588 589
        if ( !$opt_dryrun ) {
          printf( "Will apply patch '%s' (%s)\n", $patch,
590
                  $schema_type );
591 592 593 594 595 596 597
          push( @apply_these, $entry );

          if ( $r < $opt_release && $patch =~ /a\.sql$/ ) {
            # Warn about possible setting schema_version with an 'a'
            # patch.
            $schema_version_warning = 1;
          }
598 599
        }
        else {
600 601
          printf( "Would apply patch '%s' (%s)\n",
                  $patch, $schema_type );
602 603
        }
      }
604
    } ## end foreach my $entry ( sort { ...})
605

606 607
  } ## end for ( my $r = $start_version...)

608
  if ( $opt_dryrun || !@apply_these ) { print("\n"); next }
609

610
  my $apply_patches;
611
  local $| = 1;
612 613 614 615 616 617 618
  if($opt_interactive) {
    print("Proceed with applying these patches? (y/N): ");
    my $yesno = <STDIN>;
    chomp($yesno);
    $apply_patches = (lc($yesno) =~ /^y(?:es)?$/) ? 1 : 0;
  }
  else {
619
    $apply_patches = 1;
620 621
    print "Enterning non-interative mode. Will apply patches\n";
  }
622

623
  if ( $apply_patches ) {
624 625 626 627 628
  PATCH:
    foreach my $entry (@apply_these) {
      my $patch = $entry->{'patch'};
      my $path  = $entry->{'path'};

629 630 631 632 633 634 635 636
      my @cmd_list = (  $opt_mysql,
                        "--host=$opt_host",
                        "--user=$opt_user");
      push(@cmd_list,   "--password=$opt_pass") if $opt_pass;
      push(@cmd_list,   "--port=$opt_port",
                        "--database=$database",
                        "--verbose",
                        "--execute=source $path" );
637 638 639 640 641 642 643 644

      printf( "Executing the following command:\n%s\n",
              join( ' ', @cmd_list ) );

      if ( system(@cmd_list) ) {
        warn( sprintf( "Failed to apply patch '%s' to database '%s'!\n",
                       $patch, $database ) );

645 646 647 648
        if(!$opt_interactive) {
          warn('In non-interative mode; aborting current run');
          exit(1);
        }
649 650 651 652 653
        print("Next patch, next database, or abort? (p/d/A): ");

        my $response = <STDIN>;
        chomp($response);

654 655 656
        if    ( lc($response) =~ /^p(?:atch)?$/ )    { next PATCH }
        elsif ( lc($response) =~ /^d(?:atabase)?$/ ) { next DATABASE }
        else                                         { exit(1) }
657 658
      }
    } ## end foreach my $entry (@apply_these)
659 660 661 662 663 664

    if ( !$opt_quiet && $schema_version_warning ) {
      warn( "Applied one or several 'a' patches, " .
            "schema_version might have been updated\n" );
    }

665 666
  } ## end if ( lc($yesno) =~ /^y(?:es)?$/)

667 668
  print("\n");

669 670
} ## end while ( $sth->fetch() )

671 672 673 674 675 676
if(!$found_databases) {
  printf(('-'x80)."\n");
  printf("No databases considered. Check your --database/--type/--release flags\n");
  printf(('-'x80)."\n");
}

677
$dbh->disconnect();
678 679

sub _sql_dir {
680 681 682 683
  my ($git_repo, $schema_type, $schema_file) = @_;
  my $git_dir;
  if($opt_gitdir) {
    $git_dir = $opt_gitdir;
684 685 686
  }
  else {
    my ($volume, $directories, $file) = splitpath(__FILE__);
687
    $directories = curdir() unless $directories;
688
    $git_dir = catdir($directories, updir(), updir());
689
  }
690
  my $sql_dir = rel2abs(canonpath( catdir( $git_dir, $git_repo, 'sql' ) ));
691 692
  my $schema_location = catfile($sql_dir, $schema_file);
  if(! -f $schema_location) {
693
    if($opt_verbose) {
694 695
      printf("Could not find the schema file '%s' for E! module %s", $schema_location, $git_repo);
      printf("\tTry using --gitdir if your checkouts are in a non-standard location\n") if $opt_gitdir;
696
    }
697 698
    return;
  }
Andy Yates's avatar
Andy Yates committed
699
  printf("Using '%s' as our SQL directory\n", $sql_dir) if ! $opt_quiet;
700 701
  return $sql_dir;
}