=head1 LICENSE Copyright (c) 1999-2013 The European Bioinformatics Institute and Genome Research Limited. All rights reserved. This software is distributed under a modified Apache license. For license details, please see http://www.ensembl.org/info/about/code_licence.html =head1 CONTACT Please email comments or questions to the public Ensembl developers list at . 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 package Bio::EnsEMBL::DBSQL::AttributeAdaptor; use strict; use warnings; use Bio::EnsEMBL::DBSQL::BaseAdaptor; use Bio::EnsEMBL::Attribute; 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() 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; } ## Single store sub store_on_Object { my ($self, $object_id, $attributes, $table, $type) = @_; my $db = $self->db(); if (!defined $type) { $type = $table; } my $sth = $self->prepare( "INSERT into " . $table. "_attrib ". "SET " . $type . "_id = ?, attrib_type_id = ?, ". "value = ? " ); 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_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; } ## 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)){ $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 ?"); $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_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; } ## Fetch methods sub fetch_all { throw("Use of method fetch_all not supported for attributes"); } 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; } # # _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 $sth1 = $self->prepare("INSERT IGNORE INTO attrib_type set code = ?, name = ?, " . "description = ?"); $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 = $sth1->{'mysql_insertid'}; 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;