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