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