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