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