Removed auto_increment from a field's extra attribute; it's already saved in is_auto_...
[dbsrgits/SQL-Translator.git] / lib / SQL / Translator / Schema / Table.pm
CommitLineData
3c5de62a 1package SQL::Translator::Schema::Table;
2
3# ----------------------------------------------------------------------
4598b71c 4# $Id: Table.pm,v 1.33 2005-06-29 22:02:29 duality72 Exp $
3c5de62a 5# ----------------------------------------------------------------------
6606c4c6 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::Table - SQL::Translator table object
28
29=head1 SYNOPSIS
30
31 use SQL::Translator::Schema::Table;
0f3cc5c0 32 my $table = SQL::Translator::Schema::Table->new( name => 'foo' );
3c5de62a 33
34=head1 DESCSIPTION
35
36C<SQL::Translator::Schema::Table> is the table object.
37
38=head1 METHODS
39
40=cut
41
42use strict;
30f4ec44 43use SQL::Translator::Utils 'parse_list_arg';
0f3cc5c0 44use SQL::Translator::Schema::Constants;
3c5de62a 45use SQL::Translator::Schema::Constraint;
46use SQL::Translator::Schema::Field;
47use SQL::Translator::Schema::Index;
c8515c9f 48use Data::Dumper;
3c5de62a 49
b6a880d1 50use base 'SQL::Translator::Schema::Object';
51
0f3cc5c0 52use vars qw( $VERSION $FIELD_ORDER );
3c5de62a 53
4598b71c 54$VERSION = sprintf "%d.%02d", q$Revision: 1.33 $ =~ /(\d+)\.(\d+)/;
65dd38c0 55
56
57# Stringify to our name, being careful not to pass any args through so we don't
58# accidentally set it to undef. We also have to tweak bool so the object is
59# still true when it doesn't have a name (which shouldn't happen!).
60use overload
61 '""' => sub { shift->name },
62 'bool' => sub { $_[0]->name || $_[0] },
63 fallback => 1,
64;
3c5de62a 65
66# ----------------------------------------------------------------------
9371be50 67
68__PACKAGE__->_attributes( qw/schema name comments options order/ );
3c5de62a 69
70=pod
71
72=head2 new
73
74Object constructor.
75
43b9dc7a 76 my $table = SQL::Translator::Schema::Table->new(
77 schema => $schema,
78 name => 'foo',
79 );
3c5de62a 80
81=cut
82
3c5de62a 83# ----------------------------------------------------------------------
3c5de62a 84sub add_constraint {
85
86=pod
87
88=head2 add_constraint
89
0f3cc5c0 90Add a constraint to the table. Returns the newly created
91C<SQL::Translator::Schema::Constraint> object.
3c5de62a 92
870024f3 93 my $c1 = $table->add_constraint(
94 name => 'pk',
95 type => PRIMARY_KEY,
96 fields => [ 'foo_id' ],
3c5de62a 97 );
98
dfdb0568 99 my $c2 = SQL::Translator::Schema::Constraint->new( name => 'uniq' );
100 $c2 = $table->add_constraint( $constraint );
43b9dc7a 101
3c5de62a 102=cut
103
43b9dc7a 104 my $self = shift;
105 my $constraint_class = 'SQL::Translator::Schema::Constraint';
106 my $constraint;
107
108 if ( UNIVERSAL::isa( $_[0], $constraint_class ) ) {
109 $constraint = shift;
110 $constraint->table( $self );
111 }
112 else {
113 my %args = @_;
114 $args{'table'} = $self;
115 $constraint = $constraint_class->new( \%args ) or
b1789409 116 return $self->error( $constraint_class->error );
43b9dc7a 117 }
118
dfdb0568 119 #
120 # If we're trying to add a PK when one is already defined,
121 # then just add the fields to the existing definition.
122 #
3dd9026c 123 my $ok = 1;
dfdb0568 124 my $pk = $self->primary_key;
125 if ( $pk && $constraint->type eq PRIMARY_KEY ) {
126 $self->primary_key( $constraint->fields );
b1789409 127 $pk->name($constraint->name) if $constraint->name;
128 my %extra = $constraint->extra;
129 $pk->extra(%extra) if keys %extra;
dfdb0568 130 $constraint = $pk;
3dd9026c 131 $ok = 0;
dfdb0568 132 }
2ccf2299 133 elsif ( $constraint->type eq PRIMARY_KEY ) {
134 for my $fname ( $constraint->fields ) {
135 if ( my $f = $self->get_field( $fname ) ) {
136 $f->is_primary_key( 1 );
137 }
138 }
139 }
3dd9026c 140 #
141 # See if another constraint of the same type
be53b4c8 142 # covers the same fields. -- This doesn't work! ky
3dd9026c 143 #
be53b4c8 144# elsif ( $constraint->type ne CHECK_C ) {
145# my @field_names = $constraint->fields;
146# for my $c (
147# grep { $_->type eq $constraint->type }
148# $self->get_constraints
149# ) {
150# my %fields = map { $_, 1 } $c->fields;
151# for my $field_name ( @field_names ) {
152# if ( $fields{ $field_name } ) {
153# $constraint = $c;
154# $ok = 0;
155# last;
156# }
157# }
158# last unless $ok;
159# }
160# }
dfdb0568 161
162 if ( $ok ) {
163 push @{ $self->{'constraints'} }, $constraint;
164 }
165
3c5de62a 166 return $constraint;
167}
168
169# ----------------------------------------------------------------------
650f87eb 170sub drop_constraint {
171
172=pod
173
174=head2 drop_constraint
175
176Remove a constraint from the table. Returns the constraint object if the index
177was found and removed, an error otherwise. The single parameter can be either
178an index name or an C<SQL::Translator::Schema::Constraint> object.
179
180 $table->drop_constraint('myconstraint');
181
182=cut
183
184 my $self = shift;
185 my $constraint_class = 'SQL::Translator::Schema::Constraint';
186 my $constraint_name;
187
188 if ( UNIVERSAL::isa( $_[0], $constraint_class ) ) {
189 $constraint_name = shift->name;
190 }
191 else {
192 $constraint_name = shift;
193 }
194
195 if ( ! grep { $_->name eq $constraint_name } @ { $self->{'constraints'} } ) {
196 return $self->error(qq[Can't drop constraint: "$constraint_name" doesn't exist]);
197 }
198
199 my @cs = @{ $self->{'constraints'} };
200 my ($constraint_id) = grep { $cs[$_]->name eq $constraint_name } (0..$#cs);
201 my $constraint = splice(@{$self->{'constraints'}}, $constraint_id, 1);
202
203 return $constraint;
204}
205
206# ----------------------------------------------------------------------
3c5de62a 207sub add_index {
208
209=pod
210
211=head2 add_index
212
0f3cc5c0 213Add an index to the table. Returns the newly created
214C<SQL::Translator::Schema::Index> object.
3c5de62a 215
870024f3 216 my $i1 = $table->add_index(
3c5de62a 217 name => 'name',
218 fields => [ 'name' ],
219 type => 'normal',
220 );
221
dfdb0568 222 my $i2 = SQL::Translator::Schema::Index->new( name => 'id' );
223 $i2 = $table->add_index( $index );
43b9dc7a 224
3c5de62a 225=cut
226
43b9dc7a 227 my $self = shift;
228 my $index_class = 'SQL::Translator::Schema::Index';
229 my $index;
230
231 if ( UNIVERSAL::isa( $_[0], $index_class ) ) {
232 $index = shift;
233 $index->table( $self );
234 }
235 else {
236 my %args = @_;
237 $args{'table'} = $self;
238 $index = $index_class->new( \%args ) or return
239 $self->error( $index_class->error );
240 }
241
3c5de62a 242 push @{ $self->{'indices'} }, $index;
243 return $index;
244}
245
246# ----------------------------------------------------------------------
650f87eb 247sub drop_index {
248
249=pod
250
251=head2 drop_index
252
253Remove an index from the table. Returns the index object if the index was
254found and removed, an error otherwise. The single parameter can be either
255an index name of an C<SQL::Translator::Schema::Index> object.
256
257 $table->drop_index('myindex');
258
259=cut
260
261 my $self = shift;
262 my $index_class = 'SQL::Translator::Schema::Index';
263 my $index_name;
264
265 if ( UNIVERSAL::isa( $_[0], $index_class ) ) {
266 $index_name = shift->name;
267 }
268 else {
269 $index_name = shift;
270 }
271
272 if ( ! grep { $_->name eq $index_name } @{ $self->{'indices'} }) {
273 return $self->error(qq[Can't drop index: "$index_name" doesn't exist]);
274 }
275
276 my @is = @{ $self->{'indices'} };
277 my ($index_id) = grep { $is[$_]->name eq $index_name } (0..$#is);
278 my $index = splice(@{$self->{'indices'}}, $index_id, 1);
279
280 return $index;
281}
282
283# ----------------------------------------------------------------------
3c5de62a 284sub add_field {
285
286=pod
287
288=head2 add_field
289
43b9dc7a 290Add an field to the table. Returns the newly created
291C<SQL::Translator::Schema::Field> object. The "name" parameter is
292required. If you try to create a field with the same name as an
293existing field, you will get an error and the field will not be created.
3c5de62a 294
870024f3 295 my $f1 = $table->add_field(
0f3cc5c0 296 name => 'foo_id',
297 data_type => 'integer',
298 size => 11,
3c5de62a 299 );
300
870024f3 301 my $f2 = SQL::Translator::Schema::Field->new(
43b9dc7a 302 name => 'name',
303 table => $table,
304 );
870024f3 305 $f2 = $table->add_field( $field2 ) or die $table->error;
43b9dc7a 306
3c5de62a 307=cut
308
dfdb0568 309 my $self = shift;
43b9dc7a 310 my $field_class = 'SQL::Translator::Schema::Field';
311 my $field;
312
313 if ( UNIVERSAL::isa( $_[0], $field_class ) ) {
314 $field = shift;
315 $field->table( $self );
316 }
317 else {
318 my %args = @_;
319 $args{'table'} = $self;
320 $field = $field_class->new( \%args ) or return
321 $self->error( $field_class->error );
322 }
323
30f4ec44 324 $field->order( ++$FIELD_ORDER );
65dd38c0 325 # We know we have a name as the Field->new above errors if none given.
326 my $field_name = $field->name;
43b9dc7a 327
328 if ( exists $self->{'fields'}{ $field_name } ) {
870024f3 329 return $self->error(qq[Can't create field: "$field_name" exists]);
43b9dc7a 330 }
331 else {
332 $self->{'fields'}{ $field_name } = $field;
43b9dc7a 333 }
334
3c5de62a 335 return $field;
336}
650f87eb 337# ----------------------------------------------------------------------
338sub drop_field {
339
340=pod
341
342=head2 drop_field
343
344Remove a field from the table. Returns the field object if the field was
345found and removed, an error otherwise. The single parameter can be either
346a field name or an C<SQL::Translator::Schema::Field> object.
347
348 $table->drop_field('myfield');
349
350=cut
351
352 my $self = shift;
353 my $field_class = 'SQL::Translator::Schema::Field';
354 my $field_name;
355
356 if ( UNIVERSAL::isa( $_[0], $field_class ) ) {
357 $field_name = shift->name;
358 }
359 else {
360 $field_name = shift;
361 }
362 my %args = @_;
363 my $cascade = $args{'cascade'};
364
365 if ( ! exists $self->{'fields'}{ $field_name } ) {
366 return $self->error(qq[Can't drop field: "$field_name" doesn't exists]);
367 }
368
369 my $field = delete $self->{'fields'}{ $field_name };
370
371 if ( $cascade ) {
372 # Remove this field from all indices using it
373 foreach my $i ($self->get_indices()) {
374 my @fs = $i->fields();
375 @fs = grep { $_ ne $field->name } @fs;
376 $i->fields(@fs);
377 }
378
379 # Remove this field from all constraints using it
380 foreach my $c ($self->get_constraints()) {
381 my @cs = $c->fields();
382 @cs = grep { $_ ne $field->name } @cs;
383 $c->fields(@cs);
384 }
385 }
386
387 return $field;
388}
3c5de62a 389
390# ----------------------------------------------------------------------
88b8377e 391sub comments {
392
393=pod
394
395=head2 comments
396
397Get or set the comments on a table. May be called several times to
398set and it will accumulate the comments. Called in an array context,
399returns each comment individually; called in a scalar context, returns
400all the comments joined on newlines.
401
402 $table->comments('foo');
403 $table->comments('bar');
404 print join( ', ', $table->comments ); # prints "foo, bar"
405
406=cut
407
eb3b8ae4 408 my $self = shift;
409 my @comments = ref $_[0] ? @{ $_[0] } : @_;
b891fb49 410
eb3b8ae4 411 for my $arg ( @comments ) {
b891fb49 412 $arg = $arg->[0] if ref $arg;
eb3b8ae4 413 push @{ $self->{'comments'} }, $arg if defined $arg && $arg;
b891fb49 414 }
88b8377e 415
eb3b8ae4 416 if ( @{ $self->{'comments'} || [] } ) {
417 return wantarray
418 ? @{ $self->{'comments'} }
419 : join( "\n", @{ $self->{'comments'} } )
420 ;
421 }
422 else {
423 return wantarray ? () : undef;
424 }
88b8377e 425}
426
427# ----------------------------------------------------------------------
0f3cc5c0 428sub get_constraints {
429
430=pod
431
432=head2 get_constraints
433
434Returns all the constraint objects as an array or array reference.
435
436 my @constraints = $table->get_constraints;
437
438=cut
439
440 my $self = shift;
441
442 if ( ref $self->{'constraints'} ) {
443 return wantarray
444 ? @{ $self->{'constraints'} } : $self->{'constraints'};
445 }
446 else {
447 $self->error('No constraints');
448 return wantarray ? () : undef;
449 }
450}
451
452# ----------------------------------------------------------------------
453sub get_indices {
3c5de62a 454
455=pod
456
0f3cc5c0 457=head2 get_indices
3c5de62a 458
0f3cc5c0 459Returns all the index objects as an array or array reference.
3c5de62a 460
0f3cc5c0 461 my @indices = $table->get_indices;
3c5de62a 462
463=cut
464
465 my $self = shift;
0f3cc5c0 466
467 if ( ref $self->{'indices'} ) {
468 return wantarray
469 ? @{ $self->{'indices'} }
470 : $self->{'indices'};
471 }
472 else {
473 $self->error('No indices');
474 return wantarray ? () : undef;
475 }
476}
477
478# ----------------------------------------------------------------------
43b9dc7a 479sub get_field {
480
481=pod
482
483=head2 get_field
484
485Returns a field by the name provided.
486
487 my $field = $table->get_field('foo');
488
489=cut
490
491 my $self = shift;
492 my $field_name = shift or return $self->error('No field name');
493 return $self->error( qq[Field "$field_name" does not exist] ) unless
494 exists $self->{'fields'}{ $field_name };
495 return $self->{'fields'}{ $field_name };
496}
497
498# ----------------------------------------------------------------------
0f3cc5c0 499sub get_fields {
500
501=pod
502
503=head2 get_fields
504
505Returns all the field objects as an array or array reference.
506
507 my @fields = $table->get_fields;
508
509=cut
510
511 my $self = shift;
512 my @fields =
30f4ec44 513 map { $_->[1] }
514 sort { $a->[0] <=> $b->[0] }
515 map { [ $_->order, $_ ] }
0f3cc5c0 516 values %{ $self->{'fields'} || {} };
517
518 if ( @fields ) {
519 return wantarray ? @fields : \@fields;
520 }
521 else {
522 $self->error('No fields');
523 return wantarray ? () : undef;
524 }
3c5de62a 525}
526
527# ----------------------------------------------------------------------
528sub is_valid {
529
530=pod
531
532=head2 is_valid
533
534Determine whether the view is valid or not.
535
536 my $ok = $view->is_valid;
537
538=cut
539
540 my $self = shift;
43b9dc7a 541 return $self->error('No name') unless $self->name;
0f3cc5c0 542 return $self->error('No fields') unless $self->get_fields;
543
544 for my $object (
545 $self->get_fields, $self->get_indices, $self->get_constraints
546 ) {
547 return $object->error unless $object->is_valid;
548 }
549
550 return 1;
3c5de62a 551}
552
870024f3 553# ----------------------------------------------------------------------
65157eda 554sub is_trivial_link {
555
556=pod
557
719915f2 558=head2 is_trivial_link
559
560True if table has no data (non-key) fields and only uses single key joins.
65157eda 561
562=cut
563
564 my $self = shift;
565 return 0 if $self->is_data;
566 return $self->{'is_trivial_link'} if defined $self->{'is_trivial_link'};
567
568 $self->{'is_trivial_link'} = 1;
569
570 my %fk = ();
571
572 foreach my $field ( $self->get_fields ) {
573 next unless $field->is_foreign_key;
574 $fk{$field->foreign_key_reference->reference_table}++;
575 }
576
577 foreach my $referenced (keys %fk){
f9c5e794 578 if($fk{$referenced} > 1){
579 $self->{'is_trivial_link'} = 0;
580 last;
65157eda 581 }
3d6c9056 582 }
65157eda 583
584 return $self->{'is_trivial_link'};
585
586}
587
69c7a62f 588sub is_data {
69c7a62f 589
870024f3 590=pod
591
592=head2 is_data
593
719915f2 594Returns true if the table has some non-key fields.
595
870024f3 596=cut
597
598 my $self = shift;
599 return $self->{'is_data'} if defined $self->{'is_data'};
69c7a62f 600
870024f3 601 $self->{'is_data'} = 0;
69c7a62f 602
870024f3 603 foreach my $field ( $self->get_fields ) {
604 if ( !$field->is_primary_key and !$field->is_foreign_key ) {
605 $self->{'is_data'} = 1;
606 return $self->{'is_data'};
607 }
608 }
609
610 return $self->{'is_data'};
69c7a62f 611}
612
870024f3 613# ----------------------------------------------------------------------
69c7a62f 614sub can_link {
615
616=pod
617
618=head2 can_link
619
620Determine whether the table can link two arg tables via many-to-many.
621
622 my $ok = $table->can_link($table1,$table2);
623
624=cut
625
870024f3 626 my ( $self, $table1, $table2 ) = @_;
627
628 return $self->{'can_link'}{ $table1->name }{ $table2->name }
629 if defined $self->{'can_link'}{ $table1->name }{ $table2->name };
630
631 if ( $self->is_data == 1 ) {
632 $self->{'can_link'}{ $table1->name }{ $table2->name } = [0];
633 $self->{'can_link'}{ $table2->name }{ $table1->name } = [0];
634 return $self->{'can_link'}{ $table1->name }{ $table2->name };
635 }
636
637 my %fk = ();
638
639 foreach my $field ( $self->get_fields ) {
640 if ( $field->is_foreign_key ) {
641 push @{ $fk{ $field->foreign_key_reference->reference_table } },
642 $field->foreign_key_reference;
643 }
644 }
645
646 if ( !defined( $fk{ $table1->name } ) or !defined( $fk{ $table2->name } ) )
647 {
648 $self->{'can_link'}{ $table1->name }{ $table2->name } = [0];
649 $self->{'can_link'}{ $table2->name }{ $table1->name } = [0];
650 return $self->{'can_link'}{ $table1->name }{ $table2->name };
651 }
652
653 # trivial traversal, only one way to link the two tables
654 if ( scalar( @{ $fk{ $table1->name } } == 1 )
655 and scalar( @{ $fk{ $table2->name } } == 1 ) )
656 {
657 $self->{'can_link'}{ $table1->name }{ $table2->name } =
658 [ 'one2one', $fk{ $table1->name }, $fk{ $table2->name } ];
659 $self->{'can_link'}{ $table1->name }{ $table2->name } =
660 [ 'one2one', $fk{ $table2->name }, $fk{ $table1->name } ];
661
662 # non-trivial traversal. one way to link table2,
663 # many ways to link table1
664 }
665 elsif ( scalar( @{ $fk{ $table1->name } } > 1 )
666 and scalar( @{ $fk{ $table2->name } } == 1 ) )
667 {
668 $self->{'can_link'}{ $table1->name }{ $table2->name } =
669 [ 'many2one', $fk{ $table1->name }, $fk{ $table2->name } ];
670 $self->{'can_link'}{ $table2->name }{ $table1->name } =
671 [ 'one2many', $fk{ $table2->name }, $fk{ $table1->name } ];
672
673 # non-trivial traversal. one way to link table1,
674 # many ways to link table2
675 }
676 elsif ( scalar( @{ $fk{ $table1->name } } == 1 )
677 and scalar( @{ $fk{ $table2->name } } > 1 ) )
678 {
679 $self->{'can_link'}{ $table1->name }{ $table2->name } =
680 [ 'one2many', $fk{ $table1->name }, $fk{ $table2->name } ];
681 $self->{'can_link'}{ $table2->name }{ $table1->name } =
682 [ 'many2one', $fk{ $table2->name }, $fk{ $table1->name } ];
683
684 # non-trivial traversal. many ways to link table1 and table2
685 }
686 elsif ( scalar( @{ $fk{ $table1->name } } > 1 )
687 and scalar( @{ $fk{ $table2->name } } > 1 ) )
688 {
689 $self->{'can_link'}{ $table1->name }{ $table2->name } =
690 [ 'many2many', $fk{ $table1->name }, $fk{ $table2->name } ];
691 $self->{'can_link'}{ $table2->name }{ $table1->name } =
692 [ 'many2many', $fk{ $table2->name }, $fk{ $table1->name } ];
693
694 # one of the tables didn't export a key
695 # to this table, no linking possible
696 }
697 else {
698 $self->{'can_link'}{ $table1->name }{ $table2->name } = [0];
699 $self->{'can_link'}{ $table2->name }{ $table1->name } = [0];
700 }
701
702 return $self->{'can_link'}{ $table1->name }{ $table2->name };
69c7a62f 703}
704
43b9dc7a 705# ----------------------------------------------------------------------
dfdb0568 706sub name {
707
708=pod
709
710=head2 name
711
870024f3 712Get or set the table's name.
dfdb0568 713
65dd38c0 714Errors ("No table name") if you try to set a blank name.
715
716If provided an argument, checks the schema object for a table of
717that name and disallows the change if one exists (setting the error to
718"Can't use table name "%s": table exists").
dfdb0568 719
720 my $table_name = $table->name('foo');
721
722=cut
723
724 my $self = shift;
725
65dd38c0 726 if ( @_ ) {
727 my $arg = shift || return $self->error( "No table name" );
dfdb0568 728 if ( my $schema = $self->schema ) {
870024f3 729 return $self->error( qq[Can't use table name "$arg": table exists] )
dfdb0568 730 if $schema->get_table( $arg );
731 }
732 $self->{'name'} = $arg;
733 }
734
735 return $self->{'name'} || '';
736}
737
738# ----------------------------------------------------------------------
43b9dc7a 739sub schema {
740
741=pod
742
743=head2 schema
744
870024f3 745Get or set the table's schema object.
43b9dc7a 746
747 my $schema = $table->schema;
748
749=cut
750
751 my $self = shift;
752 if ( my $arg = shift ) {
753 return $self->error('Not a schema object') unless
754 UNIVERSAL::isa( $arg, 'SQL::Translator::Schema' );
755 $self->{'schema'} = $arg;
756 }
757
758 return $self->{'schema'};
759}
760
761# ----------------------------------------------------------------------
762sub primary_key {
763
764=pod
765
870024f3 766=head2 primary_key
43b9dc7a 767
870024f3 768Gets or sets the table's primary key(s). Takes one or more field
5e84ac85 769names (as a string, list or array[ref]) as an argument. If the field
770names are present, it will create a new PK if none exists, or it will
771add to the fields of an existing PK (and will unique the field names).
772Returns the C<SQL::Translator::Schema::Constraint> object representing
773the primary key.
774
775These are eqivalent:
43b9dc7a 776
777 $table->primary_key('id');
5e84ac85 778 $table->primary_key(['name']);
779 $table->primary_key('id','name']);
43b9dc7a 780 $table->primary_key(['id','name']);
781 $table->primary_key('id,name');
782 $table->primary_key(qw[ id name ]);
783
784 my $pk = $table->primary_key;
785
786=cut
787
30f4ec44 788 my $self = shift;
789 my $fields = parse_list_arg( @_ );
43b9dc7a 790
5e84ac85 791 my $constraint;
43b9dc7a 792 if ( @$fields ) {
793 for my $f ( @$fields ) {
794 return $self->error(qq[Invalid field "$f"]) unless
795 $self->get_field($f);
796 }
797
798 my $has_pk;
799 for my $c ( $self->get_constraints ) {
800 if ( $c->type eq PRIMARY_KEY ) {
801 $has_pk = 1;
802 $c->fields( @{ $c->fields }, @$fields );
5e84ac85 803 $constraint = $c;
43b9dc7a 804 }
805 }
806
807 unless ( $has_pk ) {
5e84ac85 808 $constraint = $self->add_constraint(
43b9dc7a 809 type => PRIMARY_KEY,
810 fields => $fields,
88b8377e 811 ) or return;
43b9dc7a 812 }
813 }
814
5e84ac85 815 if ( $constraint ) {
816 return $constraint;
817 }
818 else {
819 for my $c ( $self->get_constraints ) {
820 return $c if $c->type eq PRIMARY_KEY;
821 }
43b9dc7a 822 }
823
dfdb0568 824 return;
43b9dc7a 825}
826
827# ----------------------------------------------------------------------
828sub options {
829
830=pod
831
832=head2 options
833
870024f3 834Get or set the table's options (e.g., table types for MySQL). Returns
43b9dc7a 835an array or array reference.
836
837 my @options = $table->options;
838
839=cut
840
841 my $self = shift;
30f4ec44 842 my $options = parse_list_arg( @_ );
43b9dc7a 843
844 push @{ $self->{'options'} }, @$options;
845
846 if ( ref $self->{'options'} ) {
4598b71c 847 return wantarray ? @{ $self->{'options'} || [] } : ($self->{'options'} || '');
43b9dc7a 848 }
849 else {
850 return wantarray ? () : [];
851 }
852}
853
30f4ec44 854# ----------------------------------------------------------------------
855sub order {
856
857=pod
858
859=head2 order
860
870024f3 861Get or set the table's order.
30f4ec44 862
863 my $order = $table->order(3);
864
865=cut
866
867 my ( $self, $arg ) = @_;
868
869 if ( defined $arg && $arg =~ /^\d+$/ ) {
870 $self->{'order'} = $arg;
871 }
872
873 return $self->{'order'} || 0;
874}
875
876# ----------------------------------------------------------------------
719915f2 877sub field_names {
878
879=head2 field_names
880
881Read-only method to return a list or array ref of the field names. Returns undef
882or an empty list if the table has no fields set. Usefull if you want to
883avoid the overload magic of the Field objects returned by the get_fields method.
884
885 my @names = $constraint->field_names;
886
887=cut
888
889 my $self = shift;
890 my @fields =
891 map { $_->name }
892 sort { $a->order <=> $b->order }
893 values %{ $self->{'fields'} || {} };
894
895 if ( @fields ) {
896 return wantarray ? @fields : \@fields;
897 }
898 else {
899 $self->error('No fields');
900 return wantarray ? () : undef;
901 }
902}
903
904# ----------------------------------------------------------------------
abf315bb 905sub equals {
906
907=pod
908
909=head2 equals
910
911Determines if this table is the same as another
912
913 my $isIdentical = $table1->equals( $table2 );
914
915=cut
916
917 my $self = shift;
918 my $other = shift;
919
920 return 0 unless $self->SUPER::equals($other);
921 return 0 unless $self->name eq $other->name;
4598b71c 922 return 0 unless $self->_compare_objects(scalar $self->options, scalar $other->options);
923 return 0 unless $self->_compare_objects(scalar $self->extra, scalar $other->extra);
abf315bb 924
925 # Fields
926 # Go through our fields
927 my %checkedFields;
928 foreach my $field ( $self->get_fields ) {
929 my $otherField = $other->get_field($field->name);
930 return 0 unless $field->equals($otherField);
931 $checkedFields{$field->name} = 1;
932 }
933 # Go through the other table's fields
934 foreach my $otherField ( $other->get_fields ) {
935 next if $checkedFields{$otherField->name};
936 return 0;
937 }
938
939 # Constraints
940 # Go through our constraints
941 my %checkedConstraints;
942CONSTRAINT:
943 foreach my $constraint ( $self->get_constraints ) {
944 foreach my $otherConstraint ( $other->get_constraints ) {
945 if ( $constraint->equals($otherConstraint) ) {
946 $checkedConstraints{$otherConstraint} = 1;
947 next CONSTRAINT;
948 }
949 }
950 return 0;
951 }
952 # Go through the other table's constraints
686b14be 953CONSTRAINT2:
abf315bb 954 foreach my $otherConstraint ( $other->get_constraints ) {
955 next if $checkedFields{$otherConstraint};
686b14be 956 foreach my $constraint ( $self->get_constraints ) {
957 if ( $otherConstraint->equals($constraint) ) {
958 next CONSTRAINT2;
959 }
960 }
abf315bb 961 return 0;
962 }
963
964 # Indices
965 # Go through our indices
966 my %checkedIndices;
967INDEX:
968 foreach my $index ( $self->get_indices ) {
969 foreach my $otherIndex ( $other->get_indices ) {
970 if ( $index->equals($otherIndex) ) {
971 $checkedIndices{$otherIndex} = 1;
972 next INDEX;
973 }
974 }
975 return 0;
976 }
686b14be 977 # Go through the other table's indices
978INDEX2:
abf315bb 979 foreach my $otherIndex ( $other->get_indices ) {
980 next if $checkedIndices{$otherIndex};
686b14be 981 foreach my $index ( $self->get_indices ) {
982 if ( $otherIndex->equals($index) ) {
983 next INDEX2;
984 }
985 }
abf315bb 986 return 0;
987 }
988
989 return 1;
990}
991
992# ----------------------------------------------------------------------
719915f2 993
994=head1 LOOKUP METHODS
995
996The following are a set of shortcut methods for getting commonly used lists of
997fields and constraints. They all return lists or array refs of Field or
998Constraint objects.
999
1000=over 4
1001
1002=item pkey_fields
1003
1004The primary key fields.
1005
1006=item fkey_fields
1007
1008All foreign key fields.
1009
1010=item nonpkey_fields
1011
1012All the fields except the primary key.
1013
1014=item data_fields
1015
1016All non key fields.
1017
1018=item unique_fields
1019
1020All fields with unique constraints.
1021
1022=item unique_constraints
1023
1024All this tables unique constraints.
1025
1026=item fkey_constraints
1027
1028All this tables foreign key constraints. (See primary_key method to get the
1029primary key constraint)
1030
1031=back
1032
1033=cut
1034
1035sub pkey_fields {
1036 my $me = shift;
1037 my @fields = grep { $_->is_primary_key } $me->get_fields;
1038 return wantarray ? @fields : \@fields;
1039}
1040
1041# ----------------------------------------------------------------------
1042sub fkey_fields {
1043 my $me = shift;
1044 my @fields;
1045 push @fields, $_->fields foreach $me->fkey_constraints;
1046 return wantarray ? @fields : \@fields;
1047}
1048
1049# ----------------------------------------------------------------------
1050sub nonpkey_fields {
1051 my $me = shift;
1052 my @fields = grep { !$_->is_primary_key } $me->get_fields;
1053 return wantarray ? @fields : \@fields;
1054}
1055
1056# ----------------------------------------------------------------------
1057sub data_fields {
1058 my $me = shift;
1059 my @fields =
1060 grep { !$_->is_foreign_key and !$_->is_primary_key } $me->get_fields;
1061 return wantarray ? @fields : \@fields;
1062}
1063
1064# ----------------------------------------------------------------------
1065sub unique_fields {
1066 my $me = shift;
1067 my @fields;
1068 push @fields, $_->fields foreach $me->unique_constraints;
1069 return wantarray ? @fields : \@fields;
1070}
1071
1072# ----------------------------------------------------------------------
1073sub unique_constraints {
1074 my $me = shift;
1075 my @cons = grep { $_->type eq UNIQUE } $me->get_constraints;
1076 return wantarray ? @cons : \@cons;
1077}
1078
1079# ----------------------------------------------------------------------
1080sub fkey_constraints {
1081 my $me = shift;
1082 my @cons = grep { $_->type eq FOREIGN_KEY } $me->get_constraints;
1083 return wantarray ? @cons : \@cons;
1084}
1085
1086# ----------------------------------------------------------------------
30f4ec44 1087sub DESTROY {
1088 my $self = shift;
1089 undef $self->{'schema'}; # destroy cyclical reference
1090 undef $_ for @{ $self->{'constraints'} };
1091 undef $_ for @{ $self->{'indices'} };
1092 undef $_ for values %{ $self->{'fields'} };
1093}
1094
3c5de62a 10951;
1096
1097# ----------------------------------------------------------------------
1098
1099=pod
1100
870024f3 1101=head1 AUTHORS
3c5de62a 1102
870024f3 1103Ken Y. Clark E<lt>kclark@cpan.orgE<gt>,
1104Allen Day E<lt>allenday@ucla.eduE<gt>.
3c5de62a 1105
1106=cut