- Added some stuff to MANIFEST.SKIP
[dbsrgits/SQL-Translator.git] / lib / SQL / Translator / Schema.pm
CommitLineData
3c5de62a 1package SQL::Translator::Schema;
2
97382c6d 3# vim: sw=4: ts=4:
4
3c5de62a 5# ----------------------------------------------------------------------
821a0fde 6# $Id$
3c5de62a 7# ----------------------------------------------------------------------
977651a5 8# Copyright (C) 2002-4 SQLFairy Authors
3c5de62a 9#
10# This program is free software; you can redistribute it and/or
11# modify it under the terms of the GNU General Public License as
12# published by the Free Software Foundation; version 2.
13#
14# This program is distributed in the hope that it will be useful, but
15# WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17# General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program; if not, write to the Free Software
21# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
22# 02111-1307 USA
23# -------------------------------------------------------------------
24
25=pod
26
27=head1 NAME
28
29SQL::Translator::Schema - SQL::Translator schema object
30
31=head1 SYNOPSIS
32
33 use SQL::Translator::Schema;
97382c6d 34 my $schema = SQL::Translator::Schema->new(
35 name => 'Foo',
36 database => 'MySQL',
37 );
38 my $table = $schema->add_table( name => 'foo' );
39 my $view = $schema->add_view( name => 'bar', sql => '...' );
40
3c5de62a 41
42=head1 DESCSIPTION
43
44C<SQL::Translator::Schema> is the object that accepts, validates, and
45returns the database structure.
46
47=head1 METHODS
48
49=cut
50
51use strict;
9480e70b 52use SQL::Translator::Schema::Constants;
daf24e05 53use SQL::Translator::Schema::Procedure;
3c5de62a 54use SQL::Translator::Schema::Table;
5974bee7 55use SQL::Translator::Schema::Trigger;
3c5de62a 56use SQL::Translator::Schema::View;
4c4636ed 57
5974bee7 58use SQL::Translator::Utils 'parse_list_arg';
3c5de62a 59
b6a880d1 60use base 'SQL::Translator::Schema::Object';
daf24e05 61use vars qw[ $VERSION $TABLE_ORDER $VIEW_ORDER $TRIGGER_ORDER $PROC_ORDER ];
3c5de62a 62
821a0fde 63$VERSION = sprintf "%d.%02d", q$Revision$ =~ /(\d+)\.(\d+)/;
9371be50 64
da8ab524 65__PACKAGE__->_attributes(qw/name database translator/);
3c5de62a 66
97382c6d 67# ----------------------------------------------------------------------
68sub as_graph {
69
3c5de62a 70=pod
71
97382c6d 72=head2 as_graph
3c5de62a 73
97382c6d 74Returns the schema as an L<SQL::Translator::Schema::Graph> object.
3c5de62a 75
76=cut
4c4636ed 77 require SQL::Translator::Schema::Graph;
3c5de62a 78
da8ab524 79 my $self = shift;
4c4636ed 80
97382c6d 81 return SQL::Translator::Schema::Graph->new(
da8ab524 82 translator => $self->translator );
10f36920 83}
84
3c5de62a 85# ----------------------------------------------------------------------
e7ca845a 86sub as_graph_pm {
87
88=pod
89
da5a1bae 90=head2 as_graph_pm
e7ca845a 91
92Returns a Graph::Directed object with the table names for nodes.
93
94=cut
95
da5a1bae 96 require Graph::Directed;
97
e7ca845a 98 my $self = shift;
99 my $g = Graph::Directed->new;
100
101 for my $table ( $self->get_tables ) {
102 my $tname = $table->name;
103 $g->add_vertex( $tname );
104
105 for my $field ( $table->get_fields ) {
106 if ( $field->is_foreign_key ) {
107 my $fktable = $field->foreign_key_reference->reference_table;
108
109 $g->add_edge( $fktable, $tname );
110 }
111 }
112 }
113
114 return $g;
115}
116
117# ----------------------------------------------------------------------
76dce619 118sub add_table {
3c5de62a 119
120=pod
121
76dce619 122=head2 add_table
3c5de62a 123
76dce619 124Add a table object. Returns the new SQL::Translator::Schema::Table object.
99248301 125The "name" parameter is required. If you try to create a table with the
126same name as an existing table, you will get an error and the table will
127not be created.
3c5de62a 128
68e8e2e1 129 my $t1 = $schema->add_table( name => 'foo' ) or die $schema->error;
130 my $t2 = SQL::Translator::Schema::Table->new( name => 'bar' );
131 $t2 = $schema->add_table( $table_bar ) or die $schema->error;
3c5de62a 132
133=cut
134
99248301 135 my $self = shift;
136 my $table_class = 'SQL::Translator::Schema::Table';
137 my $table;
138
139 if ( UNIVERSAL::isa( $_[0], $table_class ) ) {
140 $table = shift;
da8ab524 141 $table->schema($self);
99248301 142 }
143 else {
144 my %args = @_;
145 $args{'schema'} = $self;
da8ab524 146 $table = $table_class->new( \%args )
147 or return $self->error( $table_class->error );
99248301 148 }
3c5de62a 149
d0b43695 150 $table->order( ++$TABLE_ORDER );
da8ab524 151
23044758 152 # We know we have a name as the Table->new above errors if none given.
153 my $table_name = $table->name;
99248301 154
da8ab524 155 if ( defined $self->{'tables'}{$table_name} ) {
99248301 156 return $self->error(qq[Can't create table: "$table_name" exists]);
157 }
158 else {
da8ab524 159 $self->{'tables'}{$table_name} = $table;
99248301 160 }
3c5de62a 161
162 return $table;
163}
164
165# ----------------------------------------------------------------------
650f87eb 166sub drop_table {
167
168=pod
169
170=head2 drop_table
171
172Remove a table from the schema. Returns the table object if the table was found
173and removed, an error otherwise. The single parameter can be either a table
174name or an C<SQL::Translator::Schema::Table> object. The "cascade" parameter
175can be set to 1 to also drop all triggers on the table, default is 0.
176
177 $schema->drop_table('mytable');
178 $schema->drop_table('mytable', cascade => 1);
179
180=cut
181
da8ab524 182 my $self = shift;
183 my $table_class = 'SQL::Translator::Schema::Table';
650f87eb 184 my $table_name;
185
186 if ( UNIVERSAL::isa( $_[0], $table_class ) ) {
187 $table_name = shift->name;
188 }
189 else {
190 $table_name = shift;
191 }
da8ab524 192 my %args = @_;
650f87eb 193 my $cascade = $args{'cascade'};
194
da8ab524 195 if ( !exists $self->{'tables'}{$table_name} ) {
650f87eb 196 return $self->error(qq[Can't drop table: $table_name" doesn't exist]);
197 }
198
da8ab524 199 my $table = delete $self->{'tables'}{$table_name};
200
201 if ($cascade) {
650f87eb 202
650f87eb 203 # Drop all triggers on this table
da8ab524 204 $self->drop_trigger()
205 for ( grep { $_->on_table eq $table_name } @{ $self->{'triggers'} } );
650f87eb 206 }
207 return $table;
208}
209
210# ----------------------------------------------------------------------
daf24e05 211sub add_procedure {
212
213=pod
214
215=head2 add_procedure
216
650f87eb 217Add a procedure object. Returns the new SQL::Translator::Schema::Procedure
218object. The "name" parameter is required. If you try to create a procedure
219with the same name as an existing procedure, you will get an error and the
220procedure will not be created.
daf24e05 221
222 my $p1 = $schema->add_procedure( name => 'foo' );
223 my $p2 = SQL::Translator::Schema::Procedure->new( name => 'bar' );
224 $p2 = $schema->add_procedure( $procedure_bar ) or die $schema->error;
225
226=cut
227
228 my $self = shift;
229 my $procedure_class = 'SQL::Translator::Schema::Procedure';
230 my $procedure;
231
232 if ( UNIVERSAL::isa( $_[0], $procedure_class ) ) {
233 $procedure = shift;
da8ab524 234 $procedure->schema($self);
daf24e05 235 }
236 else {
237 my %args = @_;
238 $args{'schema'} = $self;
239 return $self->error('No procedure name') unless $args{'name'};
da8ab524 240 $procedure = $procedure_class->new( \%args )
241 or return $self->error( $procedure_class->error );
daf24e05 242 }
243
244 $procedure->order( ++$PROC_ORDER );
da8ab524 245 my $procedure_name = $procedure->name
246 or return $self->error('No procedure name');
daf24e05 247
da8ab524 248 if ( defined $self->{'procedures'}{$procedure_name} ) {
daf24e05 249 return $self->error(
da8ab524 250 qq[Can't create procedure: "$procedure_name" exists] );
daf24e05 251 }
252 else {
da8ab524 253 $self->{'procedures'}{$procedure_name} = $procedure;
daf24e05 254 }
255
256 return $procedure;
257}
da8ab524 258
650f87eb 259# ----------------------------------------------------------------------
260sub drop_procedure {
261
262=pod
263
264=head2 drop_procedure
265
266Remove a procedure from the schema. Returns the procedure object if the
267procedure was found and removed, an error otherwise. The single parameter
268can be either a procedure name or an C<SQL::Translator::Schema::Procedure>
269object.
270
271 $schema->drop_procedure('myprocedure');
272
273=cut
274
da8ab524 275 my $self = shift;
276 my $proc_class = 'SQL::Translator::Schema::Procedure';
650f87eb 277 my $proc_name;
278
279 if ( UNIVERSAL::isa( $_[0], $proc_class ) ) {
280 $proc_name = shift->name;
281 }
282 else {
283 $proc_name = shift;
284 }
285
da8ab524 286 if ( !exists $self->{'procedures'}{$proc_name} ) {
287 return $self->error(
288 qq[Can't drop procedure: $proc_name" doesn't exist]);
650f87eb 289 }
290
da8ab524 291 my $proc = delete $self->{'procedures'}{$proc_name};
650f87eb 292
293 return $proc;
294}
daf24e05 295
296# ----------------------------------------------------------------------
5974bee7 297sub add_trigger {
298
299=pod
300
301=head2 add_trigger
302
303Add a trigger object. Returns the new SQL::Translator::Schema::Trigger object.
304The "name" parameter is required. If you try to create a trigger with the
305same name as an existing trigger, you will get an error and the trigger will
306not be created.
307
308 my $t1 = $schema->add_trigger( name => 'foo' );
309 my $t2 = SQL::Translator::Schema::Trigger->new( name => 'bar' );
310 $t2 = $schema->add_trigger( $trigger_bar ) or die $schema->error;
311
312=cut
313
314 my $self = shift;
315 my $trigger_class = 'SQL::Translator::Schema::Trigger';
316 my $trigger;
317
318 if ( UNIVERSAL::isa( $_[0], $trigger_class ) ) {
319 $trigger = shift;
da8ab524 320 $trigger->schema($self);
5974bee7 321 }
322 else {
323 my %args = @_;
daf24e05 324 $args{'schema'} = $self;
5974bee7 325 return $self->error('No trigger name') unless $args{'name'};
da8ab524 326 $trigger = $trigger_class->new( \%args )
327 or return $self->error( $trigger_class->error );
5974bee7 328 }
329
330 $trigger->order( ++$TRIGGER_ORDER );
5974bee7 331
4faaaac6 332 my $trigger_name = $trigger->name or return $self->error('No trigger name');
da8ab524 333 if ( defined $self->{'triggers'}{$trigger_name} ) {
5974bee7 334 return $self->error(qq[Can't create trigger: "$trigger_name" exists]);
335 }
336 else {
da8ab524 337 $self->{'triggers'}{$trigger_name} = $trigger;
5974bee7 338 }
339
340 return $trigger;
341}
da8ab524 342
650f87eb 343# ----------------------------------------------------------------------
344sub drop_trigger {
345
346=pod
347
348=head2 drop_trigger
349
350Remove a trigger from the schema. Returns the trigger object if the trigger was
351found and removed, an error otherwise. The single parameter can be either a
352trigger name or an C<SQL::Translator::Schema::Trigger> object.
353
354 $schema->drop_trigger('mytrigger');
355
356=cut
357
da8ab524 358 my $self = shift;
359 my $trigger_class = 'SQL::Translator::Schema::Trigger';
650f87eb 360 my $trigger_name;
361
362 if ( UNIVERSAL::isa( $_[0], $trigger_class ) ) {
363 $trigger_name = shift->name;
364 }
365 else {
366 $trigger_name = shift;
367 }
368
da8ab524 369 if ( !exists $self->{'triggers'}{$trigger_name} ) {
370 return $self->error(
371 qq[Can't drop trigger: $trigger_name" doesn't exist]);
650f87eb 372 }
373
da8ab524 374 my $trigger = delete $self->{'triggers'}{$trigger_name};
650f87eb 375
376 return $trigger;
377}
5974bee7 378
379# ----------------------------------------------------------------------
76dce619 380sub add_view {
3c5de62a 381
382=pod
383
76dce619 384=head2 add_view
3c5de62a 385
76dce619 386Add a view object. Returns the new SQL::Translator::Schema::View object.
99248301 387The "name" parameter is required. If you try to create a view with the
388same name as an existing view, you will get an error and the view will
389not be created.
390
68e8e2e1 391 my $v1 = $schema->add_view( name => 'foo' );
392 my $v2 = SQL::Translator::Schema::View->new( name => 'bar' );
393 $v2 = $schema->add_view( $view_bar ) or die $schema->error;
3c5de62a 394
395=cut
396
da8ab524 397 my $self = shift;
99248301 398 my $view_class = 'SQL::Translator::Schema::View';
399 my $view;
400
401 if ( UNIVERSAL::isa( $_[0], $view_class ) ) {
402 $view = shift;
da8ab524 403 $view->schema($self);
99248301 404 }
405 else {
406 my %args = @_;
daf24e05 407 $args{'schema'} = $self;
99248301 408 return $self->error('No view name') unless $args{'name'};
409 $view = $view_class->new( \%args ) or return $view_class->error;
410 }
3c5de62a 411
d0b43695 412 $view->order( ++$VIEW_ORDER );
99248301 413 my $view_name = $view->name or return $self->error('No view name');
414
da8ab524 415 if ( defined $self->{'views'}{$view_name} ) {
99248301 416 return $self->error(qq[Can't create view: "$view_name" exists]);
417 }
418 else {
da8ab524 419 $self->{'views'}{$view_name} = $view;
99248301 420 }
3c5de62a 421
76dce619 422 return $view;
3c5de62a 423}
424
425# ----------------------------------------------------------------------
650f87eb 426sub drop_view {
427
428=pod
429
430=head2 drop_view
431
432Remove a view from the schema. Returns the view object if the view was found
433and removed, an error otherwise. The single parameter can be either a view
434name or an C<SQL::Translator::Schema::View> object.
435
436 $schema->drop_view('myview');
437
438=cut
439
da8ab524 440 my $self = shift;
441 my $view_class = 'SQL::Translator::Schema::View';
650f87eb 442 my $view_name;
443
444 if ( UNIVERSAL::isa( $_[0], $view_class ) ) {
445 $view_name = shift->name;
446 }
447 else {
448 $view_name = shift;
449 }
450
da8ab524 451 if ( !exists $self->{'views'}{$view_name} ) {
650f87eb 452 return $self->error(qq[Can't drop view: $view_name" doesn't exist]);
453 }
454
da8ab524 455 my $view = delete $self->{'views'}{$view_name};
650f87eb 456
457 return $view;
458}
459
460# ----------------------------------------------------------------------
99248301 461sub database {
462
463=pod
464
465=head2 database
466
467Get or set the schema's database. (optional)
468
469 my $database = $schema->database('PostgreSQL');
470
471=cut
472
473 my $self = shift;
474 $self->{'database'} = shift if @_;
475 return $self->{'database'} || '';
476}
477
478# ----------------------------------------------------------------------
76dce619 479sub is_valid {
3c5de62a 480
481=pod
482
76dce619 483=head2 is_valid
3c5de62a 484
76dce619 485Returns true if all the tables and views are valid.
3c5de62a 486
76dce619 487 my $ok = $schema->is_valid or die $schema->error;
488
489=cut
490
491 my $self = shift;
492
493 return $self->error('No tables') unless $self->get_tables;
494
495 for my $object ( $self->get_tables, $self->get_views ) {
496 return $object->error unless $object->is_valid;
497 }
498
499 return 1;
500}
501
502# ----------------------------------------------------------------------
daf24e05 503sub get_procedure {
504
505=pod
506
507=head2 get_procedure
508
509Returns a procedure by the name provided.
510
511 my $procedure = $schema->get_procedure('foo');
512
513=cut
514
da8ab524 515 my $self = shift;
daf24e05 516 my $procedure_name = shift or return $self->error('No procedure name');
da8ab524 517 return $self->error(qq[Table "$procedure_name" does not exist])
518 unless exists $self->{'procedures'}{$procedure_name};
519 return $self->{'procedures'}{$procedure_name};
daf24e05 520}
521
522# ----------------------------------------------------------------------
523sub get_procedures {
524
525=pod
526
527=head2 get_procedures
528
529Returns all the procedures as an array or array reference.
530
531 my @procedures = $schema->get_procedures;
532
533=cut
534
da8ab524 535 my $self = shift;
536 my @procedures =
537 map { $_->[1] }
538 sort { $a->[0] <=> $b->[0] }
539 map { [ $_->order, $_ ] } values %{ $self->{'procedures'} };
daf24e05 540
da8ab524 541 if (@procedures) {
daf24e05 542 return wantarray ? @procedures : \@procedures;
543 }
544 else {
545 $self->error('No procedures');
546 return wantarray ? () : undef;
547 }
548}
549
550# ----------------------------------------------------------------------
76dce619 551sub get_table {
552
553=pod
554
555=head2 get_table
556
557Returns a table by the name provided.
558
559 my $table = $schema->get_table('foo');
560
561=cut
562
da8ab524 563 my $self = shift;
76dce619 564 my $table_name = shift or return $self->error('No table name');
6a25a87d 565 my $case_insensitive = shift;
566 if ( $case_insensitive ) {
567 $table_name = uc($table_name);
568 foreach my $table ( keys %{$self->{tables}} ) {
569 return $self->{tables}{$table} if $table_name eq uc($table);
570 }
571 return $self->error(qq[Table "$table_name" does not exist]);
572 }
da8ab524 573 return $self->error(qq[Table "$table_name" does not exist])
574 unless exists $self->{'tables'}{$table_name};
575 return $self->{'tables'}{$table_name};
76dce619 576}
577
578# ----------------------------------------------------------------------
579sub get_tables {
580
581=pod
582
583=head2 get_tables
584
585Returns all the tables as an array or array reference.
586
587 my @tables = $schema->get_tables;
588
589=cut
590
591 my $self = shift;
da8ab524 592 my @tables =
593 map { $_->[1] }
594 sort { $a->[0] <=> $b->[0] }
595 map { [ $_->order, $_ ] } values %{ $self->{'tables'} };
76dce619 596
da8ab524 597 if (@tables) {
76dce619 598 return wantarray ? @tables : \@tables;
599 }
600 else {
601 $self->error('No tables');
602 return wantarray ? () : undef;
603 }
604}
605
606# ----------------------------------------------------------------------
daf24e05 607sub get_trigger {
608
609=pod
610
611=head2 get_trigger
612
613Returns a trigger by the name provided.
614
615 my $trigger = $schema->get_trigger('foo');
616
617=cut
618
da8ab524 619 my $self = shift;
daf24e05 620 my $trigger_name = shift or return $self->error('No trigger name');
da8ab524 621 return $self->error(qq[Table "$trigger_name" does not exist])
622 unless exists $self->{'triggers'}{$trigger_name};
623 return $self->{'triggers'}{$trigger_name};
daf24e05 624}
625
626# ----------------------------------------------------------------------
627sub get_triggers {
628
629=pod
630
631=head2 get_triggers
632
633Returns all the triggers as an array or array reference.
634
635 my @triggers = $schema->get_triggers;
636
637=cut
638
da8ab524 639 my $self = shift;
640 my @triggers =
641 map { $_->[1] }
642 sort { $a->[0] <=> $b->[0] }
643 map { [ $_->order, $_ ] } values %{ $self->{'triggers'} };
daf24e05 644
da8ab524 645 if (@triggers) {
daf24e05 646 return wantarray ? @triggers : \@triggers;
647 }
648 else {
649 $self->error('No triggers');
650 return wantarray ? () : undef;
651 }
652}
653
654# ----------------------------------------------------------------------
76dce619 655sub get_view {
656
657=pod
658
659=head2 get_view
660
661Returns a view by the name provided.
662
663 my $view = $schema->get_view('foo');
3c5de62a 664
665=cut
666
da8ab524 667 my $self = shift;
76dce619 668 my $view_name = shift or return $self->error('No view name');
da8ab524 669 return $self->error('View "$view_name" does not exist')
670 unless exists $self->{'views'}{$view_name};
671 return $self->{'views'}{$view_name};
76dce619 672}
3c5de62a 673
76dce619 674# ----------------------------------------------------------------------
675sub get_views {
3c5de62a 676
76dce619 677=pod
678
679=head2 get_views
680
681Returns all the views as an array or array reference.
682
683 my @views = $schema->get_views;
684
685=cut
686
687 my $self = shift;
da8ab524 688 my @views =
689 map { $_->[1] }
690 sort { $a->[0] <=> $b->[0] }
691 map { [ $_->order, $_ ] } values %{ $self->{'views'} };
76dce619 692
da8ab524 693 if (@views) {
76dce619 694 return wantarray ? @views : \@views;
695 }
696 else {
697 $self->error('No views');
698 return wantarray ? () : undef;
699 }
3c5de62a 700}
701
99248301 702# ----------------------------------------------------------------------
9480e70b 703sub make_natural_joins {
704
705=pod
706
707=head2 make_natural_joins
708
709Creates foriegn key relationships among like-named fields in different
710tables. Accepts the following arguments:
711
712=over 4
713
650f87eb 714=item * join_pk_only
9480e70b 715
716A True or False argument which determins whether or not to perform
717the joins from primary keys to fields of the same name in other tables
718
719=item * skip_fields
720
721A list of fields to skip in the joins
722
723=back 4
724
725 $schema->make_natural_joins(
726 join_pk_only => 1,
727 skip_fields => 'name,department_id',
728 );
729
730=cut
731
732 my $self = shift;
733 my %args = @_;
734 my $join_pk_only = $args{'join_pk_only'} || 0;
da8ab524 735 my %skip_fields =
736 map { s/^\s+|\s+$//g; $_, 1 } @{ parse_list_arg( $args{'skip_fields'} ) };
9480e70b 737
738 my ( %common_keys, %pk );
739 for my $table ( $self->get_tables ) {
740 for my $field ( $table->get_fields ) {
741 my $field_name = $field->name or next;
da8ab524 742 next if $skip_fields{$field_name};
743 $pk{$field_name} = 1 if $field->is_primary_key;
744 push @{ $common_keys{$field_name} }, $table->name;
9480e70b 745 }
da8ab524 746 }
747
9480e70b 748 for my $field ( keys %common_keys ) {
da8ab524 749 next if $join_pk_only and !defined $pk{$field};
9480e70b 750
da8ab524 751 my @table_names = @{ $common_keys{$field} };
9480e70b 752 next unless scalar @table_names > 1;
753
754 for my $i ( 0 .. $#table_names ) {
da8ab524 755 my $table1 = $self->get_table( $table_names[$i] ) or next;
9480e70b 756
757 for my $j ( 1 .. $#table_names ) {
da8ab524 758 my $table2 = $self->get_table( $table_names[$j] ) or next;
9480e70b 759 next if $table1->name eq $table2->name;
760
761 $table1->add_constraint(
762 type => FOREIGN_KEY,
763 fields => $field,
764 reference_table => $table2->name,
765 reference_fields => $field,
766 );
650f87eb 767 }
9480e70b 768 }
650f87eb 769 }
9480e70b 770
771 return 1;
772}
773
774# ----------------------------------------------------------------------
99248301 775sub name {
776
777=pod
778
779=head2 name
780
781Get or set the schema's name. (optional)
782
783 my $schema_name = $schema->name('Foo Database');
784
785=cut
786
787 my $self = shift;
788 $self->{'name'} = shift if @_;
789 return $self->{'name'} || '';
790}
791
97382c6d 792# ----------------------------------------------------------------------
793sub translator {
794
795=pod
796
10f36920 797=head2 translator
47fed978 798
97382c6d 799Get the SQL::Translator instance that instantiated the parser.
47fed978 800
801=cut
802
47fed978 803 my $self = shift;
10f36920 804 $self->{'translator'} = shift if @_;
805 return $self->{'translator'};
47fed978 806}
807
d0b43695 808# ----------------------------------------------------------------------
809sub DESTROY {
810 my $self = shift;
811 undef $_ for values %{ $self->{'tables'} };
da8ab524 812 undef $_ for values %{ $self->{'views'} };
d0b43695 813}
814
3c5de62a 8151;
816
817# ----------------------------------------------------------------------
818
819=pod
820
821=head1 AUTHOR
822
97382c6d 823Ken Youens-Clark E<lt>kclark@cpan.orgE<gt>.
3c5de62a 824
825=cut
da8ab524 826