View | Details | Raw Unified | Return to bug 25078
Collapse All | Expand All

(-)a/C4/Installer.pm (-1 / +232 lines)
Lines 19-28 package C4::Installer; Link Here
19
19
20
use Modern::Perl;
20
use Modern::Perl;
21
21
22
use Try::Tiny;
22
use Encode qw( encode is_utf8 );
23
use Encode qw( encode is_utf8 );
23
use DBIx::RunSQL;
24
use DBIx::RunSQL;
24
use YAML::XS;
25
use YAML::XS;
25
use C4::Context;
26
use C4::Context;
27
use Koha::Schema;
26
use DBI;
28
use DBI;
27
use Koha;
29
use Koha;
28
30
Lines 30-36 use vars qw(@ISA @EXPORT); Link Here
30
BEGIN {
32
BEGIN {
31
    require Exporter;
33
    require Exporter;
32
    @ISA = qw( Exporter );
34
    @ISA = qw( Exporter );
33
    push @EXPORT, qw( primary_key_exists foreign_key_exists index_exists column_exists TableExists);
35
    push @EXPORT, qw( primary_key_exists foreign_key_exists index_exists column_exists TableExists TransformToNum CheckVersion sanitize_zero_date update get_db_entries );
34
};
36
};
35
37
36
=head1 NAME
38
=head1 NAME
Lines 683-688 sub TableExists { # Could be renamed table_exists for consistency Link Here
683
    return 0;
685
    return 0;
684
}
686
}
685
687
688
sub version_from_file {
689
    my $file = shift;
690
    return unless $file =~ m|(^\|/)(\d{2})(\d{2})(\d{2})(\d{3}).pl$|;
691
    return sprintf "%s.%s.%s.%s", $2, $3, $4, $5;
692
}
693
694
sub get_db_entries {
695
    my $db_revs_dir = C4::Context->config('intranetdir') . '/installer/data/mysql/db_revs';
696
    opendir my $dh, $db_revs_dir or die "Cannot open $db_revs_dir dir ($!)";
697
    my @files = sort grep { m|\.pl$| && ! m|skeleton\.pl$| } readdir $dh;
698
    my @need_update;
699
    for my $file ( @files ) {
700
        my $version = version_from_file( $file );
701
702
        unless ( $version ) {
703
            warn "Invalid db_rev found: " . $file;
704
            next
705
        }
706
707
        next unless CheckVersion( $version );
708
709
        push @need_update, sprintf( "%s/%s", $db_revs_dir, $file );
710
    }
711
    return \@need_update;
712
}
713
714
sub update {
715
    my ( $files, $params ) = @_;
716
717
    my $force = $params->{force} || 0;
718
719
    my $schema = Koha::Database->new->schema;
720
    my ( @done, @errors );
721
    for my $file ( @$files ) {
722
723
        my $db_rev = do $file;
724
725
        my $error;
726
727
        try {
728
            $schema->txn_do(
729
                sub {
730
                    $db_rev->{up}->();
731
                }
732
            );
733
        } catch {
734
            $error = $_;
735
        };
736
737
        my $db_entry = {
738
            bug_number  => $db_rev->{bug_number},
739
            description => ( ref $db_rev->{description} eq 'CODE' )
740
              ? $db_rev->{description}->()
741
              : $db_rev->{description},
742
            version     => version_from_file($file),
743
            time        => POSIX::strftime( "%H:%M:%S", localtime ),
744
        };
745
        $db_entry->{output} = output_version( { %$db_entry, done => !$error } );
746
747
        if ( $error ) {
748
            push @errors, { %$db_entry, error => $error };
749
            $force ? next : last ;
750
                # We stop the update if an error occurred!
751
        }
752
753
        SetVersion($db_entry->{version});
754
        push @done, $db_entry;
755
    }
756
    return { success => \@done, error => \@errors };
757
}
758
759
sub output_version {
760
    my ( $db_entry ) = @_;
761
762
    my $descriptions = $db_entry->{description};
763
    my $DBversion = $db_entry->{version};
764
    my $bug_number = $db_entry->{bug_number};
765
    my $time = $db_entry->{time};
766
    my $done = $db_entry->{done} ? "done" : "failed";
767
768
    unless ( ref($descriptions) ) {
769
        $descriptions = [ $descriptions ];
770
    }
771
772
    my $first = 1;
773
    my @output;
774
    for my $description ( @$descriptions ) {
775
        if ( @$descriptions > 1 ) {
776
            if ( $first ) {
777
                unless ( $bug_number ) {
778
                    push @output, sprintf "Upgrade to %s %s [%s]:", $DBversion, $done, $time;
779
                } else {
780
                    push @output, sprintf "Upgrade to %s %s [%s]: Bug %5s", $DBversion, $done, $time, $bug_number;
781
                }
782
            }
783
            push @output, sprintf "\t\t\t\t\t\t   - %s", $description;
784
        } else {
785
            unless ( $bug_number ) {
786
                push @output, sprintf "Upgrade to %s %s [%s]: %s", $DBversion, $done, $time, $description;
787
            } else {
788
                push @output, sprintf "Upgrade to %s %s [%s]: Bug %5s - %s", $DBversion, $done, $time, $bug_number, $description;
789
            }
790
        }
791
        $first = 0;
792
    }
793
    return \@output;
794
}
795
796
=head2 DropAllForeignKeys($table)
797
798
Drop all foreign keys of the table $table
799
800
=cut
801
802
sub DropAllForeignKeys {
803
    my ($table) = @_;
804
    # get the table description
805
    my $dbh = C4::Context->dbh;
806
    my $sth = $dbh->prepare("SHOW CREATE TABLE $table");
807
    $sth->execute;
808
    my $vsc_structure = $sth->fetchrow;
809
    # split on CONSTRAINT keyword
810
    my @fks = split /CONSTRAINT /,$vsc_structure;
811
    # parse each entry
812
    foreach (@fks) {
813
        # isolate what is before FOREIGN KEY, if there is something, it's a foreign key to drop
814
        $_ = /(.*) FOREIGN KEY.*/;
815
        my $id = $1;
816
        if ($id) {
817
            # we have found 1 foreign, drop it
818
            $dbh->do("ALTER TABLE $table DROP FOREIGN KEY $id");
819
            $id="";
820
        }
821
    }
822
}
823
824
825
=head2 TransformToNum
826
827
Transform the Koha version from a 4 parts string
828
to a number, with just 1 .
829
830
=cut
831
832
sub TransformToNum {
833
    my $version = shift;
834
    # remove the 3 last . to have a Perl number
835
    $version =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
836
    # three X's at the end indicate that you are testing patch with dbrev
837
    # change it into 999
838
    # prevents error on a < comparison between strings (should be: lt)
839
    $version =~ s/XXX$/999/;
840
    return $version;
841
}
842
843
=head2 SetVersion
844
845
set the DBversion in the systempreferences
846
847
=cut
848
849
sub SetVersion {
850
    return if $_[0]=~ /XXX$/;
851
      #you are testing a patch with a db revision; do not change version
852
    my $kohaversion = TransformToNum($_[0]);
853
    my $dbh = C4::Context->dbh;
854
    if (C4::Context->preference('Version')) {
855
      my $finish=$dbh->prepare("UPDATE systempreferences SET value=? WHERE variable='Version'");
856
      $finish->execute($kohaversion);
857
    } else {
858
      my $finish=$dbh->prepare("INSERT into systempreferences (variable,value,explanation) values ('Version',?,'The Koha database version. WARNING: Do not change this value manually, it is maintained by the webinstaller')");
859
      $finish->execute($kohaversion);
860
    }
861
    C4::Context::clear_syspref_cache(); # invalidate cached preferences
862
}
863
864
sub NewVersion {
865
    # void FIXME replace me
866
}
867
868
=head2 CheckVersion
869
870
Check whether a given update should be run when passed the proposed version
871
number. The update will always be run if the proposed version is greater
872
than the current database version and less than or equal to the version in
873
kohaversion.pl. The update is also run if the version contains XXX, though
874
this behavior will be changed following the adoption of non-linear updates
875
as implemented in bug 7167.
876
877
=cut
878
879
sub CheckVersion {
880
    my ($proposed_version) = @_;
881
    my $version_number = TransformToNum($proposed_version);
882
883
    # The following line should be deleted when bug 7167 is pushed
884
    return 1 if ( $proposed_version =~ m/XXX/ );
885
886
    if ( C4::Context->preference("Version") < $version_number
887
        && $version_number <= TransformToNum( $Koha::VERSION ) )
888
    {
889
        return 1;
890
    }
891
892
    return 0;
893
}
894
895
sub sanitize_zero_date {
896
    my ( $table_name, $column_name ) = @_;
897
898
    my $dbh = C4::Context->dbh;
899
900
    my (undef, $datatype) = $dbh->selectrow_array(qq|
901
        SHOW COLUMNS FROM $table_name WHERE Field = ?|, undef, $column_name);
902
903
    if ( $datatype eq 'date' ) {
904
        $dbh->do(qq|
905
            UPDATE $table_name
906
            SET $column_name = NULL
907
            WHERE CAST($column_name AS CHAR(10)) = '0000-00-00';
908
        |);
909
    } else {
910
        $dbh->do(qq|
911
            UPDATE $table_name
912
            SET $column_name = NULL
913
            WHERE CAST($column_name AS CHAR(19)) = '0000-00-00 00:00:00';
914
        |);
915
    }
916
}
686
917
687
=head1 AUTHOR
918
=head1 AUTHOR
688
919
(-)a/Koha.pm (-1 / +1 lines)
Lines 29-35 use vars qw{ $VERSION }; Link Here
29
# - #4 : the developer version. The 4th number is the database subversion.
29
# - #4 : the developer version. The 4th number is the database subversion.
30
#        used by developers when the database changes. updatedatabase take care of the changes itself
30
#        used by developers when the database changes. updatedatabase take care of the changes itself
31
#        and is automatically called by Auth.pm when needed.
31
#        and is automatically called by Auth.pm when needed.
32
$VERSION = "21.06.00.000";
32
$VERSION = "21.06.00.001";
33
33
34
sub version {
34
sub version {
35
    return $VERSION;
35
    return $VERSION;
(-)a/installer/data/mysql/db_revs/210600000.pl (+8 lines)
Line 0 Link Here
1
use Modern::Perl;
2
use utf8;
3
4
{
5
    bug_number => undef,
6
    description => ["🎵 Run, rabbit run. 🎶", "Dig that hole, forget the sun,", "And when at last the work is done", "Don't sit down it's time to dig another one."],
7
    up => sub {},
8
}
(-)a/installer/data/mysql/db_revs/skeleton.pl (+13 lines)
Line 0 Link Here
1
use Modern::Perl;
2
3
{
4
    bug_number => "BUG_NUMBER",
5
    description => "A single line description",
6
    # description => ["Multi", "lines", "description"],
7
    # description => sub { return ["Your dynamic description"] },
8
    up => sub {
9
        my $dbh = C4::Context->dbh;
10
        # Do you stuffs here
11
        $dbh->do(q{});
12
    },
13
}
(-)a/installer/data/mysql/updatedatabase.pl (-155 / +16 lines)
Lines 34-39 use feature 'say'; Link Here
34
# CPAN modules
34
# CPAN modules
35
use DBI;
35
use DBI;
36
use Getopt::Long;
36
use Getopt::Long;
37
use Encode qw( encode_utf8 );
37
# Koha modules
38
# Koha modules
38
use C4::Context;
39
use C4::Context;
39
use C4::Installer;
40
use C4::Installer;
Lines 61-70 my ( Link Here
61
62
62
my $schema = Koha::Database->new()->schema();
63
my $schema = Koha::Database->new()->schema();
63
64
64
my $silent;
65
my ( $silent, $force );
65
GetOptions(
66
GetOptions(
66
    's' =>\$silent
67
    's'     => \$silent,
67
    );
68
    'force' => \$force,
69
);
68
my $dbh = C4::Context->dbh;
70
my $dbh = C4::Context->dbh;
69
$|=1; # flushes output
71
$|=1; # flushes output
70
72
Lines 24279-24287 if( CheckVersion( $DBversion ) ) { Link Here
24279
    NewVersion( $DBversion, "", "Koha 21.05.00 release" );
24281
    NewVersion( $DBversion, "", "Koha 21.05.00 release" );
24280
}
24282
}
24281
24283
24282
$DBversion = '21.06.00.000';
24284
unless ( $ENV{HTTP_HOST} ) { # Is that correct?
24283
if( CheckVersion( $DBversion ) ) {
24285
    my $files = C4::Installer::get_db_entries;
24284
    NewVersion( $DBversion, "", ["🎵 Run, rabbit run. 🎶", "Dig that hole, forget the sun,", "And when at last the work is done", "Don't sit down it's time to dig another one."] );
24286
    my $report = update( $files, { force => 1 } );
24287
24288
    for my $s ( @{ $report->{success} } ) {
24289
        say Encode::encode_utf8(join "\n", @{$s->{output}});
24290
    }
24291
    for my $e ( @{ $report->{error} } ) {
24292
        say Encode::encode_utf8(join "\n", @{$e->{output}});
24293
        say Encode::encode_utf8("ERROR - " . $e->{error});
24294
    }
24285
}
24295
}
24286
24296
24287
# SEE bug 13068
24297
# SEE bug 13068
Lines 24302-24454 foreach my $file ( sort readdir $dirh ) { Link Here
24302
    }
24312
    }
24303
}
24313
}
24304
24314
24305
=head1 FUNCTIONS
24306
24307
=head2 DropAllForeignKeys($table)
24308
24309
Drop all foreign keys of the table $table
24310
24311
=cut
24312
24313
sub DropAllForeignKeys {
24314
    my ($table) = @_;
24315
    # get the table description
24316
    my $sth = $dbh->prepare("SHOW CREATE TABLE $table");
24317
    $sth->execute;
24318
    my $vsc_structure = $sth->fetchrow;
24319
    # split on CONSTRAINT keyword
24320
    my @fks = split /CONSTRAINT /,$vsc_structure;
24321
    # parse each entry
24322
    foreach (@fks) {
24323
        # isolate what is before FOREIGN KEY, if there is something, it's a foreign key to drop
24324
        $_ = /(.*) FOREIGN KEY.*/;
24325
        my $id = $1;
24326
        if ($id) {
24327
            # we have found 1 foreign, drop it
24328
            $dbh->do("ALTER TABLE $table DROP FOREIGN KEY $id");
24329
            $id="";
24330
        }
24331
    }
24332
}
24333
24334
24335
=head2 TransformToNum
24336
24337
Transform the Koha version from a 4 parts string
24338
to a number, with just 1 .
24339
24340
=cut
24341
24342
sub TransformToNum {
24343
    my $version = shift;
24344
    # remove the 3 last . to have a Perl number
24345
    $version =~ s/(.*\..*)\.(.*)\.(.*)/$1$2$3/;
24346
    # three X's at the end indicate that you are testing patch with dbrev
24347
    # change it into 999
24348
    # prevents error on a < comparison between strings (should be: lt)
24349
    $version =~ s/XXX$/999/;
24350
    return $version;
24351
}
24352
24353
=head2 SetVersion
24354
24355
set the DBversion in the systempreferences
24356
24357
=cut
24358
24359
sub SetVersion {
24360
    return if $_[0]=~ /XXX$/;
24361
      #you are testing a patch with a db revision; do not change version
24362
    my $kohaversion = TransformToNum($_[0]);
24363
    if (C4::Context->preference('Version')) {
24364
      my $finish=$dbh->prepare("UPDATE systempreferences SET value=? WHERE variable='Version'");
24365
      $finish->execute($kohaversion);
24366
    } else {
24367
      my $finish=$dbh->prepare("INSERT into systempreferences (variable,value,explanation) values ('Version',?,'The Koha database version. WARNING: Do not change this value manually, it is maintained by the webinstaller')");
24368
      $finish->execute($kohaversion);
24369
    }
24370
    C4::Context::clear_syspref_cache(); # invalidate cached preferences
24371
}
24372
24373
sub NewVersion {
24374
    my ( $DBversion, $bug_number, $descriptions ) = @_;
24375
24376
    SetVersion($DBversion);
24377
24378
    unless ( ref($descriptions) ) {
24379
        $descriptions = [ $descriptions ];
24380
    }
24381
    my $first = 1;
24382
    my $time = POSIX::strftime("%H:%M:%S",localtime);
24383
    for my $description ( @$descriptions ) {
24384
        if ( @$descriptions > 1 ) {
24385
            if ( $first ) {
24386
                unless ( $bug_number ) {
24387
                    say sprintf "Upgrade to %s done [%s]: %s", $DBversion, $time, $description;
24388
                } else {
24389
                    say sprintf "Upgrade to %s done [%s]: Bug %5s - %s", $DBversion, $time, $bug_number, $description;
24390
                }
24391
            } else {
24392
                say sprintf "\t\t\t\t\t\t   - %s", $description;
24393
            }
24394
        } else {
24395
            unless ( $bug_number ) {
24396
                say sprintf "Upgrade to %s done [%s]: %s", $DBversion, $time, $description;
24397
            } else {
24398
                say sprintf "Upgrade to %s done [%s]: Bug %5s - %s", $DBversion, $time, $bug_number, $description;
24399
            }
24400
        }
24401
        $first = 0;
24402
    }
24403
}
24404
24405
=head2 CheckVersion
24406
24407
Check whether a given update should be run when passed the proposed version
24408
number. The update will always be run if the proposed version is greater
24409
than the current database version and less than or equal to the version in
24410
kohaversion.pl. The update is also run if the version contains XXX, though
24411
this behavior will be changed following the adoption of non-linear updates
24412
as implemented in bug 7167.
24413
24414
=cut
24415
24416
sub CheckVersion {
24417
    my ($proposed_version) = @_;
24418
    my $version_number = TransformToNum($proposed_version);
24419
24420
    # The following line should be deleted when bug 7167 is pushed
24421
    return 1 if ( $proposed_version =~ m/XXX/ );
24422
24423
    if ( C4::Context->preference("Version") < $version_number
24424
        && $version_number <= TransformToNum( $Koha::VERSION ) )
24425
    {
24426
        return 1;
24427
    }
24428
    else {
24429
        return 0;
24430
    }
24431
}
24432
24433
sub sanitize_zero_date {
24434
    my ( $table_name, $column_name ) = @_;
24435
24436
    my (undef, $datatype) = $dbh->selectrow_array(qq|
24437
        SHOW COLUMNS FROM $table_name WHERE Field = ?|, undef, $column_name);
24438
24439
    if ( $datatype eq 'date' ) {
24440
        $dbh->do(qq|
24441
            UPDATE $table_name
24442
            SET $column_name = NULL
24443
            WHERE CAST($column_name AS CHAR(10)) = '0000-00-00';
24444
        |);
24445
    } else {
24446
        $dbh->do(qq|
24447
            UPDATE $table_name
24448
            SET $column_name = NULL
24449
            WHERE CAST($column_name AS CHAR(19)) = '0000-00-00 00:00:00';
24450
        |);
24451
    }
24452
}
24453
24454
exit;
24315
exit;
(-)a/installer/install.pl (-35 / +11 lines)
Lines 394-434 elsif ( $step && $step == 3 ) { Link Here
394
            chk_log( $logdir, "updatedatabase-error_$filename_suffix" )
394
            chk_log( $logdir, "updatedatabase-error_$filename_suffix" )
395
        );
395
        );
396
396
397
        my $cmd = C4::Context->config("intranetdir")
397
        my $updatedatabase_path = C4::Context->config("intranetdir")
398
          . "/installer/data/$info{dbms}/updatedatabase.pl >> $logfilepath 2>> $logfilepath_errors";
398
          . "/installer/data/$info{dbms}/updatedatabase.pl";
399
399
400
        system($cmd );
400
        my $db_entries = get_db_entries();
401
401
        my $report = update( $db_entries );
402
        my $fh;
402
403
        open( $fh, "<:encoding(utf-8)", $logfilepath )
403
        # FIXME restore log to logfilepath and logfilepath_errors
404
          or die "Cannot open log file $logfilepath: $!";
404
405
        my @report = <$fh>;
405
        $template->param( success => $report->{success}, error => $report->{error} );
406
        close $fh;
406
407
        if (@report) {
407
        #warn "The following errors were returned while attempting to run the updatedatabase.pl script:\n"; #FIXME restore this
408
            $template->param( update_report =>
409
                  [ map { { line => $_ =~ s/\t/&emsp;&emsp;/gr } } split( /\n/, join( '', @report ) ) ]
410
            );
411
            $template->param( has_update_succeeds => 1 );
412
        }
413
        else {
414
            eval { `rm $logfilepath` };
415
        }
416
        open( $fh, "<:encoding(utf-8)", $logfilepath_errors )
417
          or die "Cannot open log file $logfilepath_errors: $!";
418
        @report = <$fh>;
419
        close $fh;
420
        if (@report) {
421
            $template->param( update_errors =>
422
                  [ map { { line => $_ } } split( /\n/, join( '', @report ) ) ]
423
            );
424
            $template->param( has_update_errors => 1 );
425
            warn
426
"The following errors were returned while attempting to run the updatedatabase.pl script:\n";
427
            foreach my $line (@report) { warn "$line\n"; }
428
        }
429
        else {
430
            eval { `rm $logfilepath_errors` };
431
        }
432
        $template->param( $op => 1 );
408
        $template->param( $op => 1 );
433
    }
409
    }
434
    else {
410
    else {
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/installer/step3.tt (-15 / +26 lines)
Lines 244-274 Link Here
244
244
245
                [% IF ( updatestructure ) %]
245
                [% IF ( updatestructure ) %]
246
                    <h2>Updating database structure</h2>
246
                    <h2>Updating database structure</h2>
247
                    [% IF ( has_update_succeeds ) %]
247
                    [% IF success %]
248
                        <p>Update report :</p>
248
                        <p>Update report:</p>
249
                        <ul>
249
                        <ul>
250
                            [% FOREACH l IN update_report %]
250
                            [% FOR s IN success %]
251
                                [% SET line = l.line %]
251
                                [% FOR o IN s.output %]
252
                                [% IF line.match('^Upgrade to') %]
252
                                    <li>[% o | html %]</li>
253
                                    <li>[% line | $raw %]</li>
253
                                    [% IF s.output.size > 1 %]
254
                                [% ELSE %]
254
                                        [% IF loop.first %]<ul>[% ELSIF loop.last %]</ul>[% END %]
255
                                    [% line | $raw %]<br/>
255
                                    [% END %]
256
                                [% END %]
256
                                [% END %]
257
                            [% END %]
257
                            [% END %]
258
                        </ul>
258
                        </ul>
259
                    [% END %]
259
                    [% END %]
260
                    [% IF ( has_update_errors ) %]
260
                    [% IF error %]
261
                        <p>Update errors :</p>
261
                        <p>Update error :</p>
262
                        <ul>
262
                        <ul>
263
                            [% FOREACH update_error IN update_errors %]
263
                            [% FOR e IN error %]
264
                                <li class="update_error">[% update_error.line | html %]</li>
264
                                [% FOR o IN e.output %]
265
                                    <li class="update_error">
266
                                        [% o | html %]
267
                                        <br/>
268
                                        ERROR: [% e.error | html %]
269
270
                                        [% IF e.output.size > 1 %]
271
                                            [% IF loop.first %]<ul>[% ELSIF loop.last %]</ul>[% END %]
272
                                        [% END %]
273
                                    </li>
274
                                [% END %]
265
                            [% END %]
275
                            [% END %]
266
                        </ul>
276
                        </ul>
267
                    [% END %]
277
                    [% END %]
268
                    [% UNLESS ( has_update_errors ) %]
278
                    [% UNLESS error %]
269
                        <p>Everything went okay. Update done.</p>
279
                        <p>Everything went okay. Update done.</p>
280
                        <p><a href="install.pl?step=3&amp;op=finished" class="btn btn-primary">Continue to log in to Koha</a></p>
281
                    [% ELSE %]
282
                        <p><a href="install.pl?step=3&amp;op=updatestructure" class="btn btn-primary">Try again</a></p>
270
                    [% END %]
283
                    [% END %]
271
                    <p><a href="install.pl?step=3&amp;op=finished" class="btn btn-primary">Continue to log in to Koha</a></p>
272
               [% END # / IF updatestructure %]
284
               [% END # / IF updatestructure %]
273
            </div> <!-- / #installer-step3 -->
285
            </div> <!-- / #installer-step3 -->
274
        </div> <!-- / .row -->
286
        </div> <!-- / .row -->
275
- 

Return to bug 25078