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