Basic test for SQL::Translator::Shell
[dbsrgits/SQL-Translator.git] / lib / SQL / Translator / Schema.pm
CommitLineData
3c5de62a 1package SQL::Translator::Schema;
2
3# ----------------------------------------------------------------------
9bfce1c2 4# $Id: Schema.pm,v 1.8 2003-06-18 23:14:31 kycl4rk Exp $
3c5de62a 5# ----------------------------------------------------------------------
6# Copyright (C) 2003 Ken Y. Clark <kclark@cpan.org>
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;
3c5de62a 48use SQL::Translator::Schema::Table;
40c522c6 49use SQL::Translator::Utils 'parse_list_arg';
3c5de62a 50use SQL::Translator::Schema::View;
51
52use base 'Class::Base';
76dce619 53use vars qw[ $VERSION $TABLE_ORDER $VIEW_ORDER ];
3c5de62a 54
9bfce1c2 55$VERSION = sprintf "%d.%02d", q$Revision: 1.8 $ =~ /(\d+)\.(\d+)/;
3c5de62a 56
57# ----------------------------------------------------------------------
58sub init {
59
60=pod
61
62=head2 new
63
64Object constructor.
65
99248301 66 my $schema = SQL::Translator->new(
67 name => 'Foo',
68 database => 'MySQL',
69 );
3c5de62a 70
71=cut
72
73 my ( $self, $config ) = @_;
99248301 74 $self->params( $config, qw[ name database ] ) || return undef;
3c5de62a 75 return $self;
76}
77
78# ----------------------------------------------------------------------
76dce619 79sub add_table {
3c5de62a 80
81=pod
82
76dce619 83=head2 add_table
3c5de62a 84
76dce619 85Add a table object. Returns the new SQL::Translator::Schema::Table object.
99248301 86The "name" parameter is required. If you try to create a table with the
87same name as an existing table, you will get an error and the table will
88not be created.
3c5de62a 89
68e8e2e1 90 my $t1 = $schema->add_table( name => 'foo' ) or die $schema->error;
91 my $t2 = SQL::Translator::Schema::Table->new( name => 'bar' );
92 $t2 = $schema->add_table( $table_bar ) or die $schema->error;
3c5de62a 93
94=cut
95
99248301 96 my $self = shift;
97 my $table_class = 'SQL::Translator::Schema::Table';
98 my $table;
99
100 if ( UNIVERSAL::isa( $_[0], $table_class ) ) {
101 $table = shift;
102 $table->schema( $self );
103 }
104 else {
105 my %args = @_;
106 $args{'schema'} = $self;
107 $table = $table_class->new( \%args ) or return
108 $self->error( $table_class->error );
109 }
3c5de62a 110
d0b43695 111 $table->order( ++$TABLE_ORDER );
99248301 112 my $table_name = $table->name or return $self->error('No table name');
113
114 if ( defined $self->{'tables'}{ $table_name } ) {
115 return $self->error(qq[Can't create table: "$table_name" exists]);
116 }
117 else {
118 $self->{'tables'}{ $table_name } = $table;
99248301 119 }
3c5de62a 120
121 return $table;
122}
123
124# ----------------------------------------------------------------------
76dce619 125sub add_view {
3c5de62a 126
127=pod
128
76dce619 129=head2 add_view
3c5de62a 130
76dce619 131Add a view object. Returns the new SQL::Translator::Schema::View object.
99248301 132The "name" parameter is required. If you try to create a view with the
133same name as an existing view, you will get an error and the view will
134not be created.
135
68e8e2e1 136 my $v1 = $schema->add_view( name => 'foo' );
137 my $v2 = SQL::Translator::Schema::View->new( name => 'bar' );
138 $v2 = $schema->add_view( $view_bar ) or die $schema->error;
3c5de62a 139
140=cut
141
99248301 142 my $self = shift;
143 my $view_class = 'SQL::Translator::Schema::View';
144 my $view;
145
146 if ( UNIVERSAL::isa( $_[0], $view_class ) ) {
147 $view = shift;
148 }
149 else {
150 my %args = @_;
151 return $self->error('No view name') unless $args{'name'};
152 $view = $view_class->new( \%args ) or return $view_class->error;
153 }
3c5de62a 154
d0b43695 155 $view->order( ++$VIEW_ORDER );
99248301 156 my $view_name = $view->name or return $self->error('No view name');
157
158 if ( defined $self->{'views'}{ $view_name } ) {
159 return $self->error(qq[Can't create view: "$view_name" exists]);
160 }
161 else {
162 $self->{'views'}{ $view_name } = $view;
99248301 163 }
3c5de62a 164
76dce619 165 return $view;
3c5de62a 166}
167
168# ----------------------------------------------------------------------
99248301 169sub database {
170
171=pod
172
173=head2 database
174
175Get or set the schema's database. (optional)
176
177 my $database = $schema->database('PostgreSQL');
178
179=cut
180
181 my $self = shift;
182 $self->{'database'} = shift if @_;
183 return $self->{'database'} || '';
184}
185
186# ----------------------------------------------------------------------
76dce619 187sub is_valid {
3c5de62a 188
189=pod
190
76dce619 191=head2 is_valid
3c5de62a 192
76dce619 193Returns true if all the tables and views are valid.
3c5de62a 194
76dce619 195 my $ok = $schema->is_valid or die $schema->error;
196
197=cut
198
199 my $self = shift;
200
201 return $self->error('No tables') unless $self->get_tables;
202
203 for my $object ( $self->get_tables, $self->get_views ) {
204 return $object->error unless $object->is_valid;
205 }
206
207 return 1;
208}
209
210# ----------------------------------------------------------------------
211sub get_table {
212
213=pod
214
215=head2 get_table
216
217Returns a table by the name provided.
218
219 my $table = $schema->get_table('foo');
220
221=cut
222
223 my $self = shift;
224 my $table_name = shift or return $self->error('No table name');
99248301 225 return $self->error( qq[Table "$table_name" does not exist] ) unless
76dce619 226 exists $self->{'tables'}{ $table_name };
227 return $self->{'tables'}{ $table_name };
228}
229
230# ----------------------------------------------------------------------
231sub get_tables {
232
233=pod
234
235=head2 get_tables
236
237Returns all the tables as an array or array reference.
238
239 my @tables = $schema->get_tables;
240
241=cut
242
243 my $self = shift;
d0b43695 244 my @tables =
245 map { $_->[1] }
246 sort { $a->[0] <=> $b->[0] }
247 map { [ $_->order, $_ ] }
76dce619 248 values %{ $self->{'tables'} };
249
250 if ( @tables ) {
251 return wantarray ? @tables : \@tables;
252 }
253 else {
254 $self->error('No tables');
255 return wantarray ? () : undef;
256 }
257}
258
259# ----------------------------------------------------------------------
260sub get_view {
261
262=pod
263
264=head2 get_view
265
266Returns a view by the name provided.
267
268 my $view = $schema->get_view('foo');
3c5de62a 269
270=cut
271
272 my $self = shift;
76dce619 273 my $view_name = shift or return $self->error('No view name');
274 return $self->error('View "$view_name" does not exist') unless
275 exists $self->{'views'}{ $view_name };
276 return $self->{'views'}{ $view_name };
277}
3c5de62a 278
76dce619 279# ----------------------------------------------------------------------
280sub get_views {
3c5de62a 281
76dce619 282=pod
283
284=head2 get_views
285
286Returns all the views as an array or array reference.
287
288 my @views = $schema->get_views;
289
290=cut
291
292 my $self = shift;
d0b43695 293 my @views =
294 map { $_->[1] }
295 sort { $a->[0] <=> $b->[0] }
296 map { [ $_->order, $_ ] }
99248301 297 values %{ $self->{'views'} };
76dce619 298
299 if ( @views ) {
300 return wantarray ? @views : \@views;
301 }
302 else {
303 $self->error('No views');
304 return wantarray ? () : undef;
305 }
3c5de62a 306}
307
99248301 308# ----------------------------------------------------------------------
9480e70b 309sub make_natural_joins {
310
311=pod
312
313=head2 make_natural_joins
314
315Creates foriegn key relationships among like-named fields in different
316tables. Accepts the following arguments:
317
318=over 4
319
320=item * join_pk_only
321
322A True or False argument which determins whether or not to perform
323the joins from primary keys to fields of the same name in other tables
324
325=item * skip_fields
326
327A list of fields to skip in the joins
328
329=back 4
330
331 $schema->make_natural_joins(
332 join_pk_only => 1,
333 skip_fields => 'name,department_id',
334 );
335
336=cut
337
338 my $self = shift;
339 my %args = @_;
340 my $join_pk_only = $args{'join_pk_only'} || 0;
40c522c6 341 my %skip_fields = map { s/^\s+|\s+$//g; $_, 1 } @{
342 parse_list_arg( $args{'skip_fields'} )
343 };
9480e70b 344
345 my ( %common_keys, %pk );
346 for my $table ( $self->get_tables ) {
347 for my $field ( $table->get_fields ) {
348 my $field_name = $field->name or next;
349 next if $skip_fields{ $field_name };
350 $pk{ $field_name } = 1 if $field->is_primary_key;
351 push @{ $common_keys{ $field_name } }, $table->name;
352 }
353 }
354
355 for my $field ( keys %common_keys ) {
356 next if $join_pk_only and !defined $pk{ $field };
357
358 my @table_names = @{ $common_keys{ $field } };
359 next unless scalar @table_names > 1;
360
361 for my $i ( 0 .. $#table_names ) {
362 my $table1 = $self->get_table( $table_names[ $i ] ) or next;
363
364 for my $j ( 1 .. $#table_names ) {
365 my $table2 = $self->get_table( $table_names[ $j ] ) or next;
366 next if $table1->name eq $table2->name;
367
368 $table1->add_constraint(
369 type => FOREIGN_KEY,
370 fields => $field,
371 reference_table => $table2->name,
372 reference_fields => $field,
373 );
374 }
375 }
376 }
377
378 return 1;
379}
380
381# ----------------------------------------------------------------------
99248301 382sub name {
383
384=pod
385
386=head2 name
387
388Get or set the schema's name. (optional)
389
390 my $schema_name = $schema->name('Foo Database');
391
392=cut
393
394 my $self = shift;
395 $self->{'name'} = shift if @_;
396 return $self->{'name'} || '';
397}
398
d0b43695 399# ----------------------------------------------------------------------
400sub DESTROY {
401 my $self = shift;
402 undef $_ for values %{ $self->{'tables'} };
403 undef $_ for values %{ $self->{'views'} };
404}
405
3c5de62a 4061;
407
408# ----------------------------------------------------------------------
409
410=pod
411
412=head1 AUTHOR
413
414Ken Y. Clark E<lt>kclark@cpan.orgE<gt>
415
416=cut