=head1 LICENSE
Copyright [1999-2015] Wellcome Trust Sanger Institute and the EMBL-European Bioinformatics Institute
Copyright [2016-2024] EMBL-European Bioinformatics Institute
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
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.
=cut
=head1 CONTACT
Please email comments or questions to the public Ensembl
Questions may also be sent to the Ensembl help desk at
=cut
=head1 NAME
Bio::EnsEMBL::DBSQL::AttributeAdaptor - Provides database interaction for
Bio::EnsEMBL::Attribute objects.
=head1 SYNOPSIS
# $db is a Bio::EnsEMBL::DBSQL::DBAdaptor object:
$attribute_adaptor = $db->get_AttributeAdaptor();
$attributes = $attribute_adaptor->fetch_all_by_MiscFeature($feature);
$attributes = $attribute_adaptor->fetch_all_by_Slice($slice);
$attribute_adaptor->store_on_Slice( $slice, \@attributes );
$attribute_adaptor->store_on_MiscFeature( $misc_feature,
\@attributes )
=head1 DESCRIPTION
=head1 METHODS
=cut
$Bio::EnsEMBL::DBSQL::AttributeAdaptor::VERSION = '112.0_56'; # TRIAL
$Bio::EnsEMBL::DBSQL::AttributeAdaptor::VERSION = '112.056';
use strict;
use Bio::EnsEMBL::Utils::Exception qw(throw warning);
use Bio::EnsEMBL::Utils::Scalar qw(assert_ref);
use vars qw(@ISA);
@ISA = qw(Bio::EnsEMBL::DBSQL::BaseAdaptor);
=head2 new
Arg [...] : Superclass args. See Bio::EnsEMBL::DBSQL::BaseAdaptor
Description: Instantiates a Bio::EnsEMBL::DBSQL::AttributeAdaptor
Returntype : Bio::EnsEMBL::AttributeAdaptor
Exceptions : none
Caller : DBAdaptor
Status : Stable
=cut
sub new {
my $class = shift;
my $self = $class->SUPER::new(@_);
# cache creation could go here
return $self;
}
## Storing methods
## Batch store
sub store_batch_on_Object {
my ($self, $table, $attributes, $batch_size) = @_;
# $attributes is a hashref where the key is the object ID
# and the value is an array ref of Attribute objects
# maintain a hash of attrib type IDs by code so we don't have to keep looking them up...
my $attrib_type_ids = {};
# create an arrayref of the values to store
my $rows = [];
$batch_size ||= scalar(values(%$attributes));
while (my ($obj_id, $attribs) = each %{$attributes}) {
for my $attrib (@{$attribs}) {
my $attrib_type_id = $attrib_type_ids->{$attrib->code()};
if (!defined $attrib_type_id) {
$attrib_type_id = $self->_store_type($attrib);
$attrib_type_ids->{$attrib->code()} = $attrib_type_id;
}
push @$rows, sprintf('(%d, %d, %s)', $obj_id, $attrib_type_id, $self->dbc()->db_handle()->quote($attrib->value()));
if (scalar(@$rows) == $batch_size) {
$rows = $self->_store_batch_rows($table, $rows);
}
}
}
$rows = $self->_store_batch_rows($table, $rows);
return;
}
sub _store_batch_rows {
my ($self, $table, $rows) = @_;
if (scalar(@$rows) > 0) {
$self->dbc()->sql_helper()->execute_update(-SQL => 'INSERT INTO ' . $table . '_attrib(' . $table . '_id, attrib_type_id, value) VALUES ' . join(',', @$rows));
}
return [];
}
sub store_batch_on_MiscAttrib {
my ($self, $attributes, $batch_size) = @_;
$self->store_batch_on_Object('misc_feature', $attributes, $batch_size);
return;
}
sub store_batch_on_Slice {
my ($self, $attributes, $batch_size) = @_;
$self->store_batch_on_Object('seq_region', $attributes, $batch_size);
return;
}
sub store_batch_on_Gene {
my ($self, $attributes, $batch_size) = @_;
$self->store_batch_on_Object('gene', $attributes, $batch_size);
return;
}
sub store_batch_on_Transcript {
my ($self, $attributes, $batch_size) = @_;
$self->store_batch_on_Object('transcript', $attributes, $batch_size);
return;
}
sub store_batch_on_Translation {
my ($self, $attributes, $batch_size) = @_;
$self->store_batch_on_Object('translation', $attributes, $batch_size);
return;
}
sub store_batch_on_DnaDnaAlignFeature {
my ($self, $attributes, $batch_size) = @_;
$self->store_batch_on_Object('dna_align_feature', $attributes, $batch_size);
return;
}
## Single store
sub store_on_Object {
my ($self, $object_id, $attributes, $table, $type) = @_;
my $db = $self->db();
if (!defined $type) {
$type = $table;
}
my $insert_ignore = $self->insert_ignore_clause();
my $sth = $self->prepare( "${insert_ignore} INTO ${table}_attrib (${type}_id, attrib_type_id, value)" .
"VALUES (?, ?, ?)" );
for my $attrib ( @$attributes ) {
if(!ref($attrib) && $attrib->isa('Bio::EnsEMBL::Attribute')) {
throw("Reference to list of Bio::EnsEMBL::Attribute objects " .
"argument expected.");
}
my $atid = $self->_store_type( $attrib );
$sth->bind_param(1,$object_id,SQL_INTEGER);
$sth->bind_param(2,$atid,SQL_INTEGER);
$sth->bind_param(3,$attrib->value,SQL_VARCHAR);
$sth->execute();
}
return;
}
sub store_on_MiscFeature {
my ($self, $object, $attributes) = @_;
assert_ref( $object, 'Bio::EnsEMBL::MiscFeature');
my $object_id = $object->dbID();
$self->store_on_Object($object_id, $attributes, 'misc', 'misc_feature');
return;
}
sub store_on_Slice {
my ($self, $object, $attributes) = @_;
assert_ref( $object, 'Bio::EnsEMBL::Slice');
my $object_id = $object->get_seq_region_id();
$self->store_on_Object($object_id, $attributes, 'seq_region');
my $undef_circular_cache = 0;
for my $attrib ( @$attributes ) {
if ((defined $attrib->code) and ($attrib->code eq 'circular_seq')) {
$undef_circular_cache = 1;
}
}
if ($undef_circular_cache) {
#the slice is circular
$object->{'circular'} = 1;
my $slice_adaptor = $object->adaptor();
#undefine slice adaptor->is_circular and the circular slice cache
if (defined $slice_adaptor) {
$slice_adaptor->{'is_circular'} = undef;
$slice_adaptor->{'circular_sr_id_cache'} = {};
}
}
return;
}
sub store_on_Gene {
my ($self, $object, $attributes) = @_;
my $object_id;
if (!ref($object)){
$object_id = $object;
}
else {
$object_id = $object->dbID;
}
$self->store_on_Object($object_id, $attributes, 'gene');
return;
}
sub store_on_Transcript {
my ($self, $object, $attributes) = @_;
my $object_id;
if (!ref($object)){
$object_id = $object;
}
else {
$object_id = $object->dbID;
}
$self->store_on_Object($object_id, $attributes, 'transcript');
return;
}
sub store_on_RNAProduct {
my ($self, $object, $attributes) = @_;
my $object_id;
if (!ref($object)) {
$object_id = $object;
}
else {
$object_id = $object->dbID();
}
$self->store_on_Object($object_id, $attributes, 'rnaproduct');
return;
}
sub store_on_Translation {
my ($self, $object, $attributes) = @_;
my $object_id;
if (!ref($object)){
$object_id = $object;
}
else {
$object_id = $object->dbID;
}
$self->store_on_Object($object_id, $attributes, 'translation');
return;
}
sub store_on_DnaDnaAlignFeature {
my ($self, $object, $attributes) = @_;
my $object_id;
if (!ref($object)){
$object_id = $object;
}
else {
$object_id = $object->dbID;
}
$self->store_on_Object($object_id, $attributes, 'dna_align_feature');
return;
}
## Remove methods
sub remove_from_Object {
my ($self, $object_id, $table, $code, $type) = @_;
my $db = $self->db();
if(!defined($object_id)) {
throw("$table must have dbID.");
}
if (!defined($type)) {
$type = $table;
}
my $sth;
if (defined($code)) {
if ($db->dbc->driver() eq 'mysql') {
$sth = $self->prepare("DELETE a FROM " . $table . "_attrib a, attrib_type at " .
"WHERE a.attrib_type_id = at.attrib_type_id AND ".
"a." . $type . "_id = ? AND ".
"at.code like ?");
} else {
$sth = $self->prepare(qq{DELETE FROM ${table}_attrib
WHERE ${type}_id = ? AND
attrib_type_id IN
(SELECT attrib_type_id
FROM attrib_type
WHERE code LIKE ? ) });
}
$sth->bind_param(1,$object_id,SQL_INTEGER);
$sth->bind_param(2,$code,SQL_VARCHAR);
}
else{
$sth = $self->prepare("DELETE FROM " . $table . "_attrib " .
"WHERE " . $type . "_id = ?");
$sth->bind_param(1,$object_id,SQL_INTEGER);
}
$sth->execute();
$sth->finish();
return;
}
sub remove_from_MiscFeature {
my ($self, $object, $code) = @_;
assert_ref($object, 'Bio::EnsEMBL::MiscFeature');
my $object_id = $object->dbID();
$self->remove_from_Object($object_id, 'misc', $code, 'misc_feature');
return;
}
sub remove_from_Slice {
my ($self, $object, $code) = @_;
assert_ref($object, 'Bio::EnsEMBL::Slice');
my $object_id = $object->get_seq_region_id();
$self->remove_from_Object($object_id, 'seq_region', $code);
return;
}
sub remove_from_Gene {
my ($self, $object, $code) = @_;
assert_ref($object, 'Bio::EnsEMBL::Gene');
my $object_id = $object->dbID();
$self->remove_from_Object($object_id, 'gene', $code);
return;
}
sub remove_from_Transcript {
my ($self, $object, $code) = @_;
assert_ref($object, 'Bio::EnsEMBL::Transcript');
my $object_id = $object->dbID();
$self->remove_from_Object($object_id, 'transcript', $code);
return;
}
sub remove_from_RNAProduct {
my ($self, $object, $code) = @_;
assert_ref($object, 'Bio::EnsEMBL::RNAProduct');
my $object_id = $object->dbID();
$self->remove_from_Object($object_id, 'rnaproduct', $code);
return;
}
sub remove_from_Translation {
my ($self, $object, $code) = @_;
assert_ref($object, 'Bio::EnsEMBL::Translation');
my $object_id = $object->dbID();
$self->remove_from_Object($object_id, 'translation', $code);
return;
}
sub remove_from_DnaDnaAlignFeature {
my ($self, $object, $code) = @_;
assert_ref($object, 'Bio::EnsEMBL::DnaDnaAlignFeature');
my $object_id = $object->dbID();
$self->remove_from_Object($object_id, 'dna_align_feature', $code);
return;
}
## Fetch methods
sub fetch_all {
throw("Use of method fetch_all not supported for attributes");
}
sub fetch_by_code {
my ($self, $code) = @_;
my $sql = "SELECT attrib_type_id, code, name, description " .
"FROM attrib_type
WHERE code = '$code' ";
my $sth = $self->prepare($sql);
$sth->execute();
my ($attrib_type_id, $name, $desc);
$sth->bind_columns(\$attrib_type_id, \$code, \$name, \$desc);
my @results = $sth->fetchrow_array;
$sth->finish();
return \@results;
}
sub fetch_all_by_Object {
my ($self, $object_id, $table, $code, $type) = @_;
if (!defined $type) {
$type = $table;
}
my $sql = "SELECT at.code, at.name, at.description, a.value " .
"FROM " . $table . "_attrib a, attrib_type at " .
"WHERE at.attrib_type_id = a.attrib_type_id ";
if (defined($code)) {
$sql .= 'AND at.code like "' . $code . '" ';
}
if (defined($object_id)) {
$sql .= "AND a." .$type . "_id = " . $object_id;
}
my $sth = $self->prepare($sql);
$sth->execute();
my $results = $self->_obj_from_sth($sth);
$sth->finish();
return $results;
}
sub fetch_all_by_MiscFeature {
my ($self, $object, $code) = @_;
my $object_id;
if (defined($object)) {
assert_ref($object, 'Bio::EnsEMBL::MiscFeature');
$object_id = $object->dbID();
}
my $results = $self->fetch_all_by_Object($object_id, 'misc', $code, 'misc_feature');
return $results;
}
sub fetch_all_by_Slice {
my ($self, $object, $code) = @_;
my $object_id;
if (defined($object)) {
assert_ref($object, 'Bio::EnsEMBL::Slice');
$object_id = $object->get_seq_region_id();
}
my $results = $self->fetch_all_by_Object($object_id, 'seq_region', $code);
return $results;
}
sub fetch_all_by_Gene {
my ($self, $object, $code) = @_;
my $object_id;
if (defined($object)) {
assert_ref($object, 'Bio::EnsEMBL::Gene');
$object_id = $object->dbID();
}
my $results = $self->fetch_all_by_Object($object_id, 'gene', $code);
return $results;
}
sub fetch_all_by_Transcript {
my ($self, $object, $code) = @_;
my $object_id;
if (defined($object)) {
assert_ref($object, 'Bio::EnsEMBL::Transcript');
$object_id = $object->dbID();
}
my $results = $self->fetch_all_by_Object($object_id, 'transcript', $code);
return $results;
}
sub fetch_all_by_Translation {
my ($self, $object, $code) = @_;
my $object_id;
if (defined($object)) {
assert_ref($object, 'Bio::EnsEMBL::Translation');
$object_id = $object->dbID();
}
my $results = $self->fetch_all_by_Object($object_id, 'translation', $code);
return $results;
}
sub fetch_all_by_RNAProduct {
my ($self, $object, $code) = @_;
my $object_id;
if (defined($object)) {
assert_ref($object, 'Bio::EnsEMBL::RNAProduct');
$object_id = $object->dbID();
}
my $results = $self->fetch_all_by_Object($object_id, 'rnaproduct', $code);
return $results;
}
sub fetch_all_by_DnaDnaAlignFeature {
my ($self, $object, $code) = @_;
my $object_id;
if (defined($object)) {
assert_ref($object, 'Bio::EnsEMBL::DnaDnaAlignFeature');
$object_id = $object->dbID();
}
my $results = $self->fetch_all_by_Object($object_id, 'dna_align_feature', $code);
return $results;
}
#
# _id_check
#
# backwards compatibility check:
# check if $ensID is an object; if so, return $obj->dbID
#
sub _id_check {
my $self = shift;
my $ensID = shift;
if ($ensID =~ /^\d+$/) {
return $ensID;
} elsif ( ref($ensID) eq 'Bio::EnsEMBL::Gene'
or ref($ensID) eq 'Bio::EnsEMBL::Transcript'
or ref($ensID) eq 'Bio::EnsEMBL::Translation')
{
warning("You should pass a dbID rather than an ensembl object to store the attribute on");
if ($ensID->dbID) {
return $ensID->dbID;
} else {
throw("Ensembl object " . $ensID->display_id . " doesn't have a dbID, can't store attribute");
}
} else {
throw("Invalid dbID");
}
} ## end sub _id_check
# _store_type
sub _store_type {
my $self = shift;
my $attrib = shift;
my $insert_ignore = $self->insert_ignore_clause();
my $sth1 = $self->prepare
("${insert_ignore} INTO attrib_type (code, name, description) values (?, ?, ?)" );
$sth1->bind_param(1, $attrib->code, SQL_VARCHAR);
$sth1->bind_param(2, $attrib->name, SQL_VARCHAR);
$sth1->bind_param(3, $attrib->description, SQL_LONGVARCHAR);
my $rows_inserted = $sth1->execute();
my $atid = $self->last_insert_id('attrib_type_id', undef, 'attrib_type');
if ($rows_inserted == 0) {
# the insert failed because the code is already stored
my $sth2 = $self->prepare("SELECT attrib_type_id FROM attrib_type " . "WHERE code = ?");
$sth2->bind_param(1, $attrib->code, SQL_VARCHAR);
$sth2->execute();
($atid) = $sth2->fetchrow_array();
$sth2->finish();
if (!$atid) {
throw("Could not store or fetch attrib_type code [" . $attrib->code . "]\n" . "Wrong database user/permissions?");
}
}
$sth1->finish();
return $atid;
} ## end sub _store_type
sub _obj_from_sth {
my $self = shift;
my $sth = shift;
my ($code, $name, $desc, $value);
$sth->bind_columns(\$code, \$name, \$desc, \$value);
my @results;
while ($sth->fetch()) {
push @results,
Bio::EnsEMBL::Attribute->new_fast({'code' => $code,
'name' => $name,
'description' => $desc,
'value' => $value});
}
return \@results;
}
1;