Added collection tags for the Schemas objects (tables, views, etc)
[dbsrgits/SQL-Translator.git] / lib / SQL / Translator / Schema.pm
CommitLineData
3c5de62a 1package SQL::Translator::Schema;
2
3# ----------------------------------------------------------------------
9aeb1164 4# $Id: Schema.pm,v 1.15 2004-07-26 13:55:51 kycl4rk Exp $
3c5de62a 5# ----------------------------------------------------------------------
977651a5 6# Copyright (C) 2002-4 SQLFairy Authors
3c5de62a 7#
8# This program is free software; you can redistribute it and/or
9# modify it under the terms of the GNU General Public License as
10# published by the Free Software Foundation; version 2.
11#
12# This program is distributed in the hope that it will be useful, but
13# WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15# General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with this program; if not, write to the Free Software
19# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
20# 02111-1307 USA
21# -------------------------------------------------------------------
22
23=pod
24
25=head1 NAME
26
27SQL::Translator::Schema - SQL::Translator schema object
28
29=head1 SYNOPSIS
30
31 use SQL::Translator::Schema;
76dce619 32 my $schema = SQL::Translator::Schema->new;
33 my $table = $schema->add_table( name => 'foo' );
34 my $view = $schema->add_view( name => 'bar', sql => '...' );
3c5de62a 35
36=head1 DESCSIPTION
37
38C<SQL::Translator::Schema> is the object that accepts, validates, and
39returns the database structure.
40
41=head1 METHODS
42
43=cut
44
45use strict;
46use Class::Base;
9480e70b 47use SQL::Translator::Schema::Constants;
daf24e05 48use SQL::Translator::Schema::Procedure;
3c5de62a 49use SQL::Translator::Schema::Table;
5974bee7 50use SQL::Translator::Schema::Trigger;
3c5de62a 51use SQL::Translator::Schema::View;
5974bee7 52use SQL::Translator::Utils 'parse_list_arg';
3c5de62a 53
54use base 'Class::Base';
daf24e05 55use vars qw[ $VERSION $TABLE_ORDER $VIEW_ORDER $TRIGGER_ORDER $PROC_ORDER ];
3c5de62a 56
9aeb1164 57$VERSION = sprintf "%d.%02d", q$Revision: 1.15 $ =~ /(\d+)\.(\d+)/;
3c5de62a 58
59# ----------------------------------------------------------------------
60sub init {
61
62=pod
63
64=head2 new
65
66Object constructor.
67
9aeb1164 68 my $schema = SQL::Translator::Schema->new(
99248301 69 name => 'Foo',
70 database => 'MySQL',
71 );
3c5de62a 72
73=cut
74
75 my ( $self, $config ) = @_;
99248301 76 $self->params( $config, qw[ name database ] ) || return undef;
3c5de62a 77 return $self;
78}
79
80# ----------------------------------------------------------------------
76dce619 81sub add_table {
3c5de62a 82
83=pod
84
76dce619 85=head2 add_table
3c5de62a 86
76dce619 87Add a table object. Returns the new SQL::Translator::Schema::Table object.
99248301 88The "name" parameter is required. If you try to create a table with the
89same name as an existing table, you will get an error and the table will
90not be created.
3c5de62a 91
68e8e2e1 92 my $t1 = $schema->add_table( name => 'foo' ) or die $schema->error;
93 my $t2 = SQL::Translator::Schema::Table->new( name => 'bar' );
94 $t2 = $schema->add_table( $table_bar ) or die $schema->error;
3c5de62a 95
96=cut
97
99248301 98 my $self = shift;
99 my $table_class = 'SQL::Translator::Schema::Table';
100 my $table;
101
102 if ( UNIVERSAL::isa( $_[0], $table_class ) ) {
103 $table = shift;
104 $table->schema( $self );
105 }
106 else {
107 my %args = @_;
108 $args{'schema'} = $self;
109 $table = $table_class->new( \%args ) or return
110 $self->error( $table_class->error );
111 }
3c5de62a 112
d0b43695 113 $table->order( ++$TABLE_ORDER );
23044758 114 # We know we have a name as the Table->new above errors if none given.
115 my $table_name = $table->name;
99248301 116
117 if ( defined $self->{'tables'}{ $table_name } ) {
118 return $self->error(qq[Can't create table: "$table_name" exists]);
119 }
120 else {
121 $self->{'tables'}{ $table_name } = $table;
99248301 122 }
3c5de62a 123
124 return $table;
125}
126
127# ----------------------------------------------------------------------
daf24e05 128sub add_procedure {
129
130=pod
131
132=head2 add_procedure
133
134Add a procedure object. Returns the new
135SQL::Translator::Schema::Procedure object. The "name" parameter is
136required. If you try to create a procedure with the same name as an
137existing procedure, you will get an error and the procedure will not
138be created.
139
140 my $p1 = $schema->add_procedure( name => 'foo' );
141 my $p2 = SQL::Translator::Schema::Procedure->new( name => 'bar' );
142 $p2 = $schema->add_procedure( $procedure_bar ) or die $schema->error;
143
144=cut
145
146 my $self = shift;
147 my $procedure_class = 'SQL::Translator::Schema::Procedure';
148 my $procedure;
149
150 if ( UNIVERSAL::isa( $_[0], $procedure_class ) ) {
151 $procedure = shift;
152 $procedure->schema( $self );
153 }
154 else {
155 my %args = @_;
156 $args{'schema'} = $self;
157 return $self->error('No procedure name') unless $args{'name'};
158 $procedure = $procedure_class->new( \%args ) or
159 return $self->error( $procedure_class->error );
160 }
161
162 $procedure->order( ++$PROC_ORDER );
163 my $procedure_name = $procedure->name or return
164 $self->error('No procedure name');
165
166 if ( defined $self->{'procedures'}{ $procedure_name } ) {
167 return $self->error(
168 qq[Can't create procedure: "$procedure_name" exists]
169 );
170 }
171 else {
172 $self->{'procedures'}{ $procedure_name } = $procedure;
173 }
174
175 return $procedure;
176}
177
178# ----------------------------------------------------------------------
5974bee7 179sub add_trigger {
180
181=pod
182
183=head2 add_trigger
184
185Add a trigger object. Returns the new SQL::Translator::Schema::Trigger object.
186The "name" parameter is required. If you try to create a trigger with the
187same name as an existing trigger, you will get an error and the trigger will
188not be created.
189
190 my $t1 = $schema->add_trigger( name => 'foo' );
191 my $t2 = SQL::Translator::Schema::Trigger->new( name => 'bar' );
192 $t2 = $schema->add_trigger( $trigger_bar ) or die $schema->error;
193
194=cut
195
196 my $self = shift;
197 my $trigger_class = 'SQL::Translator::Schema::Trigger';
198 my $trigger;
199
200 if ( UNIVERSAL::isa( $_[0], $trigger_class ) ) {
201 $trigger = shift;
daf24e05 202 $trigger->schema( $self );
5974bee7 203 }
204 else {
205 my %args = @_;
daf24e05 206 $args{'schema'} = $self;
5974bee7 207 return $self->error('No trigger name') unless $args{'name'};
208 $trigger = $trigger_class->new( \%args ) or
209 return $self->error( $trigger_class->error );
210 }
211
212 $trigger->order( ++$TRIGGER_ORDER );
213 my $trigger_name = $trigger->name or return $self->error('No trigger name');
214
215 if ( defined $self->{'triggers'}{ $trigger_name } ) {
216 return $self->error(qq[Can't create trigger: "$trigger_name" exists]);
217 }
218 else {
219 $self->{'triggers'}{ $trigger_name } = $trigger;
220 }
221
222 return $trigger;
223}
224
225# ----------------------------------------------------------------------
76dce619 226sub add_view {
3c5de62a 227
228=pod
229
76dce619 230=head2 add_view
3c5de62a 231
76dce619 232Add a view object. Returns the new SQL::Translator::Schema::View object.
99248301 233The "name" parameter is required. If you try to create a view with the
234same name as an existing view, you will get an error and the view will
235not be created.
236
68e8e2e1 237 my $v1 = $schema->add_view( name => 'foo' );
238 my $v2 = SQL::Translator::Schema::View->new( name => 'bar' );
239 $v2 = $schema->add_view( $view_bar ) or die $schema->error;
3c5de62a 240
241=cut
242
99248301 243 my $self = shift;
244 my $view_class = 'SQL::Translator::Schema::View';
245 my $view;
246
247 if ( UNIVERSAL::isa( $_[0], $view_class ) ) {
248 $view = shift;
daf24e05 249 $view->schema( $self );
99248301 250 }
251 else {
252 my %args = @_;
daf24e05 253 $args{'schema'} = $self;
99248301 254 return $self->error('No view name') unless $args{'name'};
255 $view = $view_class->new( \%args ) or return $view_class->error;
256 }
3c5de62a 257
d0b43695 258 $view->order( ++$VIEW_ORDER );
99248301 259 my $view_name = $view->name or return $self->error('No view name');
260
261 if ( defined $self->{'views'}{ $view_name } ) {
262 return $self->error(qq[Can't create view: "$view_name" exists]);
263 }
264 else {
265 $self->{'views'}{ $view_name } = $view;
99248301 266 }
3c5de62a 267
76dce619 268 return $view;
3c5de62a 269}
270
271# ----------------------------------------------------------------------
99248301 272sub database {
273
274=pod
275
276=head2 database
277
278Get or set the schema's database. (optional)
279
280 my $database = $schema->database('PostgreSQL');
281
282=cut
283
284 my $self = shift;
285 $self->{'database'} = shift if @_;
286 return $self->{'database'} || '';
287}
288
289# ----------------------------------------------------------------------
76dce619 290sub is_valid {
3c5de62a 291
292=pod
293
76dce619 294=head2 is_valid
3c5de62a 295
76dce619 296Returns true if all the tables and views are valid.
3c5de62a 297
76dce619 298 my $ok = $schema->is_valid or die $schema->error;
299
300=cut
301
302 my $self = shift;
303
304 return $self->error('No tables') unless $self->get_tables;
305
306 for my $object ( $self->get_tables, $self->get_views ) {
307 return $object->error unless $object->is_valid;
308 }
309
310 return 1;
311}
312
313# ----------------------------------------------------------------------
daf24e05 314sub get_procedure {
315
316=pod
317
318=head2 get_procedure
319
320Returns a procedure by the name provided.
321
322 my $procedure = $schema->get_procedure('foo');
323
324=cut
325
326 my $self = shift;
327 my $procedure_name = shift or return $self->error('No procedure name');
328 return $self->error( qq[Table "$procedure_name" does not exist] ) unless
329 exists $self->{'procedures'}{ $procedure_name };
330 return $self->{'procedures'}{ $procedure_name };
331}
332
333# ----------------------------------------------------------------------
334sub get_procedures {
335
336=pod
337
338=head2 get_procedures
339
340Returns all the procedures as an array or array reference.
341
342 my @procedures = $schema->get_procedures;
343
344=cut
345
346 my $self = shift;
347 my @procedures =
348 map { $_->[1] }
349 sort { $a->[0] <=> $b->[0] }
350 map { [ $_->order, $_ ] }
351 values %{ $self->{'procedures'} };
352
353 if ( @procedures ) {
354 return wantarray ? @procedures : \@procedures;
355 }
356 else {
357 $self->error('No procedures');
358 return wantarray ? () : undef;
359 }
360}
361
362# ----------------------------------------------------------------------
76dce619 363sub get_table {
364
365=pod
366
367=head2 get_table
368
369Returns a table by the name provided.
370
371 my $table = $schema->get_table('foo');
372
373=cut
374
375 my $self = shift;
376 my $table_name = shift or return $self->error('No table name');
99248301 377 return $self->error( qq[Table "$table_name" does not exist] ) unless
76dce619 378 exists $self->{'tables'}{ $table_name };
379 return $self->{'tables'}{ $table_name };
380}
381
382# ----------------------------------------------------------------------
383sub get_tables {
384
385=pod
386
387=head2 get_tables
388
389Returns all the tables as an array or array reference.
390
391 my @tables = $schema->get_tables;
392
393=cut
394
395 my $self = shift;
d0b43695 396 my @tables =
397 map { $_->[1] }
398 sort { $a->[0] <=> $b->[0] }
399 map { [ $_->order, $_ ] }
76dce619 400 values %{ $self->{'tables'} };
401
402 if ( @tables ) {
403 return wantarray ? @tables : \@tables;
404 }
405 else {
406 $self->error('No tables');
407 return wantarray ? () : undef;
408 }
409}
410
411# ----------------------------------------------------------------------
daf24e05 412sub get_trigger {
413
414=pod
415
416=head2 get_trigger
417
418Returns a trigger by the name provided.
419
420 my $trigger = $schema->get_trigger('foo');
421
422=cut
423
424 my $self = shift;
425 my $trigger_name = shift or return $self->error('No trigger name');
426 return $self->error( qq[Table "$trigger_name" does not exist] ) unless
427 exists $self->{'triggers'}{ $trigger_name };
428 return $self->{'triggers'}{ $trigger_name };
429}
430
431# ----------------------------------------------------------------------
432sub get_triggers {
433
434=pod
435
436=head2 get_triggers
437
438Returns all the triggers as an array or array reference.
439
440 my @triggers = $schema->get_triggers;
441
442=cut
443
444 my $self = shift;
445 my @triggers =
446 map { $_->[1] }
447 sort { $a->[0] <=> $b->[0] }
448 map { [ $_->order, $_ ] }
449 values %{ $self->{'triggers'} };
450
451 if ( @triggers ) {
452 return wantarray ? @triggers : \@triggers;
453 }
454 else {
455 $self->error('No triggers');
456 return wantarray ? () : undef;
457 }
458}
459
460# ----------------------------------------------------------------------
76dce619 461sub get_view {
462
463=pod
464
465=head2 get_view
466
467Returns a view by the name provided.
468
469 my $view = $schema->get_view('foo');
3c5de62a 470
471=cut
472
473 my $self = shift;
76dce619 474 my $view_name = shift or return $self->error('No view name');
475 return $self->error('View "$view_name" does not exist') unless
476 exists $self->{'views'}{ $view_name };
477 return $self->{'views'}{ $view_name };
478}
3c5de62a 479
76dce619 480# ----------------------------------------------------------------------
481sub get_views {
3c5de62a 482
76dce619 483=pod
484
485=head2 get_views
486
487Returns all the views as an array or array reference.
488
489 my @views = $schema->get_views;
490
491=cut
492
493 my $self = shift;
d0b43695 494 my @views =
495 map { $_->[1] }
496 sort { $a->[0] <=> $b->[0] }
497 map { [ $_->order, $_ ] }
99248301 498 values %{ $self->{'views'} };
76dce619 499
500 if ( @views ) {
501 return wantarray ? @views : \@views;
502 }
503 else {
504 $self->error('No views');
505 return wantarray ? () : undef;
506 }
3c5de62a 507}
508
99248301 509# ----------------------------------------------------------------------
9480e70b 510sub make_natural_joins {
511
512=pod
513
514=head2 make_natural_joins
515
516Creates foriegn key relationships among like-named fields in different
517tables. Accepts the following arguments:
518
519=over 4
520
521=item * join_pk_only
522
523A True or False argument which determins whether or not to perform
524the joins from primary keys to fields of the same name in other tables
525
526=item * skip_fields
527
528A list of fields to skip in the joins
529
530=back 4
531
532 $schema->make_natural_joins(
533 join_pk_only => 1,
534 skip_fields => 'name,department_id',
535 );
536
537=cut
538
539 my $self = shift;
540 my %args = @_;
541 my $join_pk_only = $args{'join_pk_only'} || 0;
40c522c6 542 my %skip_fields = map { s/^\s+|\s+$//g; $_, 1 } @{
543 parse_list_arg( $args{'skip_fields'} )
544 };
9480e70b 545
546 my ( %common_keys, %pk );
547 for my $table ( $self->get_tables ) {
548 for my $field ( $table->get_fields ) {
549 my $field_name = $field->name or next;
550 next if $skip_fields{ $field_name };
551 $pk{ $field_name } = 1 if $field->is_primary_key;
552 push @{ $common_keys{ $field_name } }, $table->name;
553 }
554 }
555
556 for my $field ( keys %common_keys ) {
557 next if $join_pk_only and !defined $pk{ $field };
558
559 my @table_names = @{ $common_keys{ $field } };
560 next unless scalar @table_names > 1;
561
562 for my $i ( 0 .. $#table_names ) {
563 my $table1 = $self->get_table( $table_names[ $i ] ) or next;
564
565 for my $j ( 1 .. $#table_names ) {
566 my $table2 = $self->get_table( $table_names[ $j ] ) or next;
567 next if $table1->name eq $table2->name;
568
569 $table1->add_constraint(
570 type => FOREIGN_KEY,
571 fields => $field,
572 reference_table => $table2->name,
573 reference_fields => $field,
574 );
575 }
576 }
577 }
578
579 return 1;
580}
581
582# ----------------------------------------------------------------------
99248301 583sub name {
584
585=pod
586
587=head2 name
588
589Get or set the schema's name. (optional)
590
591 my $schema_name = $schema->name('Foo Database');
592
593=cut
594
595 my $self = shift;
596 $self->{'name'} = shift if @_;
597 return $self->{'name'} || '';
598}
599
d0b43695 600# ----------------------------------------------------------------------
601sub DESTROY {
602 my $self = shift;
603 undef $_ for values %{ $self->{'tables'} };
604 undef $_ for values %{ $self->{'views'} };
605}
606
3c5de62a 6071;
608
609# ----------------------------------------------------------------------
610
611=pod
612
613=head1 AUTHOR
614
615Ken Y. Clark E<lt>kclark@cpan.orgE<gt>
616
617=cut