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