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