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