Raw content of EnsTestDB
=pod
=head1 NAME - EnsTestDB
=head1 SYNOPSIS
# Add test dir to lib search path
use lib 't';
use EnsTestDB;
my $ens_test = EnsTestDB->new();
# Load some data into the db
$ens_test->do_sql_file("some_data.sql");
# Get an EnsEMBL db object for the test db
my $db = $ens_test->get_DBSQL_Obj;
=head1 DESCRIPTION
This is a module used just by the EnsEMBL test
suite to create a test database for a particular
test. Creating a new object creates a database
with a name such that it should never clash with
other users testing on the same server. The
database is destroyed when the object goes out of
scope.
The settings, such as the server host and port,
are found in the file B. See
B for an example.
=head1 METHODS
=cut
package EnsTestDB;
use vars qw(@ISA);
use strict;
use Sys::Hostname 'hostname';
use Bio::EnsEMBL::DBLoader;
use DBI;
use Carp;
#Package variable for unique database name
my $counter=0;
{
# This is a list of possible entries in the config
# file "EnsTestDB.conf" or in the hash being used.
my %known_field = map {$_, 1} qw(
driver
host
user
port
pass
schema_sql
module
readonly_user
);
### now takes an optional argument; when given, it can be a filename
### or a hash, and will be used to get arguments from. If not, the
### file 'EnsTestDB.conf' will be tried; it it exist; that is taken;
### otherwise, some hopefully defaults will be used.
sub new {
my( $pkg, $arg ) = @_;
$counter++;
my $self =undef;
$self = do 'EnsTestDB.conf'
|| {
'driver' => 'mysql',
'host' => 'localhost',
'user' => 'root',
'port' => '3306',
'pass' => undef,
'schema_sql' => ['../sql/table.sql'],
'module' => 'Bio::EnsEMBL::DBSQL::DBAdaptor'
};
if ($arg) {
if (ref $arg eq 'HASH' ) { # a hash ref
foreach my $key (keys %$arg) {
$self->{$key} = $arg->{$key};
}
}
elsif (-f $arg ) { # a file name
$self = do $arg;
} else {
confess "expected a hash ref or existing file";
}
}
foreach my $f (keys %$self) {
confess "Unknown config field: '$f'" unless $known_field{$f};
}
bless $self, $pkg;
$self->create_db;
return $self;
}
}
sub driver {
my( $self, $value ) = @_;
if ($value) {
$self->{'driver'} = $value;
}
return $self->{'driver'} || confess "driver not set";
}
sub host {
my( $self, $value ) = @_;
if ($value) {
$self->{'host'} = $value;
}
return $self->{'host'} || confess "host not set";
}
sub user {
my( $self, $value ) = @_;
if ($value) {
$self->{'user'} = $value;
}
return $self->{'user'} || confess "user not set";
}
sub port {
my( $self, $value ) = @_;
if ($value) {
$self->{'port'} = $value;
}
return $self->{'port'};
}
sub pass {
my( $self, $value ) = @_;
if ($value) {
$self->{'pass'} = $value;
}
return $self->{'pass'};
}
sub schema_sql {
my( $self, $value ) = @_;
if ($value) {
push(@{$self->{'schema_sql'}}, $value);
}
return $self->{'schema_sql'} || confess "schema_sql not set";
}
sub dbname {
my( $self ) = @_;
$self->{'_dbname'} ||= $self->_create_db_name();
return $self->{'_dbname'};
}
# convenience method: by calling it, you get the name of the database,
# which you can cut-n-paste into another window for doing some mysql
# stuff interactively
sub pause {
my ($self) = @_;
my $db = $self->{'_dbname'};
print STDERR "pausing to inspect database; name of database is: $db\n";
print STDERR "press ^D to continue\n";
`cat `;
}
sub module {
my ($self, $value) = @_;
$self->{'module'} = $value if ($value);
return $self->{'module'};
}
sub _create_db_name {
my( $self ) = @_;
my $host = hostname();
my $db_name = "_test_db_${host}_$$".$counter;
$db_name =~ s{\W}{_}g;
return $db_name;
}
sub create_db {
my( $self ) = @_;
### FIXME: not portable between different drivers
my $locator = 'DBI:'. $self->driver .':host='.$self->host;
my $db = DBI->connect(
$locator, $self->user, $self->pass, {RaiseError => 1}
) or confess "Can't connect to server";
my $db_name = $self->dbname;
$db->do("CREATE DATABASE $db_name");
$db->disconnect;
$self->do_sql_file(@{$self->schema_sql});
}
sub db_handle {
my( $self ) = @_;
unless ($self->{'_db_handle'}) {
$self->{'_db_handle'} = DBI->connect(
$self->test_locator, $self->user, $self->pass, {RaiseError => 1}
) or confess "Can't connect to server";
}
return $self->{'_db_handle'};
}
sub test_locator {
my( $self ) = @_;
my $locator = 'dbi:'. $self->driver .':database='. $self->dbname;
foreach my $meth (qw{ host port pass}) {
if (my $value = $self->$meth()) {
$locator .= ";$meth=$value";
}
}
return $locator;
}
sub ensembl_locator {
my( $self) = @_;
my $module = ($self->module() || 'Bio::EnsEMBL::DBSQL::DBAdaptor');
my $locator = '';
foreach my $meth (qw{ host port dbname user pass}) {
my $value = $self->$meth();
next unless defined $value;
$locator .= ';' if $locator;
$locator .= "$meth=$value";
}
$locator .= ";perlonlyfeatures=1";
return "$module/$locator";
}
# return the database handle:
sub get_DBSQL_Obj {
my( $self ) = @_;
my $locator = $self->ensembl_locator();
my $db = Bio::EnsEMBL::DBLoader->new($locator);
$db->dnadb($self->dnadb);
}
sub do_sql_file {
my( $self, @files ) = @_;
local *SQL;
my $i = 0;
my $dbh = $self->db_handle;
my $comment_strip_warned=0;
foreach my $file (@files)
{
my $sql = '';
open SQL, $file or die "Can't read SQL file '$file' : $!";
while () {
# careful with stripping out comments; quoted text
# (e.g. aligments) may contain them. Just warn (once) and ignore
if ( /'[^']*#[^']*'/
|| /'[^']*--[^']*'/ ) {
if ( $comment_strip_warned++ ) {
# already warned
} else {
warn "#################################\n".
warn "# found comment strings inside quoted string; not stripping, too complicated: $_\n";
warn "# (continuing, assuming all these they are simply valid quoted strings)\n";
warn "#################################\n";
}
} else {
s/(#|--).*//; # Remove comments
}
next unless /\S/; # Skip lines which are all space
$sql .= $_;
$sql .= ' ';
}
close SQL;
#Modified split statement, only semicolumns before end of line,
#so we can have them inside a string in the statement
#\s*\n, takes in account the case when there is space before the new line
foreach my $s (grep /\S/, split /;[ \t]*\n/, $sql) {
$self->validate_sql($s);
$dbh->do($s);
$i++
}
}
return $i;
} # do_sql_file
sub validate_sql {
my ($self, $statement) = @_;
if ($statement =~ /insert/i)
{
$statement =~ s/\n/ /g; #remove newlines
die ("INSERT should use explicit column names (-c switch in mysqldump)\n$statement\n")
unless ($statement =~ /insert.+into.*\(.+\).+values.*\(.+\)/i);
}
}
sub dnadb {
my ($self,$dnadb) = @_;
if (defined($dnadb)) {
$self->{_dnadb} = $dnadb;
}
return $self->{_dnadb};
}
sub DESTROY {
my( $self ) = @_;
if (my $dbh = $self->db_handle) {
my $db_name = $self->dbname;
$dbh->do("DROP DATABASE $db_name");
$dbh->disconnect;
}
}
1;
__END__
=head1 AUTHOR
James Gilbert B jgrg@sanger.ac.uk