DBAdaptor.pm 10.7 KB
Newer Older
1
=pod
2
3
4

=head1 NAME

5
    Bio::EnsEMBL::Hive::DBSQL::DBAdaptor
6
7
8

=head1 SYNOPSIS

9
    my $db = Bio::EnsEMBL::Hive::DBSQL::DBAdaptor->new( -url => 'mysql://my_username:my_password@my_hostname:3306/my_hive_database' );
10
11

=head1 DESCRIPTION
12

13
14
15
16
    This object represents the handle for a Hive system enabled database

=head1 LICENSE

17
    Copyright [1999-2015] Wellcome Trust Sanger Institute and the EMBL-European Bioinformatics Institute
Matthieu Muffato's avatar
Matthieu Muffato committed
18
    Copyright [2016-2017] EMBL-European Bioinformatics Institute
19
20
21
22
23
24
25
26
27

    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.
28
29
30

=head1 CONTACT

31
    Please subscribe to the Hive mailing list:  http://listserver.ebi.ac.uk/mailman/listinfo/ehive-users  to discuss Hive-related questions or to be notified of our updates
32
33
34
35
36
37
38

=cut


package Bio::EnsEMBL::Hive::DBSQL::DBAdaptor;

use strict;
39
use warnings;
40

41
42
use Scalar::Util qw(weaken);

43
use Bio::EnsEMBL::Hive;
44
use Bio::EnsEMBL::Hive::HivePipeline;
45
use Bio::EnsEMBL::Hive::DBSQL::DBConnection;
46
use Bio::EnsEMBL::Hive::DBSQL::SqlSchemaAdaptor;
47
48
use Bio::EnsEMBL::Hive::Utils ('throw');
use Bio::EnsEMBL::Hive::Utils::Collection;
49

50
51
52
53
use Bio::EnsEMBL::Hive::MetaParameters;
use Bio::EnsEMBL::Hive::PipelineWideParameters;
use Bio::EnsEMBL::Hive::ResourceClass;
use Bio::EnsEMBL::Hive::ResourceDescription;
54
use Bio::EnsEMBL::Hive::Analysis;
55
56
57
use Bio::EnsEMBL::Hive::AnalysisStats;
use Bio::EnsEMBL::Hive::AnalysisCtrlRule;
use Bio::EnsEMBL::Hive::DataflowRule;
58
use Bio::EnsEMBL::Hive::DataflowTarget;
59

60

61
sub new {
62
63
    my $class = shift @_;
    my %flags = @_;
64

65
    my ($dbc, $url, $reg_conf, $reg_type, $reg_alias, $species, $no_sql_schema_version_check)
66
        = delete @flags{qw(-dbconn -url -reg_conf -reg_type -reg_alias -species -no_sql_schema_version_check)};
67

68
    $url .= ';no_sql_schema_version_check=1' if($url && $no_sql_schema_version_check);
69

70
71
72
73
74
    if($reg_conf or $reg_alias) {   # need to initialize Registry even if $reg_conf is not really given
        require Bio::EnsEMBL::Registry;
        Bio::EnsEMBL::Registry->load_all($reg_conf);    # if undefined, default reg_conf will be used
    }

75
76
77
    my $self;

    if($url) {
78
        $dbc = Bio::EnsEMBL::Hive::DBSQL::DBConnection->new(-url => $url, %flags)
79
80
            or die "Unable to create a DBC using url='$url'";

81
    } elsif($reg_alias) {
82

83
84
85
        if($reg_alias=~/^(\w+):(\w+)$/) {
            ($reg_type, $reg_alias) = ($1, $2);
        }
86

87
88
        unless($reg_type) {     # if no $reg_type explicitly given, try to guess:
            my $dbas = Bio::EnsEMBL::Registry->get_all_DBAdaptors(-species => $reg_alias);
89

90
91
92
93
94
95
96
97
98
99
100
101
102
103
            if( scalar(@$dbas) == 1 ) {
                $self = $dbas->[0];
            } elsif( @$dbas ) {
                warn "The registry contains multiple entries for '$reg_alias', please prepend the reg_alias with the desired type";
            }
        }

        unless($self) {         # otherwise (or if not found) try a specific $reg_type
            $reg_type ||= 'hive';
            $self = Bio::EnsEMBL::Registry->get_DBAdaptor($reg_alias, $reg_type)
                or die "Unable to connect to DBA using reg_conf='$reg_conf', reg_type='$reg_type', reg_alias='$reg_alias'\n";
        }

        if( $self and !$self->isa($class) ) {   # if we found a non-Hive Registry entry, detach the $dbc and build a Hive dba around it:
104
            $dbc = $self->dbc;
105
            $self = undef;
106
        }
107
108
    }

109
    if($dbc && !$self) {
110
        $self = bless {}, $class;
111
        $self->dbc( $dbc );
112
    }
113
114

    unless($no_sql_schema_version_check) {
115
116

        my $dbc = $self->dbc();
117
        my $safe_url = $dbc->url('EHIVE_PASS');
118
119

        my $code_sql_schema_version = Bio::EnsEMBL::Hive::DBSQL::SqlSchemaAdaptor->get_code_sql_schema_version()
120
            || die "DB($safe_url) Could not establish code_sql_schema_version, please check that 'EHIVE_ROOT_DIR' environment variable is set correctly";
121

122
123
        my $db_sql_schema_version   = eval { $self->get_MetaAdaptor->fetch_by_meta_key( 'hive_sql_schema_version' )->{'meta_value'}; };

124
        if($@) {
125
            if($@ =~ /hive_meta.*doesn't exist/) {
126

127
                die "\nDB($safe_url) The 'hive_meta' table does not seem to exist in the database yet.\nPlease patch the database up to sql_schema_version '$code_sql_schema_version' and try again.\n";
128
129
130

            } else {

131
                die "DB($safe_url) $@";
132
            }
133
134
135

        } elsif(!$db_sql_schema_version) {

136
            die "\nDB($safe_url) The 'hive_meta' table does not contain 'hive_sql_schema_version' entry.\nPlease investigate.\n";
137
138
139

        } elsif($db_sql_schema_version < $code_sql_schema_version) {

140
            my $new_patches = Bio::EnsEMBL::Hive::DBSQL::SqlSchemaAdaptor->get_sql_schema_patches( $db_sql_schema_version, $dbc->driver )
141
                || die "DB($safe_url) sql_schema_version mismatch: the database's version is '$db_sql_schema_version' but the code is already '$code_sql_schema_version'.\n"
142
143
                      ."Unfortunately we cannot patch the database; you may have to create a new database or agree to run older code\n";

144
            my $sql_patcher_command = "$ENV{'EHIVE_ROOT_DIR'}/scripts/db_cmd.pl -url $safe_url";
145

146
            die "DB($safe_url) sql_schema_version mismatch: the database's version is '$db_sql_schema_version' but the code is already '$code_sql_schema_version'.\n"
147
148
149
               ."Please upgrade the database by applying the following patches:\n\n"
               .join("\n", map { ($_=~/\.\w*sql\w*$/) ? "\t$sql_patcher_command < $_" : "$_ -url $safe_url" } @$new_patches)
               ."\n\nand try again.\n";
150
151
152

        } elsif($code_sql_schema_version < $db_sql_schema_version) {

153
            die "DB($safe_url) sql_schema_version mismatch: the database's version is '$db_sql_schema_version', but your code is still '$code_sql_schema_version'.\n"
154
               ."Please update the code and try again.\n";
155
        }
156
    }
157

158
159
160
161
162
    if($species) {      # [compatibility with core code] store the DBAdaptor in Registry:
        require Bio::EnsEMBL::Registry;
        Bio::EnsEMBL::Registry->add_DBAdaptor( $species, 'hive', $self );
    }

163
    return $self;
164
165
166
}


167
168
169
170
sub species {   # a stub to please Registry code
    return @_;
}

171
172
173
174
sub group {     # a stub to please Registry code
    return 'hive';
}

175

176
sub dbc {
177
    my $self = shift;
178

179
    $self->{'_dbc'} = bless shift, 'Bio::EnsEMBL::Hive::DBSQL::DBConnection' if(@_);
180

181
    return $self->{'_dbc'};
182
183
184
}


185
186
187
188
189
190
sub hive_pipeline {
    my $self = shift @_;
    if (@_) {
        $self->{'_hive_pipeline'} = shift @_;
    }
    unless ($self->{'_hive_pipeline'}) {
191
        $self->{'_hive_pipeline'} = Bio::EnsEMBL::Hive::HivePipeline->new( -dba => $self );     # ToDo: this lazy-loaded object is not registered in TheApiary (yet)
192
193
194
195
196
    }
    return $self->{'_hive_pipeline'};
}


197
198
199
200
201
202
203
our %adaptor_type_2_package_name = (
    'Accumulator'           => 'Bio::EnsEMBL::Hive::DBSQL::AccumulatorAdaptor',
    'Analysis'              => 'Bio::EnsEMBL::Hive::DBSQL::AnalysisAdaptor',
    'AnalysisCtrlRule'      => 'Bio::EnsEMBL::Hive::DBSQL::AnalysisCtrlRuleAdaptor',
    'AnalysisData'          => 'Bio::EnsEMBL::Hive::DBSQL::AnalysisDataAdaptor',
    'AnalysisJob'           => 'Bio::EnsEMBL::Hive::DBSQL::AnalysisJobAdaptor',
    'AnalysisStats'         => 'Bio::EnsEMBL::Hive::DBSQL::AnalysisStatsAdaptor',
204
    'Beekeeper'             => 'Bio::EnsEMBL::Hive::DBSQL::BeekeeperAdaptor',
205
    'DataflowRule'          => 'Bio::EnsEMBL::Hive::DBSQL::DataflowRuleAdaptor',
206
    'DataflowTarget'        => 'Bio::EnsEMBL::Hive::DBSQL::DataflowTargetAdaptor',
207
208
    'LogMessage'            => 'Bio::EnsEMBL::Hive::DBSQL::LogMessageAdaptor',
    'Meta'                  => 'Bio::EnsEMBL::Hive::DBSQL::MetaAdaptor',
209
    'PipelineWideParameters'=> 'Bio::EnsEMBL::Hive::DBSQL::PipelineWideParametersAdaptor',
210
211
212
    'NakedTable'            => 'Bio::EnsEMBL::Hive::DBSQL::NakedTableAdaptor',
    'ResourceClass'         => 'Bio::EnsEMBL::Hive::DBSQL::ResourceClassAdaptor',
    'ResourceDescription'   => 'Bio::EnsEMBL::Hive::DBSQL::ResourceDescriptionAdaptor',
213
    'Role'                  => 'Bio::EnsEMBL::Hive::DBSQL::RoleAdaptor',
214
    'Semaphore'             => 'Bio::EnsEMBL::Hive::DBSQL::SemaphoreAdaptor',
215
216
217
218
219
    'Queen'                 => 'Bio::EnsEMBL::Hive::Queen',

        # aliases:
    'Job'                   => 'Bio::EnsEMBL::Hive::DBSQL::AnalysisJobAdaptor',
    'Worker'                => 'Bio::EnsEMBL::Hive::Queen',
220
    'MetaParameters'        => 'Bio::EnsEMBL::Hive::DBSQL::MetaAdaptor',
221
222
223
);


Leo Gordon's avatar
Leo Gordon committed
224
sub get_available_adaptors {
225
226

    return \%adaptor_type_2_package_name;
227
}
228

229

230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
sub parse_underscored_id_name {
    my ($self, $underscored_id_name) = @_;

    my ($is_an_id, $foo_id_method_name, $foo_obj_method_name);

    my @syll = split(/_/, $underscored_id_name);
    if($syll[scalar(@syll)-1] eq 'id') {
        pop @syll;
        ($is_an_id, $foo_id_method_name, $foo_obj_method_name) = ( 1, $underscored_id_name, join('_', @syll) );
    } else {
        ($is_an_id, $foo_id_method_name, $foo_obj_method_name) = ( 0, $underscored_id_name .'_id' , $underscored_id_name );
    }

    my $AdaptorType = '';   # will be growing from right to left
    while(@syll) {
        $AdaptorType = ucfirst(pop @syll) . $AdaptorType;
        if(exists( $self->get_available_adaptors->{ $AdaptorType })) {
            return ($AdaptorType, $is_an_id, $foo_id_method_name, $foo_obj_method_name);
        }
    }
    return;   # could not parse
}


254
255
sub get_adaptor {
    my $self = shift;
256
257
258
259
    my $AdaptorType = shift;

    my $adaptor_package_name = $self->get_available_adaptors()->{$AdaptorType}
        or throw("Could not find a module corresponding to '$AdaptorType'");
260

261
    my $signature = join(':', $adaptor_package_name, @_);
262
263
264
265

    unless( $self->{'_cached_adaptor'}{$signature} ) {

        eval "require $adaptor_package_name"
266
        or throw("Could not load or compile module '$adaptor_package_name' because $@");
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285

        $self->{'_cached_adaptor'}{$signature} = $adaptor_package_name->new( $self, @_ );
    }

    return $self->{'_cached_adaptor'}{$signature};
}


sub DESTROY { }   # to simplify AUTOLOAD

sub AUTOLOAD {
    our $AUTOLOAD;

    my $type;
    if ( $AUTOLOAD =~ /^.*::get_(\w+)Adaptor$/ ) {
        $type = $1;
    } elsif ( $AUTOLOAD =~ /^.*::get_(\w+)$/ ) {
        $type = $1;
    } else {
286
        throw( "DBAdaptor::AUTOLOAD: Could not interpret the method: $AUTOLOAD" );
287
288
289
290
291
292
293
    }

    my $self = shift;

    return $self->get_adaptor($type, @_);
}

294

295
1;