Run perltidy on this, add some documentation
[bioperl-db.git] / lib / Bio / DB / BioSQL / BaseDriver.pm
blob4b2e9f56aae81699f94ea709ca07c1124f2a03c1
2 # BioPerl module for Bio::DB::BioSQL::BaseDriver
4 # Please direct questions and support issues to <bioperl-l@bioperl.org>
6 # Cared for by Hilmar Lapp <hlapp at gmx.net>
8 # Copyright Hilmar Lapp
10 # You may distribute this module under the same terms as perl itself
12 # (c) Hilmar Lapp, hlapp at gmx.net, 2002.
13 # (c) GNF, Genomics Institute of the Novartis Research Foundation, 2002.
15 # You may distribute this module under the same terms as perl itself.
16 # Refer to the Perl Artistic License (see the license accompanying this
17 # software package, or see http://www.perl.com/language/misc/Artistic.html)
18 # for the terms under which you may use, modify, and redistribute this module.
20 # THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
21 # WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
22 # MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
24 # POD documentation - main docs before the code
26 =head1 NAME
28 Bio::DB::BioSQL::BaseDriver - DESCRIPTION of Object
30 =head1 DESCRIPTION
32 This object contains the mapping of BioPerl classes to entities, as well as
33 the private methods that construct the INSERT, DELETE, UPDATE, and SELECT
34 SQL statements.
36 =head1 FEEDBACK
38 =head2 Mailing Lists
40 User feedback is an integral part of the evolution of this and other
41 Bioperl modules. Send your comments and suggestions preferably to
42 the Bioperl mailing list. Your participation is much appreciated.
44 bioperl-l@bioperl.org - General discussion
45 http://bioperl.org/wiki/Mailing_lists - About the mailing lists
47 =head2 Support
49 Please direct usage questions or support issues to the mailing list:
51 I<bioperl-l@bioperl.org>
53 rather than to the module maintainer directly. Many experienced and
54 reponsive experts will be able look at the problem and quickly
55 address it. Please include a thorough description of the problem
56 with code and data examples if at all possible.
58 =head2 Reporting Bugs
60 Report bugs to the Bioperl bug tracking system to help us keep track
61 of the bugs and their resolution. Bug reports can be submitted via
62 the web:
64 http://redmine.open-bio.org/projects/bioperl/
66 =head1 AUTHOR - Hilmar Lapp
68 Email hlapp at gmx.net
70 =head1 CONTRIBUTORS
72 Additional contributors names and emails here
74 =head1 APPENDIX
76 The rest of the documentation details each of the object methods.
77 Internal methods are usually preceded with a _
79 =cut
81 # Let the code begin...
83 package Bio::DB::BioSQL::BaseDriver;
84 use vars qw(@ISA);
85 use strict;
87 # Object preamble - inherits from Bio::Root::Root
89 use Bio::Root::Root;
90 use Bio::DB::DBD;
92 @ISA = qw(Bio::Root::Root Bio::DB::DBD);
95 # here goes our entire object-relational mapping
97 my %object_entity_map = (
98 "Bio::BioEntry" => "bioentry",
99 "Bio::PrimarySeqI" => "bioentry",
100 "Bio::DB::BioSQL::PrimarySeqAdaptor" => "bioentry",
101 "Bio::SeqI" => "bioentry",
102 "Bio::DB::BioSQL::SeqAdaptor" => "bioentry",
103 "Bio::IdentifiableI" => "bioentry",
104 "Bio::ClusterI" => "bioentry",
105 "Bio::DB::BioSQL::ClusterAdaptor" => "bioentry",
106 "Bio::DB::BioSQL::BiosequenceAdaptor" => "biosequence",
107 "Bio::SeqFeatureI" => "seqfeature",
108 "Bio::DB::BioSQL::SeqFeatureAdaptor" => "seqfeature",
109 "Bio::Species" => "taxon_name",
110 "Bio::DB::BioSQL::SpeciesAdaptor" => "taxon_name",
111 # TaxonNode is a hack: there is no such object, but we need it
112 # to distinguish between the node and the name table
113 "TaxonNode" => "taxon",
114 "Bio::LocationI" => "location",
115 "Bio::DB::BioSQL::LocationAdaptor" => "location",
116 "Bio::DB::BioSQL::BioNamespaceAdaptor" => "biodatabase",
117 "Bio::DB::Persistent::BioNamespace" => "biodatabase",
118 "Bio::Annotation::DBLink" => "dbxref",
119 "Bio::DB::BioSQL::DBLinkAdaptor" => "dbxref",
120 "Bio::Annotation::Comment" => "comment",
121 "Bio::DB::BioSQL::CommentAdaptor" => "comment",
122 "Bio::Annotation::Reference" => "reference",
123 "Bio::DB::BioSQL::ReferenceAdaptor" => "reference",
124 "Bio::Annotation::SimpleValue" => "term",
125 "Bio::DB::BioSQL::SimpleValueAdaptor" => "term",
126 "Bio::Annotation::OntologyTerm" => "term",
127 "Bio::Ontology::TermI" => "term",
128 "Bio::DB::BioSQL::TermAdaptor" => "term",
129 "Bio::Ontology::RelationshipI" => "term_relationship",
130 "Bio::DB::BioSQL::RelationshipAdaptor" => "term_relationship",
131 "Bio::Ontology::PathI" => "term_path",
132 "Bio::Ontology::Path" => "term_path",
133 "Bio::DB::BioSQL::PathAdaptor" => "term_path",
134 "Bio::Ontology::OntologyI" => "ontology",
135 "Bio::DB::BioSQL::OntologyAdaptor" => "ontology",
136 # TermSynonym is a hack - there is no such object
137 "TermSynonym" => "term_synonym",
139 my %association_entity_map = (
140 "bioentry" => {
141 "dbxref" => "bioentry_dbxref",
142 "reference" => "bioentry_reference",
143 "term" => "bioentry_qualifier_value",
144 "bioentry" => { "term" => "bioentry_relationship", }
146 "ontology" =>
147 { "term" => { "term" => { "term" => "term_relationship", }, }, },
148 "seqfeature" => {
149 "term" => "seqfeature_qualifier_value",
150 "dbxref" => "seqfeature_dbxref",
151 "reference" => undef,
152 "seqfeature" => { "term" => "seqfeature_relationship", }
154 "dbxref" => {
155 "bioentry" => "bioentry_dbxref",
156 "seqfeature" => "seqfeature_dbxref",
157 "term" => "dbxref_qualifier_value",
159 "reference" => {
160 "bioentry" => "bioentry_reference",
161 "seqfeature" => undef,
163 "term" => {
164 "bioentry" => "bioentry_qualifier_value",
165 "dbxref" => "term_dbxref",
166 "seqfeature" => "seqfeature_qualifier_value",
167 "term" => {
168 "term" => { "ontology" => "term_relationship", },
169 "ontology" => { "term" => "term_relationship", }
171 "ontology" => { "term" => { "term" => "term_relationship", } }
174 my %slot_attribute_map = (
175 "biodatabase" => {
176 "name" => "name",
177 "namespace" => "name",
178 "authority" => "authority",
180 "taxon_name" => {
181 "classification" => undef,
182 "common_name" => undef,
183 "ncbi_taxid" => "ncbi_taxon_id",
184 "binomial" => "name",
185 "variant" => undef,
186 # the following are hacks: there is no such thing on
187 # the object model. The sole reason they are here is so that you
188 # can set the physical column name of your taxon_name table.
189 # You MUST have these columns on the taxon node table, NOT the
190 # taxon name table.
191 "name_class" => "name_class",
192 "node_rank" => "node_rank",
193 "parent_taxon" => "parent_taxon_id",
195 "taxon" => {
196 "ncbi_taxid" => "ncbi_taxon_id",
197 # the following are hacks, see taxon_name mapping
198 "name_class" => "name_class",
199 "node_rank" => "node_rank",
200 "parent_taxon" => "parent_taxon_id",
202 "bioentry" => {
203 "display_id" => "name",
204 "primary_id" => "identifier",
205 "accession_number" => "accession",
206 "desc" => "description",
207 "description" => "description",
208 "version" => "version",
209 "division" => "division",
210 "bionamespace" => "biodatabase_id",
211 "namespace" => "biodatabase_id",
212 # these are for context-sensitive FK name resolution
213 "object" => "object_bioentry_id",
214 "subject" => "subject_bioentry_id",
215 # parent and child are for backwards compatibility
216 "parent" => "object_bioentry_id",
217 "child" => "subject_bioentry_id",
219 "bioentry_relationship" => {
220 "object" => "object_bioentry_id",
221 "subject" => "subject_bioentry_id",
222 "rank" => "rank",
223 # parent and child are for backwards compatibility
224 "parent" => "object_bioentry_id",
225 "child" => "subject_bioentry_id",
227 "biosequence" => {
228 "seq_version" => "version",
229 "length" => "length",
230 "seq" => "seq",
231 "alphabet" => "alphabet",
232 "primary_seq" => "bioentry_id",
233 # NOTE: change undef to the name of the CRC column to
234 # enable having CRC64s computed for sequences automatically,
235 # or set to undef to disable
236 "crc" => undef,
238 "dbxref" => {
239 "database" => "dbname",
240 "primary_id" => "accession",
241 "version" => "version",
242 "rank" => "=>{bioentry_dbxref,seqfeature_dbxref,term_dbxref}.rank",
244 "bioentry_dbxref" => { "rank" => "rank", },
245 "term_dbxref" => { "rank" => "rank", },
246 "reference" => {
247 "authors" => "authors",
248 "title" => "title",
249 "location" => "location",
250 "medline" => "dbxref_id",
251 "pubmed" => "dbxref_id",
252 "doc_id" => "crc",
253 "start" => "=>bioentry_reference.start",
254 "end" => "=>bioentry_reference.end",
255 "rank" => "=>bioentry_reference.rank",
257 "bioentry_reference" => {
258 "start" => "start_pos",
259 "end" => "end_pos",
260 "rank" => "rank",
262 "comment" => {
263 "text" => "comment_text",
264 "rank" => "rank",
265 "Bio::DB::BioSQL::SeqFeatureAdaptor" => undef,
267 "term" => {
268 "identifier" => "identifier",
269 "name" => "name",
270 "tagname" => "name",
271 "is_obsolete" => "is_obsolete",
272 "definition" => "definition",
273 "value" =>
274 "=>{bioentry_qualifier_value,seqfeature_qualifier_value}.value",
275 "rank" =>
276 "=>{bioentry_qualifier_value,seqfeature_qualifier_value}.rank",
277 "ontology" => "ontology_id",
278 # these are for context-sensitive FK name resolution
279 # term relationships:
280 "subject" => "subject_term_id",
281 "predicate" => "predicate_term_id",
282 "object" => "object_term_id",
283 # seqfeatures:
284 "primary_tag" => "type_term_id",
285 "source_tag" => "source_term_id",
287 # term_synonym is more a hack - it doesn't correspond to an object
288 # in bioperl, but this does let you specify your column naming
289 "term_synonym" => {
290 "synonym" => "synonym",
291 "term" => "term_id"
293 "term_relationship" => {
294 "subject" => "subject_term_id",
295 "predicate" => "predicate_term_id",
296 "object" => "object_term_id",
297 "ontology" => "ontology_id",
299 "term_path" => {
300 "distance" => "distance",
301 "subject" => "subject_term_id",
302 "predicate" => "predicate_term_id",
303 "object" => "object_term_id",
304 "ontology" => "ontology_id",
306 "ontology" => {
307 "name" => "name",
308 "definition" => "definition",
310 "bioentry_qualifier_value" => {
311 "value" => "value",
312 "rank" => "rank",
314 "seqfeature" => {
315 "display_name" => "display_name",
316 "rank" => "rank",
317 "primary_tag" => "type_term_id",
318 "source_tag" => "source_term_id",
319 "entire_seq" => "bioentry_id",
321 # these are for context-sensitive FK name resolution
322 "object" => "object_seqfeature_id",
323 "subject" => "subject_seqfeature_id",
325 # parent and child are for backwards compatibility
326 "parent" => "parent_seqfeature_id",
327 "child" => "child_seqfeature_id",
329 "seqfeature_dbxref" => { "rank" => "rank", },
330 "location" => {
331 "start" => "start_pos",
332 "end" => "end_pos",
333 "strand" => "strand",
334 "rank" => "rank",
336 "seqfeature_qualifier_value" => {
337 "value" => "value",
338 "rank" => "rank",
340 "seqfeature_relationship" => {
341 "object" => "object_seqfeature_id",
342 "subject" => "subject_seqfeature_id",
343 "rank" => "rank",
344 # parent and child are for backwards compatibility
345 "parent" => "parent_seqfeature_id",
346 "child" => "child_seqfeature_id",
349 my %dont_select_attrs = ( "biosequence.seq" => 1, );
351 =head2 new
353 Title : new
354 Usage : my $obj = Bio::DB::BioSQL::BaseDriver->new();
355 Function: Builds a new Bio::DB::BioSQL::BaseDriver object
356 Returns : an instance of Bio::DB::BioSQL::BaseDriver
357 Args :
359 =cut
361 sub new {
362 my ( $class, @args ) = @_;
364 my $self = $class->SUPER::new(@args);
366 # copy the static mapping tables into our private hashs
367 # you may then change individual mappings in your derived adaptor driver
368 $self->objrel_map( \%object_entity_map );
369 $self->slot_attribute_map( \%slot_attribute_map );
370 $self->not_select_attrs( \%dont_select_attrs );
371 $self->association_entity_map( \%association_entity_map );
373 return $self;
376 =head2 prepare_delete_sth
378 Title : prepare_delete_sth
379 Usage :
380 Function: Creates a prepared statement with one placeholder variable
381 suitable to delete one row from the respective table the
382 given class maps to.
384 The method may throw an exception, or the database handle
385 methods involved may throw an exception.
387 Example :
388 Returns : A DBI statement handle for a prepared statement with one placeholder
389 Args : The calling adaptor (basically, it needs to implement dbh()).
390 Optionally, additional arguments.
392 =cut
394 sub prepare_delete_sth {
395 my ( $self, $adp ) = @_;
396 # default is a simple DELETE statement
398 # we need the table name and the name of the primary key
399 my $tbl = $self->table_name($adp);
400 my $pkname = $self->primary_key_name($tbl);
402 # straightforward SQL:
403 my $sql = "DELETE FROM $tbl WHERE $pkname = ?";
404 $adp->debug("preparing DELETE statement: $sql\n");
405 my $sth = $self->prepare( $adp->dbh(), $sql );
407 # done
408 return $sth;
411 =head2 prepare_findbypk_sth
413 Title : prepare_findbypk_sth
414 Usage :
415 Function: Prepares and returns a DBI statement handle with one placeholder for
416 the primary key. The statement is expected to return the primary key
417 as the first and then as many columns as
418 $adp->get_persistent_slots() returns, and in that order.
420 Example :
421 Returns : A DBI prepared statement handle with one placeholder
422 Args : The Bio::DB::BioSQL::BasePersistenceAdaptor derived object
423 (basically, it needs to implement dbh() and get_persistent_slots()).
424 A reference to an array of foreign key slots (class names).
426 =cut
428 sub prepare_findbypk_sth {
429 my ( $self, $adp, $fkslots ) = @_;
431 # get table name and the primary key name
432 my $table = $self->table_name($adp);
433 my $pkname = $self->primary_key_name($table);
435 # gather attributes
436 my @attrs = $self->_build_select_list( $adp, $fkslots );
438 # create the sql statement
439 my $sql
440 = "SELECT " . join( ", ", @attrs ) . " FROM $table WHERE $pkname = ?";
441 $adp->debug("preparing PK select statement: $sql\n");
443 # prepare statement and return
444 return $self->prepare( $adp->dbh(), $sql );
447 =head2 prepare_findbyuk_sth
449 Title : prepare_findbyuk_sth
450 Usage :
451 Function: Prepares and returns a DBI SELECT statement handle with as many
452 placeholders as necessary for the given unique key.
454 The statement is expected to return the primary key as the first and
455 then as many columns as $adp->get_persistent_slots() returns, and in
456 that order.
457 Example :
458 Returns : A DBI prepared statement handle with as many placeholders as
459 necessary for the given unique key
460 Args : The calling Bio::DB::BioSQL::BasePersistenceAdaptor derived object
461 (basically, it needs to implement dbh() and get_persistent_slots()).
462 A reference to a hash with the names of the object''s slots in the
463 unique key as keys and their values as values.
464 A reference to an array of foreign key objects or slots
465 (class names if slot).
467 =cut
469 sub prepare_findbyuk_sth {
470 my ( $self, $adp, $ukval_h, $fkslots ) = @_;
472 # get the slots for which we need columns
473 my @slots = $adp->get_persistent_slots();
475 # get the slot/attribute map
476 my $table = $self->table_name($adp);
477 my $slotmap = $self->slot_attribute_map($table);
479 # SELECT columns
480 my @attrs = $self->_build_select_list( $adp, $fkslots );
482 # WHERE clause constraints
483 my @cattrs = ();
484 foreach ( keys %$ukval_h ) {
485 my $col;
486 if ( exists( $slotmap->{$_} ) ) {
487 $col = $slotmap->{$_};
488 } else {
490 # try it as a foreign key
491 $col = $self->foreign_key_name($_);
493 push( @cattrs, $col || "NULL" );
494 if ( ! $col ) {
495 $self->warn( "slot $_ is in unique key, but can't be mapped to "
496 . "an entity column: you won't find anything" );
500 # create the sql statement
501 my $sql
502 = "SELECT "
503 . join( ", ", @attrs )
504 . " FROM $table WHERE "
505 . join( " AND ", map { "$_ = ?"; } @cattrs );
506 $adp->debug("preparing UK select statement: $sql\n");
508 # prepare statement and return
509 return $self->prepare( $adp->dbh(), $sql );
512 =head2 prepare_insert_association_sth
514 Title : prepare_insert_association_sth
515 Usage :
516 Function: Prepares a DBI statement handle suitable for inserting the
517 association between the two entities that correspond to the
518 given objects.
519 Example :
520 Returns : the DBI statement handle
521 Args : The calling adaptor.
522 Named parameters. Currently recognized are:
523 -objs a reference to an array of objects to be
524 associated with each other
525 -values a reference to a hash the keys of which are
526 column names and the values are values of
527 columns other than the ones for foreign keys to
528 the entities to be associated
529 -contexts optional; if given it denotes a reference
530 to an array of context keys (strings), which
531 allow the foreign key name to be determined
532 through the association map rather than through
533 foreign_key_name(). This may be necessary if
534 more than one object of the same type takes
535 part in the association. The array must be in
536 the same order as -objs, and have the same
537 number of elements. Put undef for objects
538 for which there are no multiple contexts.
540 Caveats: Make sure you *always* give the objects to be associated in the
541 same order.
543 =cut
545 sub prepare_insert_association_sth {
546 my ( $self, $adp, @args ) = @_;
547 my ($i);
549 # get arguments
550 my ( $objs, $values, $contexts )
551 = $self->_rearrange( [qw(OBJS VALUES CONTEXTS)], @args );
553 # obtain column map for non-fk columns
554 my $table = $self->association_table_name($objs);
555 if ( ! $table ) {
556 $self->throw(
557 "no object-relational map for association between "
558 . "classes ("
559 . join(
560 ",",
561 map {
562 $_->isa("Bio::DB::PersistentObjectI")
563 ? ref( $_->obj() )
564 : ref($_);
565 } @$objs
567 . ")"
570 my $columnmap = $self->slot_attribute_map($table);
571 my $attr;
572 my @attrs = ();
573 my @plchldrs = ();
575 # first, gather the foreign key names
576 $i = 0;
577 while ( $i < @$objs ) {
578 my $fktable = $self->table_name( $objs->[$i] );
579 if ( ! $fktable ) {
580 $self->throw(
581 "no object-relational map for class " . ref( $objs->[$i] ) );
583 if ( $contexts && $contexts->[$i] ) {
584 $attr = $columnmap->{ $contexts->[$i] };
585 } else {
586 $attr = $self->foreign_key_name( $objs->[$i] );
588 if ( ! $attr ) {
589 $self->throw( "unable to determine column for FK to class "
590 . ref( $objs->[$i] ) );
592 push( @attrs, $attr );
593 push( @plchldrs, "?" );
594 $i++;
597 # now add the columns for values if any
598 if ($values) {
599 foreach my $colkey ( keys %$values ) {
600 $self->throw("unmapped association column $colkey")
601 unless exists( $columnmap->{$colkey} );
602 $attr = $columnmap->{$colkey};
603 if ($attr) {
604 push( @attrs, $attr );
605 push( @plchldrs, "?" );
610 # construct SQL straightforwardly
611 my $sql
612 = "INSERT INTO $table ("
613 . join( ", ", @attrs )
614 . ") VALUES ("
615 . join( ", ", @plchldrs ) . ")";
616 $adp->debug("preparing INSERT statement: $sql\n");
618 # prepare sth and return
619 return $self->prepare( $adp->dbh(), $sql );
622 =head2 prepare_delete_association_sth
624 Title : prepare_delete_association_sth
625 Usage :
626 Function: Prepares a DBI statement handle suitable for deleting the
627 association between the two entities that correspond to the
628 given objects.
629 Example :
630 Returns : the DBI statement handle
631 Args : The calling adaptor.
632 Named parameters. Currently recognized are:
633 -objs a reference to an array of objects the association
634 between which is to be deleted
635 -values a reference to a hash the keys of which are
636 column names and the values are values of
637 columns other than the ones for foreign keys to
638 the entities to be associated
639 -contexts optional; if given it denotes a reference
640 to an array of context keys (strings), which
641 allow the foreign key name to be determined
642 through the association map rather than through
643 foreign_key_name(). This may be necessary if
644 more than one object of the same type takes
645 part in the association. The array must be in
646 the same order as -objs, and have the same
647 number of elements. Put undef for objects
648 for which there are no multiple contexts.
650 Caveats: Make sure you *always* give the objects to be associated in the
651 same order.
653 =cut
655 sub prepare_delete_association_sth {
656 my ( $self, $adp, @args ) = @_;
657 my ($i);
659 # get arguments
660 my ( $objs, $values, $contexts )
661 = $self->_rearrange( [qw(OBJS VALUES CONTEXTS)], @args );
663 # obtain column map for non-fk columns
664 my $table = $self->association_table_name($objs);
665 if ( ! $table ) {
666 $self->throw(
667 "no object-relational map for association between "
668 . "classes ("
669 . join(
670 ",",
671 map {
672 ref($_)
673 ? ( $_->isa("Bio::DB::PersistentObjectI")
674 ? ref( $_->obj() )
675 : ref($_) )
676 : $_;
677 } @$objs
679 . ")"
682 my $columnmap = $self->slot_attribute_map($table);
683 my $attr;
684 my @attrs = ();
686 # first, gather the foreign key names
687 $i = 0;
688 while ( $i < @$objs ) {
689 my $obj = $objs->[$i];
690 if ( ref($obj) && $obj->isa("Bio::DB::PersistentObjectI") ) {
691 my $fktable = $self->table_name($obj);
692 if ( ! $fktable ) {
693 $self->throw(
694 "no object-relational map for class " . ref($obj) );
696 if ( $contexts && $contexts->[$i] ) {
697 $attr = $columnmap->{ $contexts->[$i] };
698 } else {
699 $attr = $self->foreign_key_name($obj);
701 if ( ! $attr ) {
702 $self->throw( "unable to determine column for FK to class "
703 . ref($obj) );
705 push( @attrs, $attr );
707 $i++;
710 # now add the columns for values if any
711 if ($values) {
712 foreach my $colkey ( keys %$values ) {
713 $self->throw("unmapped association column $colkey")
714 unless exists( $columnmap->{$colkey} );
715 $attr = $columnmap->{$colkey};
716 push( @attrs, $attr ) if $attr;
720 # construct SQL straightforwardly
721 my $sql = "DELETE FROM $table WHERE "
722 . join( " AND ", map { $_ . " = ?"; } @attrs );
723 $adp->debug("preparing DELETE ASSOC statement: $sql\n");
725 # prepare sth and return
726 return $self->prepare( $adp->dbh(), $sql );
729 =head2 prepare_delete_query_sth
731 Title : prepare_delete_query_sth
732 Usage :
733 Function: Prepares a DBI statement handle suitable for deleting rows
734 from a table that match a number of attributes.
735 Example :
736 Returns : the DBI statement handle
737 Args : The calling adaptor.
739 Named parameters. Currently recognized are:
741 -fkobjs optional; a reference to an array of foreign
742 key objects by which to constrain; this is
743 complementary to -values
745 -contexts optional; if given it denotes a reference
746 to an array of context keys (strings), which
747 allow the foreign key name to be determined
748 through the association map rather than through
749 foreign_key_name(). This may be necessary if
750 an entity has more than one foreign key to the
751 same entity. The array must be in the same
752 order as -fkobjs, and have the same number of
753 elements. Put undef for objects for which there
754 are no multiple contexts.
756 -values optional; a reference to a hash the keys of
757 which are attribute names by which to constrain
758 the query
760 =cut
762 sub prepare_delete_query_sth {
763 my ( $self, $adp, @args ) = @_;
764 my ($i);
766 # get arguments
767 my ( $fkobjs, $values, $contexts )
768 = $self->_rearrange( [qw(FKOBJS VALUES CONTEXTS)], @args );
770 # obtain column map for attributes
771 my $table = $self->table_name($adp);
772 my $columnmap = $self->slot_attribute_map($table);
773 my @attrs = ();
774 my $attr;
776 # add the query constraint columns for foreign key columns if any
777 if ( $fkobjs && @$fkobjs ) {
778 foreach my $obj (@$fkobjs) {
779 my $fktable = $self->table_name($obj);
780 if ( ! $fktable ) {
781 $self->throw( "no object-relational map for class "
782 . ( ref($obj) ? ref($obj) : $obj ) );
784 if ( $contexts && $contexts->[$i] ) {
785 $attr = $columnmap->{ $contexts->[$i] };
786 } else {
787 $attr = $self->foreign_key_name($obj);
789 if ( ! $attr ) {
790 $self->throw( "unable to determine column for FK to class "
791 . ( ref($obj) ? ref($obj) : $obj ) );
793 push( @attrs, $attr );
797 # add any other query constraint columns
798 if ($values) {
799 foreach my $colkey ( keys %$values ) {
800 $self->throw("unmapped association column $colkey")
801 unless exists( $columnmap->{$colkey} );
802 $attr = $columnmap->{$colkey};
803 push( @attrs, $attr ) if $attr;
807 # construct SQL straightforwardly
808 my $sql = "DELETE FROM $table";
809 if (@attrs) {
810 $sql .= " WHERE " . join( " AND ", map { $_ . " = ?"; } @attrs );
812 $adp->debug("preparing DELETE QUERY statement: $sql\n");
814 # prepare sth and return
815 return $self->prepare( $adp->dbh(), $sql );
818 =head2 prepare_insert_sth
820 Title : prepare_insert_sth
821 Usage :
822 Function: Prepares a DBI statement handles suitable for inserting
823 a row (as values of the slots of an object) into a table.
824 Example :
825 Returns : the DBI statement handle
826 Args : the calling adaptor (a Bio::DB::PersistenceAdaptorI object)
827 a reference to an array of object slot names
828 a reference to an array of foreign key objects (optional)
830 =cut
832 sub prepare_insert_sth {
833 my ( $self, $adp, $slots, $fkobjs ) = @_;
835 # obtain table and object slot map
836 my $table = $self->table_name($adp);
837 my $slotmap = $self->slot_attribute_map($table);
838 $self->throw("no slot/attribute map for table $table") unless $slotmap;
840 # construct INSERT statement as straightforward SQL with placeholders
841 my @attrs = ();
842 my @plchlds = ();
843 foreach my $slot (@$slots) {
844 if ( ! exists( $slotmap->{$slot} ) ) {
845 $self->throw("no mapping for slot $slot in slot-attribute map");
848 # we don't add a column nor a placeholder for unmapped slots
849 if ( $slotmap->{$slot}
850 && ( substr( $slotmap->{$slot}, 0, 2 ) ne '=>' ) ) {
851 push( @attrs, $slotmap->{$slot} );
852 push( @plchlds, "?" );
856 # foreign keys
857 if ($fkobjs) {
858 foreach (@$fkobjs) {
859 my $fkattr = $self->foreign_key_name($_);
860 push( @attrs, $fkattr );
861 push( @plchlds, "?" );
864 my $sql
865 = "INSERT INTO "
866 . $table . " ("
867 . join( ", ", @attrs )
868 . ") VALUES ("
869 . join( ", ", @plchlds ) . ")";
870 $adp->debug("preparing INSERT statement: $sql\n");
871 return $self->prepare( $adp->dbh, $sql );
874 =head2 prepare_update_sth
876 Title : prepare_update_sth
877 Usage :
878 Function: Prepares a DBI statement handle suitable for updating
879 a row in a table where the row is identified by its
880 primary key.
881 Example :
882 Returns : the DBI statement handle
883 Args : the calling adaptor (a Bio::DB::PersistenceAdaptorI object)
884 a reference to an array of object slot names
885 a reference to an array of foreign key objects (optional)
887 =cut
889 sub prepare_update_sth {
890 my ( $self, $adp, $slots, $fkobjs ) = @_;
892 # obtain the table name and corresponding slot map
893 my $table = $self->table_name($adp);
894 my $slotmap = $self->slot_attribute_map($table);
895 $self->throw("no slot/attribute map for table $table") unless $slotmap;
897 # construct UPDATE statement as straightforward SQL
898 my @attrs = ();
899 foreach my $slot (@$slots) {
900 if ( ! exists( $slotmap->{$slot} ) ) {
901 $self->throw("no mapping for slot $_ in slot-attribute map");
904 # we don't add a column nor a placeholder for unmapped slots
905 if ( $slotmap->{$slot}
906 && ( substr( $slotmap->{$slot}, 0, 2 ) ne '=>' ) ) {
907 push( @attrs, $slotmap->{$slot} );
911 # foreign keys
912 if ($fkobjs) {
913 foreach (@$fkobjs) {
914 my $fkattr = $self->foreign_key_name($_);
915 push( @attrs, $fkattr );
918 my $ifnull = $adp->dbcontext->dbi->ifnull_sqlfunc();
919 my $sql
920 = "UPDATE $table SET "
921 . join( ", ", map { "$_ = $ifnull\(?,$_\)"; } @attrs )
922 . " WHERE "
923 . $self->primary_key_name($table) . " = ?";
924 $adp->debug("preparing UPDATE statement: $sql\n");
925 return $self->prepare( $adp->dbh(), $sql );
928 =head2 cascade_delete
930 Title : cascade_delete
931 Usage :
932 Function: Removes all persistent objects dependent from the given persistent
933 object from the database (foreign key integrity).
935 This implementation assumes that the underlying schema and RDBMS
936 support cascading deletes, and hence does nothing other than
937 returning TRUE.
938 Example :
939 Returns : TRUE on success, and FALSE otherwise
940 Args : The DBContextI implementing object for the database.
941 The object for which the dependent rows shall be deleted.
942 Optionally, additional (named) arguments.
944 =cut
946 sub cascade_delete {
948 # our default assumption is that the RDBMS does support cascading deletes
949 return 1;
952 =head2 insert_object
954 Title : insert_object
955 Usage :
956 Function:
957 Example :
958 Returns : The primary key of the newly inserted record.
959 Args : A Bio::DB::BioSQL::BasePersistenceAdaptor derived object
960 (basically, it needs to implement dbh(), sth($key, $sth),
961 dbcontext(), and get_persistent_slots()).
962 The object to be inserted.
963 A reference to an array of foreign key objects; if any of those
964 foreign key values is NULL (some foreign keys may be nullable),
965 then give the class name.
967 =cut
969 sub insert_object {
970 my ( $self, $adp, $obj, $fkobjs ) = @_;
972 # obtain the object's slots to be serialized
973 my @slots = $adp->get_persistent_slots($obj);
975 # get the INSERT statement
976 # is it cached?
977 my $cache_key = 'INSERT ' . ref($obj) . ' ' . join( ';', @slots );
978 my $sth
979 = $self->get_sth( $adp, $obj, $fkobjs, $cache_key, 'insert_object' );
981 # we need the slot map regardless of whether we need to construct the
982 # SQL or not, because we need to know which slots do not map to a column
983 # (indicated by them being mapped to undef)
984 my $table = $self->table_name($adp);
985 my $slotmap = $self->slot_attribute_map($table);
986 $self->throw("no slot/attribute map for table $table") unless $slotmap;
988 # we'll need the db handle in any case
989 my $dbh = $adp->dbh();
991 # if not cached, create SQL and prepare statement
992 if ( ! $sth ) {
993 $sth = $self->prepare_insert_sth( $adp, \@slots, $fkobjs );
995 # and cache
996 $adp->sth( $cache_key, $sth );
998 # and give interceptors a chance to do their work
999 $sth = $self->get_sth( $adp, $obj, $fkobjs, $cache_key,
1000 'insert_object' );
1003 # the implementation here is a post-insert primary-key retrieval, so
1004 # just go ahead and bind the attributes, no a-priori pk retrieval
1005 my $slotvals = $adp->get_persistent_slot_values( $obj, $fkobjs );
1006 if ( @$slotvals != @slots ) {
1007 $self->throw( "number of slots must equal the number of values "
1008 . "(slots: "
1009 . join( ";", @slots )
1010 . ") (values: \""
1011 . join( "\";\"", @$slotvals )
1012 . ")" );
1014 my $i = 0; # slots and slot values index
1015 my $j = 1; # column index
1016 while ( $i < @slots ) {
1017 if ( $slotmap->{ $slots[$i] }
1018 && ( substr( $slotmap->{ $slots[$i] }, 0, 2 ) ne '=>' ) ) {
1019 if ( $adp->verbose > 0 ) {
1020 $adp->debug(
1021 substr( ref($adp), rindex( ref($adp), "::" ) + 2 )
1022 . "::insert: "
1023 . "binding column $j to \"",
1024 $slotvals->[$i],
1025 "\" ($slots[$i])\n"
1028 $self->bind_param( $sth, $j, $slotvals->[$i] );
1029 $j++;
1031 $i++;
1034 # bind foreign key values
1035 if ($fkobjs) {
1036 foreach my $o (@$fkobjs) {
1038 # If it's an object, the value to bind is the primary key.
1039 # Otherwise bind undef.
1040 my $fk = $o && ref($o) ? $o->primary_key() : undef;
1041 if ( $adp->verbose > 0 ) {
1042 $adp->debug(
1043 substr( ref($adp), rindex( ref($adp), "::" ) + 2 )
1044 . "::insert: "
1045 . "binding column $j to \"",
1046 $fk,
1047 "\" (FK to ",
1048 ( $o
1049 ? ( ref($o) ? ref( $o->obj() ) : $o )
1050 : "<unknown>"
1052 ")\n"
1055 $self->bind_param( $sth, $j, $fk );
1056 $j++;
1060 # execute
1061 my $rv = $sth->execute();
1062 my $pk;
1064 # Note: $rv may be 0E0 (evaluates to TRUE as a string) to indicate
1065 # success, but zero rows affected, which means no row was inserted.
1066 # This may be (hopefully will be) due to an RDBMS having internally
1067 # (by means of triggers [Oracle, Pg] or rules [Pg]) encapsulated and
1068 # caught the already-exists condition.
1069 if ( $rv && ( $rv != 0 ) ) {
1071 # get the primary key that was just inserted
1072 $pk = $adp->dbcontext()->dbi()
1073 ->last_id_value( $dbh, $self->sequence_name($table) );
1074 } elsif ( ! $rv ) { # note this is *not* equivalent to $rv == 0 !
1075 # the statement failed
1076 $self->report_execute_failure(
1077 -sth => $sth,
1078 -adaptor => $adp,
1079 -op => 'insert',
1080 -vals => $slotvals,
1081 -fkobjs => $fkobjs
1085 # done, return
1086 return $pk;
1089 =head2 update_object
1091 Title : update_object
1092 Usage :
1093 Function:
1094 Example :
1095 Returns : The number of updated rows
1096 Args : A Bio::DB::BioSQL::BasePersistenceAdaptor derived object
1097 (basically, it needs to implement dbh(), sth($key, $sth),
1098 dbcontext(), and get_persistent_slots()).
1099 The object to be updated.
1100 A reference to an array of foreign key objects; if any of those
1101 foreign key values is NULL (some foreign keys may be nullable),
1102 then give the class name.
1104 =cut
1106 sub update_object {
1107 my ( $self, $adp, $obj, $fkobjs ) = @_;
1109 # obtain the object's slots to be serialized
1110 my @slots = $adp->get_persistent_slots($obj);
1112 # get the UPDATE statement
1113 # is it cached?
1114 my $cache_key = 'UPDATE ' . ref($adp) . ' ' . join( ';', @slots );
1115 my $sth
1116 = $self->get_sth( $adp, $obj, $fkobjs, $cache_key, 'update_object' );
1118 # we need the slot map regardless of whether we need to construct the
1119 # SQL or not, because we need to know which slots do not map to a column
1120 # (indicated by them being mapped to undef)
1121 my $table = $self->table_name($adp);
1122 my $slotmap = $self->slot_attribute_map($table);
1123 $self->throw("no slot/attribute map for table $table") unless $slotmap;
1125 # if not cached, create SQL and prepare statement
1126 if ( ! $sth ) {
1127 $sth = $self->prepare_update_sth( $adp, \@slots, $fkobjs );
1129 # and cache
1130 $adp->sth( $cache_key, $sth );
1132 # and give interceptors a chance to do their work
1133 $sth = $self->get_sth( $adp, $obj, $fkobjs, $cache_key,
1134 'update_object' );
1137 # bind paramater values
1138 my $slotvals = $adp->get_persistent_slot_values( $obj, $fkobjs );
1139 if ( @$slotvals != @slots ) {
1140 $self->throw("number of slots must equal the number of values");
1142 my $i = 0; # slots and slot values index
1143 my $j = 1; # column index
1144 while ( $i < @slots ) {
1145 if ( $slotmap->{ $slots[$i] }
1146 && ( substr( $slotmap->{ $slots[$i] }, 0, 2 ) ne '=>' ) ) {
1147 if ( $adp->verbose > 0 ) {
1148 $adp->debug(
1149 sprintf(
1150 "%s::update: binding column %d to \"%s\"(%s)\n",
1151 substr( ref($adp), rindex( ref($adp), "::" ) + 2 ),
1153 $slotvals->[$i] || '',
1154 ( $slots[$i] )
1158 $self->bind_param( $sth, $j, $slotvals->[$i] );
1159 $j++;
1161 $i++;
1164 # bind foreign key values
1165 if ($fkobjs) {
1166 foreach my $o (@$fkobjs) {
1168 # If it's an object, the value to bind is the primary key. If it's
1169 # numeric, the value is the number. Otherwise bind undef.
1170 my $fk
1171 = ref($o) ? $o->primary_key()
1172 : $o =~ /^\d+$/ ? $o
1173 : undef;
1174 if ( $adp->verbose > 0 ) {
1175 $adp->debug(
1176 substr( ref($adp), rindex( ref($adp), "::" ) + 2 )
1177 . "::update: "
1178 . "binding column $j to \"$fk\" (FK to "
1179 . $self->table_name($o)
1180 . ")\n" );
1182 $self->bind_param( $sth, $j, $fk );
1183 $j++;
1187 # bind the primary key (which is in the WHERE clause and hence the last)
1188 $self->bind_param( $sth, $j, $obj->primary_key() );
1190 # execute
1191 my $rv = $sth->execute();
1192 if ( ! $rv ) {
1193 $self->report_execute_failure(
1194 -sth => $sth,
1195 -adaptor => $adp,
1196 -op => 'update',
1197 -vals => $slotvals,
1198 -fkobjs => $fkobjs
1202 # done, return
1203 return $rv;
1206 =head2 get_sth
1208 Title : get_sth
1209 Usage :
1210 Function: Retrieves the (prepared) statement handle to bind
1211 parameters for and to execute for the given operation.
1213 By default this will use the supplied key to retrieve the
1214 statement from the cache.
1216 This method is here to provide an opportunity for
1217 inheriting drivers to intercept the cached statement
1218 retrieval in order to on-the-fly redirect the statement
1219 execution to use a different statement than it would have
1220 used by default.
1222 This method may return undef if for instance there is no
1223 appropriate statement handle in the cache. Returning undef
1224 will trigger the calling method to construct a statement
1225 from scratch.
1227 Example :
1228 Returns : a prepared statement handle if one is exists for the query,
1229 and undef otherwise
1230 Args : - the calling adaptor (a Bio::DB::BioSQL::BasePersistenceAdaptor
1231 derived object
1232 - the object for the persistence operation
1233 - a reference to an array of foreign key objects; if any of
1234 those foreign key values is NULL then the class name
1235 - the key to the cache of the adaptor
1236 - the operation requesting a cache key (a scalar basically
1237 representing the name of the method)
1239 =cut
1241 sub get_sth {
1242 my ( $self, $adp, $obj, $fkobjs, $key, $op ) = @_;
1244 return $adp->sth($key);
1247 =head2 translate_query
1249 Title : translate_query
1250 Usage :
1251 Function: Translates the given query as represented by the query object
1252 from objects and class names and slot names to tables and column
1253 names.
1254 Example :
1255 Returns : An object of the same class as the input query, but representing
1256 the translated query, and also with the SELECT fields properly set
1257 to facilitate object construction.
1258 Args : The calling adaptor.
1259 The query as a Bio::DB::Query::BioQuery or derived object.
1260 A reference to an array of foreign key objects.
1262 =cut
1264 sub translate_query {
1265 my ( $self, $adp, $query, $fkobjs ) = @_;
1267 # the query object can itself translate the datacollections and
1268 # slot names to column names (all it needs is a obj-rel mapper, which
1269 # is us)
1270 my %entitymap = ();
1271 my $tquery = $query->translate_query( $self, \%entitymap );
1273 # build the SELECT list
1274 my @selattrs = $self->_build_select_list( $adp, $fkobjs, \%entitymap );
1276 # set as the SELECT elements of the query
1277 $tquery->selectelts( \@selattrs );
1279 # done
1280 return $tquery;
1283 =head2 _build_select_list
1285 Title : _build_select_list
1286 Usage :
1287 Function: Builds and returns the select list for an object query. The list
1288 contains those columns, in the right order, that are necessary to
1289 populate the object.
1290 Example :
1291 Returns : An array of strings (column names, not prefixed)
1292 Args : The calling persistence adaptor.
1293 A reference to an array of foreign key entities (objects, class
1294 names, or adaptors) the object must attach.
1295 A reference to a hash table mapping entity names to aliases (if
1296 omitted, aliases will not be used, and SELECT columns can only be
1297 from one table)
1299 =cut
1301 sub _build_select_list {
1302 my ( $self, $adp, $fkobjs, $entitymap ) = @_;
1304 # get the persistent slots
1305 my @slots = $adp->get_persistent_slots();
1307 # get the slot/attribute map
1308 my $table = $self->table_name($adp);
1309 my $slotmap = $self->slot_attribute_map();
1311 # get the map of columns excluded from SELECTs
1312 my $dont_select_attrs = $self->not_select_attrs();
1314 # default the entity-alias map if not provided
1315 if ( ! $entitymap ) {
1316 $entitymap = {};
1317 $entitymap->{$table} = [$table];
1320 # Alias for the table. We'll use the first one if the table is in the
1321 # FROM list with different aliases. Also note that the alias may come
1322 # with context, which we need to strip off.
1323 my ($alias) = split( /::/, $entitymap->{$table}->[0] );
1325 # get the primary key name
1326 my $pkname = $self->primary_key_name($table);
1328 # SELECT columns
1329 my @attrs = ( $alias . "." . $pkname );
1330 foreach (@slots) {
1331 $self->throw("no mapping for slot $_ in slot-attribute map")
1332 if ! exists( $slotmap->{$table}->{$_} );
1333 my $attr = $slotmap->{$table}->{$_};
1334 my $tbl = $table;
1336 # is this attribute actually mapped to one or more other tables?
1337 if ( $attr && ( substr( $attr, 0, 2 ) eq '=>' ) ) {
1339 # yes, figure out to which attribute
1340 ( $tbl, $attr ) = split( /\./, substr( $attr, 2 ) );
1342 # is this mapped to multiple tables?
1343 if ( $tbl =~ /^\{(.*)\}$/ ) {
1345 # yes, figure out which one we have in the entity map
1346 foreach ( split( /[,\s]+/, $1 ) ) {
1348 # we just grab the first one
1349 if ( $entitymap->{$_} ) {
1350 $tbl = $_;
1351 last;
1355 $attr = $slotmap->{$tbl}->{$attr};
1357 if ( ( ! $attr )
1358 || ( ! $entitymap->{$tbl} )
1359 || $dont_select_attrs->{ $tbl . "." . $attr } ) {
1360 push( @attrs, "NULL" );
1361 } else {
1363 # same caveats as for the alias of the 'main' table
1364 my ($tblalias) = split( /::/, $entitymap->{$tbl}->[0] );
1365 push( @attrs, $tblalias . "." . $attr );
1369 # add foreign key attributes
1370 if ($fkobjs) {
1371 foreach (@$fkobjs) {
1372 my $fkattr = $self->foreign_key_name($_);
1373 $self->throw("no mapping for foreign key to $_") unless $fkattr;
1374 push( @attrs, $alias . "." . $fkattr );
1377 return @attrs;
1380 =head2 table_name
1382 Title : table_name
1383 Usage :
1384 Function: Obtain the name of the table in the relational schema
1385 corresponding to the given class name, object, or
1386 persistence adaptor.
1388 This implementation uses a object-relational hash map keyed
1389 by class to obtain the table name.
1391 Example :
1392 Returns : the name of the table (a string), or undef if the table cannot be
1393 determined
1394 Args : The referenced object, class name, or the persistence
1395 adaptor for it.
1397 =cut
1399 sub table_name {
1400 my ( $self, $obj ) = @_;
1402 # if this is an array ref, the caller is asking for an association table
1403 if ( ref($obj) && ( ref($obj) eq "ARRAY" ) ) {
1404 return $self->association_table_name($obj);
1407 # directly mapped?
1408 my $objrel_map = $self->objrel_map();
1409 my $tbl = $objrel_map->{ ref($obj) || $obj };
1410 if ( ! $tbl ) {
1412 # if not, and it's an object
1413 if ( ref($obj) ) {
1415 # if it's a persistent object, see whether the adaptor is mapped
1416 if ( $obj->isa("Bio::DB::PersistentObjectI") ) {
1417 $tbl = $objrel_map->{ ref( $obj->adaptor() ) };
1420 # if still no success, and it's not an adaptor, see which key it
1421 # implements
1422 if ( ! ( $tbl || $obj->isa("Bio::DB::PersistenceAdaptorI") ) ) {
1423 my (@classes) = grep { $obj->isa($_); } keys %$objrel_map;
1424 if (@classes) {
1425 @classes
1426 = &_order_classes_by_inheritance( $obj, @classes );
1427 $tbl = $objrel_map->{ $classes[0] };
1430 } else {
1432 # it's not an object
1434 # look up by `last name' only, provided that maps uniquely
1435 my @class = grep { /(^|::)$obj$/; } keys %$objrel_map;
1436 $tbl = $objrel_map->{ $class[0] } if ( @class == 1 );
1437 if ( ! $tbl ) {
1439 # We may have a context appended. Strip the last component
1440 # and try to start over.
1442 # Well, would be nice if we could do that. However, currently
1443 # context is appended with a '::' separator, so we can't tell
1444 # right away whether we'd be stripping off context or the module
1445 # from the path. In the absence of a method to determine what is
1446 # context and what is not, we can't go this route.
1448 #@class = split(/::/, $obj);
1449 #pop(@class);
1450 #$tbl = $self->table_name(join('::', @class)) if @class;
1454 return $tbl;
1457 sub _order_classes_by_inheritance {
1458 my ( $obj, @classes ) = @_;
1459 my $class = ref($obj) || $obj;
1460 my @sorted = ();
1462 # recursion termination condition: an array of one or less elements
1463 # is sorted already
1464 return @classes if @classes <= 1;
1466 # if there is a class equal to the class by which to order, that one
1467 # moves to the top
1468 my ($i) = grep { $classes[$_] eq $class; } ( 0 .. @classes - 1 );
1469 if ( defined($i) ) {
1470 splice( @classes, $i, 1 );
1471 push( @sorted, $class );
1474 # try to sort the rest
1475 my $aryname = "${class}::ISA"; # this is a soft reference
1476 # hence, allow soft refs
1477 no strict "refs";
1478 my @ancestors = @$aryname;
1480 # and disallow again
1481 use strict "refs";
1483 # now loop over all ancestors in the order they are in the list of
1484 # ancestors to sort the array
1485 foreach my $ancestor (@ancestors) {
1486 my @res = &_order_classes_by_inheritance( $ancestor, @classes );
1487 if (@res) {
1489 # remove those that are sorted and add to the list of sorted
1490 push( @sorted, @res );
1491 for ( $i = 0; $i < @res; $i++ ) {
1492 @classes = grep { $_ ne $res[$i]; } @classes;
1495 last unless @classes > 0;
1497 return @sorted;
1500 =head2 association_table_name
1502 Title : association_table_name
1503 Usage :
1504 Function: Obtain the name of the table in the relational schema
1505 corresponding to the association of entities as represented
1506 by their corresponding class names, objects, or persistence
1507 adaptors.
1509 This implementation will use table_name() and the map
1510 returned by association_entity_map().
1512 This method will throw an exception if the association is
1513 not mapped (not to be confused with the association being
1514 unsupported).
1516 Example :
1517 Returns : the name of the table (a string, or undef if the association is not
1518 supported by the schema)
1519 Args : A reference to an array of objects, class names, or persistence
1520 adaptors. The array may freely mix types.
1522 =cut
1524 sub association_table_name {
1525 my ( $self, $objs ) = @_;
1526 my ($tbl);
1528 # retrieve the map
1529 my $assocmap = $self->association_entity_map();
1531 # descend the tree as we encounter the objects
1532 foreach my $obj (@$objs) {
1533 $tbl = $self->table_name($obj);
1534 $assocmap = defined($tbl) ? $assocmap->{$tbl} : $tbl;
1535 last if ( ! ref($assocmap) );
1538 # not mapped?
1539 if ( ref($assocmap) ) {
1540 $self->throw(
1541 "association table for classes (" . join(
1542 ",",
1543 map {
1544 ref($_)
1545 ? ( $_->isa("Bio::DB::PersistentObjectI")
1546 ? ref( $_->obj() )
1547 : ref($_) )
1548 : $_;
1549 } @$objs
1551 . ") not mapped"
1555 # ended at a scalar (supposedly the table name)
1556 return $assocmap;
1559 =head2 primary_key_name
1561 Title : primary_key_name
1562 Usage :
1563 Function: Obtain the name of the primary key attribute for the given table in
1564 the relational schema.
1566 This implementation just appends _id to the table name,
1567 which yields correct results for at least the MySQL version
1568 of the BioSQL schema. Override it for your own schema if
1569 necessary.
1571 Example :
1572 Returns : The name of the primary key (a string)
1573 Args : The name of the table (a string)
1575 =cut
1577 sub primary_key_name {
1578 my ( $self, $table ) = @_;
1580 return $table . "_id";
1583 =head2 foreign_key_name
1585 Title : foreign_key_name
1586 Usage :
1587 Function: Obtain the foreign key name for referencing an object, as
1588 represented by object, class name, or the persistence adaptor.
1589 Example :
1590 Returns : the name of the foreign key (a string)
1591 Args : The referenced object, class name, or the persistence adaptor for
1592 it.
1594 =cut
1596 sub foreign_key_name {
1597 my ( $self, $obj ) = @_;
1598 my ( $table, $fk );
1600 # if the object is a persistent object and has the foreign_key_slot value
1601 # set, we start from there
1602 if ( ref($obj)
1603 && $obj->isa("Bio::DB::PersistentObjectI")
1604 && $obj->foreign_key_slot() ) {
1605 $obj = $obj->foreign_key_slot();
1608 # default is to get the primary key of the respective table
1609 $table = $self->table_name($obj);
1610 if ($table) {
1611 $fk = $self->_build_foreign_key_name($table);
1612 } elsif ( ! ref($obj) ) {
1614 # If the object or class name didn't map to a table it may be due
1615 # to a context being provided as a slot of a class. To try this,
1616 # remove the last component, see whether the rest maps to a table,
1617 # and if so, look up the slot in its attribute map.
1618 my @comps = split( /::/, $obj );
1619 my $slot = pop(@comps);
1620 $table = $self->table_name( join( "::", @comps ) );
1621 if ($table) {
1622 my $slotmap = $self->slot_attribute_map($table);
1623 if ($slotmap) {
1624 $fk = $slotmap->{$slot};
1628 return $fk;
1631 =head2 _build_foreign_key_name
1633 Title : _build_foreign_key_name
1634 Usage :
1635 Function: Build the column name for a foreign key to the given table.
1637 The default implementation here retrieves the primary key
1638 for the given table.
1640 This is called by foreign_key_name() once it has determined
1641 the table name. If a particular driver wants to build the
1642 foreign key name in a specific or generally different way
1643 than the default implementation here, this is the method to
1644 override (unless you also want to change the way the table
1645 is determined; in that case you would override
1646 foreign_key_name()).
1648 Example :
1649 Returns : The name of the foreign key column as a string
1650 Args : The table name as a string
1652 =cut
1654 sub _build_foreign_key_name {
1655 my $self = shift;
1656 my $table = shift;
1658 return $self->primary_key_name($table);
1661 =head2 sequence_name
1663 Title : sequence_name
1664 Usage :
1665 Function: Returns the name of the primary key generator (SQL sequence)
1666 for the given table.
1668 The value returned is passed as the second argument to the
1669 L<Bio::DB:DBI>::last_id_value as implemented by the
1670 driver. Because the parameter is not required irregardless
1671 of driver, it is perfectly legal for this method to return
1672 undef. If the L<Bio::DB::DBI> driver does need this
1673 parameter, this method should be overridden by the matching
1674 adaptor driver.
1676 The default we assume here is we dont need this value.
1678 Example :
1679 Returns : the name of the sequence (a string)
1680 Args : The name of the table.
1682 =cut
1684 sub sequence_name {
1685 return undef;
1688 =head2 objrel_map
1690 Title : objrel_map
1691 Usage :
1692 Function: Get/set the object-relational map from .
1693 Example :
1694 Returns : A reference to a hash map where object interfaces are the keys
1695 Args : Optional, on set a reference to the respective hash map
1697 =cut
1699 sub objrel_map {
1700 my ( $self, $value ) = @_;
1702 if ($value) {
1703 $self->{'_objrel_map'} = $value;
1705 return $self->{'_objrel_map'};
1708 =head2 slot_attribute_map
1710 Title : slot_attribute_map
1711 Usage :
1712 Function: Get/set the mapping for each entity from object slot names
1713 to column names.
1715 Example :
1716 Returns : A reference to a hash map with entity names being the keys,
1717 if no key (entity name, object, or adaptor) was
1718 provided. Otherwise, a hash reference with the slot names
1719 being keys to their corresponding column names.
1721 Args : Optionally, the object, adaptor, or entity for which to
1722 obtain the map.
1724 Optionally, on set a reference to a hash map satisfying the
1725 features of the returned value.
1727 =cut
1729 sub slot_attribute_map {
1730 my ( $self, $tablekey, $map ) = @_;
1732 if ($tablekey) {
1734 # this might actually be the overall map on set
1735 if ( ( ref($tablekey) eq "HASH" ) && ( ! $map ) ) {
1736 $map = $tablekey;
1737 $tablekey = undef;
1738 $self->{'_slot_attr_map'} = $map;
1739 } else {
1741 # make sure the hash exists before we query it with a key
1742 if ( ! exists( $self->{'_slot_attr_map'} ) ) {
1743 $self->{'_slot_attr_map'} = {};
1746 # see whether we need to transform it into an entity name
1747 if ( ref($tablekey) ) {
1748 $tablekey = $self->table_name($tablekey);
1751 # set/get the individual map
1752 if ($map) {
1753 $self->{'_slot_attr_map'}->{$tablekey} = $map;
1754 } else {
1755 $map = $self->{'_slot_attr_map'}->{$tablekey};
1758 } else {
1760 # return the overall map
1761 $map = $self->{'_slot_attr_map'};
1763 return $map;
1766 =head2 not_select_attrs
1768 Title : not_select_attrs
1769 Usage : $obj->not_select_attrs($newval)
1770 Function: Get/set a map of all columns that should not be included in
1771 SELECT lists.
1772 Example :
1773 Returns : value of not_select_attrs (a reference to a hash map)
1774 Args : new value (a reference to a hash map, optional)
1776 =cut
1778 sub not_select_attrs {
1779 my ( $self, $value ) = @_;
1780 if ( defined $value ) {
1781 $self->{'not_select_attrs'} = $value;
1783 return $self->{'not_select_attrs'};
1786 =head2 association_entity_map
1788 Title : association_entity_map
1789 Usage : $obj->association_entity_map($newval)
1790 Function: Get/set the association entity map. The map is an anonymous
1791 hash with entities that participate in associations being
1792 keys. The values are hash refs themselves, with the other
1793 participating entity being the key, and the value being
1794 either the name of the respective association entity, or
1795 another hash ref with the same structure if more entities
1796 participate in the association.
1798 The hash map must be commutative. I.e., the association
1799 entity must be locatable irregardless with which of the
1800 participating entities one starts.
1802 Example :
1803 Returns : value of association_entity_map (a hash ref of hash refs)
1804 Args : new value (a hash ref of hash refs, optional)
1806 =cut
1808 sub association_entity_map {
1809 my ( $self, $value ) = @_;
1810 if ( defined $value ) {
1811 $self->{'association_entity_map'} = $value;
1813 return $self->{'association_entity_map'};
1816 =head1 DBI calls for possible interception
1818 These will usually delegate straightforward DBI calls on the supplied
1819 handle, but can also be used by an inheriting adaptor driver to
1820 intercept the call and add additional parameters, for example a hash
1821 reference with named parameters.
1823 =cut
1825 =head2 commit
1827 Title : commit
1828 Usage :
1829 Function: Commits the current transaction, if the underlying driver
1830 supports transactions.
1831 Example :
1832 Returns : TRUE
1833 Args : The database connection handle for which to commit.
1835 =cut
1837 sub commit {
1838 my ( $self, $dbh ) = @_;
1839 return $dbh->commit();
1842 =head2 rollback
1844 Title : rollback
1845 Usage :
1846 Function: Triggers a rollback of the current transaction, if the
1847 underlying driver supports transactions.
1848 Example :
1849 Returns : TRUE
1850 Args : The database connection for which to rollback.
1852 =cut
1854 sub rollback {
1855 my ( $self, $dbh ) = @_;
1856 return $dbh->rollback();
1859 =head2 bind_param
1861 Title : bind_param
1862 Usage :
1863 Function: Binds a parameter value to a prepared statement.
1865 The reason this method is here is to give RDBMS-specific
1866 drivers a chance to intercept the parameter binding and
1867 perform additional actions, or add additional parameters to
1868 the call, like data type. Certain drivers need to be helped
1869 for certain types, for example DBD::Oracle for LOB
1870 parameters.
1872 Example :
1873 Returns : the return value of the DBI::bind_param() call
1874 Args : the DBI statement handle to bind to
1875 the index of the column
1876 the value to bind
1877 additional arguments to be passed to the sth->bind_param call
1879 =cut
1881 sub bind_param {
1882 my ( $self, $sth, $i, $val, @bindargs ) = @_;
1884 return $sth->bind_param( $i, $val, @bindargs );
1887 =head2 prepare
1889 Title : prepare
1890 Usage :
1891 Function: Prepares a SQL statement and returns a statement handle.
1893 The reason this method is here is the same as for
1894 bind_param.
1896 Example :
1897 Returns : the return value of the DBI::prepare() call
1898 Args : the DBI database handle for preparing the statement
1899 the SQL statement to prepare (a scalar)
1900 additional arguments to be passed to the dbh->prepare call
1902 =cut
1904 sub prepare {
1905 my ( $self, $dbh, $sql, @args ) = @_;
1907 return $dbh->prepare( $sql, @args );
1910 =head1 Utility methods
1912 =cut
1914 =head2 report_execute_failure
1916 Title : report_execute_failure
1917 Usage :
1918 Function: Report the failure to execute a SQL statement.
1920 The reporting by default uses warn() but may be requested
1921 to throw().
1923 Example :
1924 Returns :
1925 Args : Named paramaters. Currently recognized are
1926 -sth the statement handle whose execution failed
1927 -adaptor the calling adaptor
1928 (a Bio::DB::PersistenceAdaptorI object)
1929 -op the type of operation that failed ('insert',
1930 'update',...)
1931 -vals a reference to an array of values that were bound
1932 -fkobjs a reference to an array of foreign key objects
1933 that were bound (optional)
1934 -report_func the name of the method to call for reporting
1935 the message (optional, default is 'warn')
1937 =cut
1939 sub report_execute_failure {
1940 my $self = shift;
1941 my ( $sth, $adp, $op, $slotvals, $fkobjs, $reportfunc )
1942 = $self->_rearrange(
1943 [ qw(STH
1944 ADAPTOR
1946 VALS
1947 FKOBJS
1948 REPORT_FUNC
1954 $reportfunc = "warn" unless $reportfunc;
1955 my $msg = "$op in "
1956 . ref($adp)
1957 . " (driver) failed, values were (\""
1958 . join( "\",\"", @$slotvals ) . "\")";
1959 if ($fkobjs) {
1960 $msg .= " FKs ("
1961 . join( ",",
1962 map { $_ && ref($_) ? $_->primary_key() : "<NULL>"; } @$fkobjs )
1963 . ")";
1965 $self->$reportfunc( "$msg\n" . $sth->errstr );