Ignore all TT test while TT is broken
[dbsrgits/SQL-Translator.git] / lib / SQL / Translator / Schema / Table.pm
CommitLineData
3c5de62a 1package SQL::Translator::Schema::Table;
2
3# ----------------------------------------------------------------------
d6d17119 4# $Id: Table.pm,v 1.36 2005-08-10 16:45:40 duality72 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
d6d17119 54$VERSION = sprintf "%d.%02d", q$Revision: 1.36 $ =~ /(\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 }
241
3c5de62a 242 push @{ $self->{'indices'} }, $index;
243 return $index;
244}
245
246# ----------------------------------------------------------------------
650f87eb 247sub drop_index {
248
249=pod
250
251=head2 drop_index
252
253Remove an index from the table. Returns the index object if the index was
254found and removed, an error otherwise. The single parameter can be either
255an index name of an C<SQL::Translator::Schema::Index> object.
256
257 $table->drop_index('myindex');
258
259=cut
260
261 my $self = shift;
262 my $index_class = 'SQL::Translator::Schema::Index';
263 my $index_name;
264
265 if ( UNIVERSAL::isa( $_[0], $index_class ) ) {
266 $index_name = shift->name;
267 }
268 else {
269 $index_name = shift;
270 }
271
272 if ( ! grep { $_->name eq $index_name } @{ $self->{'indices'} }) {
273 return $self->error(qq[Can't drop index: "$index_name" doesn't exist]);
274 }
275
276 my @is = @{ $self->{'indices'} };
277 my ($index_id) = grep { $is[$_]->name eq $index_name } (0..$#is);
278 my $index = splice(@{$self->{'indices'}}, $index_id, 1);
279
280 return $index;
281}
282
283# ----------------------------------------------------------------------
3c5de62a 284sub add_field {
285
286=pod
287
288=head2 add_field
289
43b9dc7a 290Add an field to the table. Returns the newly created
291C<SQL::Translator::Schema::Field> object. The "name" parameter is
292required. If you try to create a field with the same name as an
293existing field, you will get an error and the field will not be created.
3c5de62a 294
870024f3 295 my $f1 = $table->add_field(
0f3cc5c0 296 name => 'foo_id',
297 data_type => 'integer',
298 size => 11,
3c5de62a 299 );
300
870024f3 301 my $f2 = SQL::Translator::Schema::Field->new(
43b9dc7a 302 name => 'name',
303 table => $table,
304 );
870024f3 305 $f2 = $table->add_field( $field2 ) or die $table->error;
43b9dc7a 306
3c5de62a 307=cut
308
dfdb0568 309 my $self = shift;
43b9dc7a 310 my $field_class = 'SQL::Translator::Schema::Field';
311 my $field;
312
313 if ( UNIVERSAL::isa( $_[0], $field_class ) ) {
314 $field = shift;
315 $field->table( $self );
316 }
317 else {
318 my %args = @_;
319 $args{'table'} = $self;
320 $field = $field_class->new( \%args ) or return
321 $self->error( $field_class->error );
322 }
323
30f4ec44 324 $field->order( ++$FIELD_ORDER );
65dd38c0 325 # We know we have a name as the Field->new above errors if none given.
326 my $field_name = $field->name;
43b9dc7a 327
328 if ( exists $self->{'fields'}{ $field_name } ) {
870024f3 329 return $self->error(qq[Can't create field: "$field_name" exists]);
43b9dc7a 330 }
331 else {
332 $self->{'fields'}{ $field_name } = $field;
43b9dc7a 333 }
334
3c5de62a 335 return $field;
336}
650f87eb 337# ----------------------------------------------------------------------
338sub drop_field {
339
340=pod
341
342=head2 drop_field
343
344Remove a field from the table. Returns the field object if the field was
345found and removed, an error otherwise. The single parameter can be either
346a field name or an C<SQL::Translator::Schema::Field> object.
347
348 $table->drop_field('myfield');
349
350=cut
351
352 my $self = shift;
353 my $field_class = 'SQL::Translator::Schema::Field';
354 my $field_name;
355
356 if ( UNIVERSAL::isa( $_[0], $field_class ) ) {
357 $field_name = shift->name;
358 }
359 else {
360 $field_name = shift;
361 }
362 my %args = @_;
363 my $cascade = $args{'cascade'};
364
365 if ( ! exists $self->{'fields'}{ $field_name } ) {
366 return $self->error(qq[Can't drop field: "$field_name" doesn't exists]);
367 }
368
369 my $field = delete $self->{'fields'}{ $field_name };
370
371 if ( $cascade ) {
372 # Remove this field from all indices using it
373 foreach my $i ($self->get_indices()) {
374 my @fs = $i->fields();
375 @fs = grep { $_ ne $field->name } @fs;
376 $i->fields(@fs);
377 }
378
379 # Remove this field from all constraints using it
380 foreach my $c ($self->get_constraints()) {
381 my @cs = $c->fields();
382 @cs = grep { $_ ne $field->name } @cs;
383 $c->fields(@cs);
384 }
385 }
386
387 return $field;
388}
3c5de62a 389
390# ----------------------------------------------------------------------
88b8377e 391sub comments {
392
393=pod
394
395=head2 comments
396
397Get or set the comments on a table. May be called several times to
398set and it will accumulate the comments. Called in an array context,
399returns each comment individually; called in a scalar context, returns
400all the comments joined on newlines.
401
402 $table->comments('foo');
403 $table->comments('bar');
404 print join( ', ', $table->comments ); # prints "foo, bar"
405
406=cut
407
eb3b8ae4 408 my $self = shift;
409 my @comments = ref $_[0] ? @{ $_[0] } : @_;
b891fb49 410
eb3b8ae4 411 for my $arg ( @comments ) {
b891fb49 412 $arg = $arg->[0] if ref $arg;
eb3b8ae4 413 push @{ $self->{'comments'} }, $arg if defined $arg && $arg;
b891fb49 414 }
88b8377e 415
eb3b8ae4 416 if ( @{ $self->{'comments'} || [] } ) {
417 return wantarray
418 ? @{ $self->{'comments'} }
419 : join( "\n", @{ $self->{'comments'} } )
420 ;
421 }
422 else {
423 return wantarray ? () : undef;
424 }
88b8377e 425}
426
427# ----------------------------------------------------------------------
0f3cc5c0 428sub get_constraints {
429
430=pod
431
432=head2 get_constraints
433
434Returns all the constraint objects as an array or array reference.
435
436 my @constraints = $table->get_constraints;
437
438=cut
439
440 my $self = shift;
441
442 if ( ref $self->{'constraints'} ) {
443 return wantarray
444 ? @{ $self->{'constraints'} } : $self->{'constraints'};
445 }
446 else {
447 $self->error('No constraints');
448 return wantarray ? () : undef;
449 }
450}
451
452# ----------------------------------------------------------------------
453sub get_indices {
3c5de62a 454
455=pod
456
0f3cc5c0 457=head2 get_indices
3c5de62a 458
0f3cc5c0 459Returns all the index objects as an array or array reference.
3c5de62a 460
0f3cc5c0 461 my @indices = $table->get_indices;
3c5de62a 462
463=cut
464
465 my $self = shift;
0f3cc5c0 466
467 if ( ref $self->{'indices'} ) {
468 return wantarray
469 ? @{ $self->{'indices'} }
470 : $self->{'indices'};
471 }
472 else {
473 $self->error('No indices');
474 return wantarray ? () : undef;
475 }
476}
477
478# ----------------------------------------------------------------------
43b9dc7a 479sub get_field {
480
481=pod
482
483=head2 get_field
484
485Returns a field by the name provided.
486
487 my $field = $table->get_field('foo');
488
489=cut
490
491 my $self = shift;
492 my $field_name = shift or return $self->error('No field name');
3a7eb46e 493 my $case_insensitive = shift;
494 if ( $case_insensitive ) {
495 $field_name = uc($field_name);
496 foreach my $field ( keys %{$self->{fields}} ) {
ab8225e7 497 return $self->{fields}{$field} if $field_name eq uc($field);
3a7eb46e 498 }
499 return $self->error(qq[Field "$field_name" does not exist]);
500 }
43b9dc7a 501 return $self->error( qq[Field "$field_name" does not exist] ) unless
502 exists $self->{'fields'}{ $field_name };
503 return $self->{'fields'}{ $field_name };
504}
505
506# ----------------------------------------------------------------------
0f3cc5c0 507sub get_fields {
508
509=pod
510
511=head2 get_fields
512
513Returns all the field objects as an array or array reference.
514
515 my @fields = $table->get_fields;
516
517=cut
518
519 my $self = shift;
520 my @fields =
30f4ec44 521 map { $_->[1] }
522 sort { $a->[0] <=> $b->[0] }
523 map { [ $_->order, $_ ] }
0f3cc5c0 524 values %{ $self->{'fields'} || {} };
525
526 if ( @fields ) {
527 return wantarray ? @fields : \@fields;
528 }
529 else {
530 $self->error('No fields');
531 return wantarray ? () : undef;
532 }
3c5de62a 533}
534
535# ----------------------------------------------------------------------
536sub is_valid {
537
538=pod
539
540=head2 is_valid
541
542Determine whether the view is valid or not.
543
544 my $ok = $view->is_valid;
545
546=cut
547
548 my $self = shift;
43b9dc7a 549 return $self->error('No name') unless $self->name;
0f3cc5c0 550 return $self->error('No fields') unless $self->get_fields;
551
552 for my $object (
553 $self->get_fields, $self->get_indices, $self->get_constraints
554 ) {
555 return $object->error unless $object->is_valid;
556 }
557
558 return 1;
3c5de62a 559}
560
870024f3 561# ----------------------------------------------------------------------
65157eda 562sub is_trivial_link {
563
564=pod
565
719915f2 566=head2 is_trivial_link
567
568True if table has no data (non-key) fields and only uses single key joins.
65157eda 569
570=cut
571
572 my $self = shift;
573 return 0 if $self->is_data;
574 return $self->{'is_trivial_link'} if defined $self->{'is_trivial_link'};
575
576 $self->{'is_trivial_link'} = 1;
577
578 my %fk = ();
579
580 foreach my $field ( $self->get_fields ) {
581 next unless $field->is_foreign_key;
582 $fk{$field->foreign_key_reference->reference_table}++;
583 }
584
585 foreach my $referenced (keys %fk){
f9c5e794 586 if($fk{$referenced} > 1){
587 $self->{'is_trivial_link'} = 0;
588 last;
65157eda 589 }
3d6c9056 590 }
65157eda 591
592 return $self->{'is_trivial_link'};
593
594}
595
69c7a62f 596sub is_data {
69c7a62f 597
870024f3 598=pod
599
600=head2 is_data
601
719915f2 602Returns true if the table has some non-key fields.
603
870024f3 604=cut
605
606 my $self = shift;
607 return $self->{'is_data'} if defined $self->{'is_data'};
69c7a62f 608
870024f3 609 $self->{'is_data'} = 0;
69c7a62f 610
870024f3 611 foreach my $field ( $self->get_fields ) {
612 if ( !$field->is_primary_key and !$field->is_foreign_key ) {
613 $self->{'is_data'} = 1;
614 return $self->{'is_data'};
615 }
616 }
617
618 return $self->{'is_data'};
69c7a62f 619}
620
870024f3 621# ----------------------------------------------------------------------
69c7a62f 622sub can_link {
623
624=pod
625
626=head2 can_link
627
628Determine whether the table can link two arg tables via many-to-many.
629
630 my $ok = $table->can_link($table1,$table2);
631
632=cut
633
870024f3 634 my ( $self, $table1, $table2 ) = @_;
635
636 return $self->{'can_link'}{ $table1->name }{ $table2->name }
637 if defined $self->{'can_link'}{ $table1->name }{ $table2->name };
638
639 if ( $self->is_data == 1 ) {
640 $self->{'can_link'}{ $table1->name }{ $table2->name } = [0];
641 $self->{'can_link'}{ $table2->name }{ $table1->name } = [0];
642 return $self->{'can_link'}{ $table1->name }{ $table2->name };
643 }
644
645 my %fk = ();
646
647 foreach my $field ( $self->get_fields ) {
648 if ( $field->is_foreign_key ) {
649 push @{ $fk{ $field->foreign_key_reference->reference_table } },
650 $field->foreign_key_reference;
651 }
652 }
653
654 if ( !defined( $fk{ $table1->name } ) or !defined( $fk{ $table2->name } ) )
655 {
656 $self->{'can_link'}{ $table1->name }{ $table2->name } = [0];
657 $self->{'can_link'}{ $table2->name }{ $table1->name } = [0];
658 return $self->{'can_link'}{ $table1->name }{ $table2->name };
659 }
660
661 # trivial traversal, only one way to link the two tables
662 if ( scalar( @{ $fk{ $table1->name } } == 1 )
663 and scalar( @{ $fk{ $table2->name } } == 1 ) )
664 {
665 $self->{'can_link'}{ $table1->name }{ $table2->name } =
666 [ 'one2one', $fk{ $table1->name }, $fk{ $table2->name } ];
667 $self->{'can_link'}{ $table1->name }{ $table2->name } =
668 [ 'one2one', $fk{ $table2->name }, $fk{ $table1->name } ];
669
670 # non-trivial traversal. one way to link table2,
671 # many ways to link table1
672 }
673 elsif ( scalar( @{ $fk{ $table1->name } } > 1 )
674 and scalar( @{ $fk{ $table2->name } } == 1 ) )
675 {
676 $self->{'can_link'}{ $table1->name }{ $table2->name } =
677 [ 'many2one', $fk{ $table1->name }, $fk{ $table2->name } ];
678 $self->{'can_link'}{ $table2->name }{ $table1->name } =
679 [ 'one2many', $fk{ $table2->name }, $fk{ $table1->name } ];
680
681 # non-trivial traversal. one way to link table1,
682 # many ways to link table2
683 }
684 elsif ( scalar( @{ $fk{ $table1->name } } == 1 )
685 and scalar( @{ $fk{ $table2->name } } > 1 ) )
686 {
687 $self->{'can_link'}{ $table1->name }{ $table2->name } =
688 [ 'one2many', $fk{ $table1->name }, $fk{ $table2->name } ];
689 $self->{'can_link'}{ $table2->name }{ $table1->name } =
690 [ 'many2one', $fk{ $table2->name }, $fk{ $table1->name } ];
691
692 # non-trivial traversal. many ways to link table1 and table2
693 }
694 elsif ( scalar( @{ $fk{ $table1->name } } > 1 )
695 and scalar( @{ $fk{ $table2->name } } > 1 ) )
696 {
697 $self->{'can_link'}{ $table1->name }{ $table2->name } =
698 [ 'many2many', $fk{ $table1->name }, $fk{ $table2->name } ];
699 $self->{'can_link'}{ $table2->name }{ $table1->name } =
700 [ 'many2many', $fk{ $table2->name }, $fk{ $table1->name } ];
701
702 # one of the tables didn't export a key
703 # to this table, no linking possible
704 }
705 else {
706 $self->{'can_link'}{ $table1->name }{ $table2->name } = [0];
707 $self->{'can_link'}{ $table2->name }{ $table1->name } = [0];
708 }
709
710 return $self->{'can_link'}{ $table1->name }{ $table2->name };
69c7a62f 711}
712
43b9dc7a 713# ----------------------------------------------------------------------
dfdb0568 714sub name {
715
716=pod
717
718=head2 name
719
870024f3 720Get or set the table's name.
dfdb0568 721
65dd38c0 722Errors ("No table name") if you try to set a blank name.
723
724If provided an argument, checks the schema object for a table of
725that name and disallows the change if one exists (setting the error to
726"Can't use table name "%s": table exists").
dfdb0568 727
728 my $table_name = $table->name('foo');
729
730=cut
731
732 my $self = shift;
733
65dd38c0 734 if ( @_ ) {
735 my $arg = shift || return $self->error( "No table name" );
dfdb0568 736 if ( my $schema = $self->schema ) {
870024f3 737 return $self->error( qq[Can't use table name "$arg": table exists] )
dfdb0568 738 if $schema->get_table( $arg );
739 }
740 $self->{'name'} = $arg;
741 }
742
743 return $self->{'name'} || '';
744}
745
746# ----------------------------------------------------------------------
43b9dc7a 747sub schema {
748
749=pod
750
751=head2 schema
752
870024f3 753Get or set the table's schema object.
43b9dc7a 754
755 my $schema = $table->schema;
756
757=cut
758
759 my $self = shift;
760 if ( my $arg = shift ) {
761 return $self->error('Not a schema object') unless
762 UNIVERSAL::isa( $arg, 'SQL::Translator::Schema' );
763 $self->{'schema'} = $arg;
764 }
765
766 return $self->{'schema'};
767}
768
769# ----------------------------------------------------------------------
770sub primary_key {
771
772=pod
773
870024f3 774=head2 primary_key
43b9dc7a 775
870024f3 776Gets or sets the table's primary key(s). Takes one or more field
5e84ac85 777names (as a string, list or array[ref]) as an argument. If the field
778names are present, it will create a new PK if none exists, or it will
779add to the fields of an existing PK (and will unique the field names).
780Returns the C<SQL::Translator::Schema::Constraint> object representing
781the primary key.
782
783These are eqivalent:
43b9dc7a 784
785 $table->primary_key('id');
5e84ac85 786 $table->primary_key(['name']);
787 $table->primary_key('id','name']);
43b9dc7a 788 $table->primary_key(['id','name']);
789 $table->primary_key('id,name');
790 $table->primary_key(qw[ id name ]);
791
792 my $pk = $table->primary_key;
793
794=cut
795
30f4ec44 796 my $self = shift;
797 my $fields = parse_list_arg( @_ );
43b9dc7a 798
5e84ac85 799 my $constraint;
43b9dc7a 800 if ( @$fields ) {
801 for my $f ( @$fields ) {
802 return $self->error(qq[Invalid field "$f"]) unless
803 $self->get_field($f);
804 }
805
806 my $has_pk;
807 for my $c ( $self->get_constraints ) {
808 if ( $c->type eq PRIMARY_KEY ) {
809 $has_pk = 1;
810 $c->fields( @{ $c->fields }, @$fields );
5e84ac85 811 $constraint = $c;
43b9dc7a 812 }
813 }
814
815 unless ( $has_pk ) {
5e84ac85 816 $constraint = $self->add_constraint(
43b9dc7a 817 type => PRIMARY_KEY,
818 fields => $fields,
88b8377e 819 ) or return;
43b9dc7a 820 }
821 }
822
5e84ac85 823 if ( $constraint ) {
824 return $constraint;
825 }
826 else {
827 for my $c ( $self->get_constraints ) {
828 return $c if $c->type eq PRIMARY_KEY;
829 }
43b9dc7a 830 }
831
dfdb0568 832 return;
43b9dc7a 833}
834
835# ----------------------------------------------------------------------
836sub options {
837
838=pod
839
840=head2 options
841
870024f3 842Get or set the table's options (e.g., table types for MySQL). Returns
43b9dc7a 843an array or array reference.
844
845 my @options = $table->options;
846
847=cut
848
849 my $self = shift;
30f4ec44 850 my $options = parse_list_arg( @_ );
43b9dc7a 851
852 push @{ $self->{'options'} }, @$options;
853
854 if ( ref $self->{'options'} ) {
4598b71c 855 return wantarray ? @{ $self->{'options'} || [] } : ($self->{'options'} || '');
43b9dc7a 856 }
857 else {
858 return wantarray ? () : [];
859 }
860}
861
30f4ec44 862# ----------------------------------------------------------------------
863sub order {
864
865=pod
866
867=head2 order
868
870024f3 869Get or set the table's order.
30f4ec44 870
871 my $order = $table->order(3);
872
873=cut
874
875 my ( $self, $arg ) = @_;
876
877 if ( defined $arg && $arg =~ /^\d+$/ ) {
878 $self->{'order'} = $arg;
879 }
880
881 return $self->{'order'} || 0;
882}
883
884# ----------------------------------------------------------------------
719915f2 885sub field_names {
886
887=head2 field_names
888
889Read-only method to return a list or array ref of the field names. Returns undef
890or an empty list if the table has no fields set. Usefull if you want to
891avoid the overload magic of the Field objects returned by the get_fields method.
892
893 my @names = $constraint->field_names;
894
895=cut
896
897 my $self = shift;
898 my @fields =
899 map { $_->name }
900 sort { $a->order <=> $b->order }
901 values %{ $self->{'fields'} || {} };
902
903 if ( @fields ) {
904 return wantarray ? @fields : \@fields;
905 }
906 else {
907 $self->error('No fields');
908 return wantarray ? () : undef;
909 }
910}
911
912# ----------------------------------------------------------------------
abf315bb 913sub equals {
914
915=pod
916
917=head2 equals
918
919Determines if this table is the same as another
920
921 my $isIdentical = $table1->equals( $table2 );
922
923=cut
924
925 my $self = shift;
926 my $other = shift;
d6d17119 927 my $case_insensitive = shift;
abf315bb 928
929 return 0 unless $self->SUPER::equals($other);
d6d17119 930 return 0 unless $case_insensitive ? uc($self->name) eq uc($other->name) : $self->name eq $other->name;
4598b71c 931 return 0 unless $self->_compare_objects(scalar $self->options, scalar $other->options);
932 return 0 unless $self->_compare_objects(scalar $self->extra, scalar $other->extra);
abf315bb 933
934 # Fields
935 # Go through our fields
936 my %checkedFields;
937 foreach my $field ( $self->get_fields ) {
d6d17119 938 my $otherField = $other->get_field($field->name, $case_insensitive);
939 return 0 unless $field->equals($otherField, $case_insensitive);
abf315bb 940 $checkedFields{$field->name} = 1;
941 }
942 # Go through the other table's fields
943 foreach my $otherField ( $other->get_fields ) {
944 next if $checkedFields{$otherField->name};
945 return 0;
946 }
947
948 # Constraints
949 # Go through our constraints
950 my %checkedConstraints;
951CONSTRAINT:
952 foreach my $constraint ( $self->get_constraints ) {
953 foreach my $otherConstraint ( $other->get_constraints ) {
d6d17119 954 if ( $constraint->equals($otherConstraint, $case_insensitive) ) {
abf315bb 955 $checkedConstraints{$otherConstraint} = 1;
956 next CONSTRAINT;
957 }
958 }
959 return 0;
960 }
961 # Go through the other table's constraints
686b14be 962CONSTRAINT2:
abf315bb 963 foreach my $otherConstraint ( $other->get_constraints ) {
964 next if $checkedFields{$otherConstraint};
686b14be 965 foreach my $constraint ( $self->get_constraints ) {
d6d17119 966 if ( $otherConstraint->equals($constraint, $case_insensitive) ) {
686b14be 967 next CONSTRAINT2;
968 }
969 }
abf315bb 970 return 0;
971 }
972
973 # Indices
974 # Go through our indices
975 my %checkedIndices;
976INDEX:
977 foreach my $index ( $self->get_indices ) {
978 foreach my $otherIndex ( $other->get_indices ) {
d6d17119 979 if ( $index->equals($otherIndex, $case_insensitive) ) {
abf315bb 980 $checkedIndices{$otherIndex} = 1;
981 next INDEX;
982 }
983 }
984 return 0;
985 }
686b14be 986 # Go through the other table's indices
987INDEX2:
abf315bb 988 foreach my $otherIndex ( $other->get_indices ) {
989 next if $checkedIndices{$otherIndex};
686b14be 990 foreach my $index ( $self->get_indices ) {
d6d17119 991 if ( $otherIndex->equals($index, $case_insensitive) ) {
686b14be 992 next INDEX2;
993 }
994 }
abf315bb 995 return 0;
996 }
997
998 return 1;
999}
1000
1001# ----------------------------------------------------------------------
719915f2 1002
1003=head1 LOOKUP METHODS
1004
1005The following are a set of shortcut methods for getting commonly used lists of
1006fields and constraints. They all return lists or array refs of Field or
1007Constraint objects.
1008
1009=over 4
1010
1011=item pkey_fields
1012
1013The primary key fields.
1014
1015=item fkey_fields
1016
1017All foreign key fields.
1018
1019=item nonpkey_fields
1020
1021All the fields except the primary key.
1022
1023=item data_fields
1024
1025All non key fields.
1026
1027=item unique_fields
1028
1029All fields with unique constraints.
1030
1031=item unique_constraints
1032
1033All this tables unique constraints.
1034
1035=item fkey_constraints
1036
1037All this tables foreign key constraints. (See primary_key method to get the
1038primary key constraint)
1039
1040=back
1041
1042=cut
1043
1044sub pkey_fields {
1045 my $me = shift;
1046 my @fields = grep { $_->is_primary_key } $me->get_fields;
1047 return wantarray ? @fields : \@fields;
1048}
1049
1050# ----------------------------------------------------------------------
1051sub fkey_fields {
1052 my $me = shift;
1053 my @fields;
1054 push @fields, $_->fields foreach $me->fkey_constraints;
1055 return wantarray ? @fields : \@fields;
1056}
1057
1058# ----------------------------------------------------------------------
1059sub nonpkey_fields {
1060 my $me = shift;
1061 my @fields = grep { !$_->is_primary_key } $me->get_fields;
1062 return wantarray ? @fields : \@fields;
1063}
1064
1065# ----------------------------------------------------------------------
1066sub data_fields {
1067 my $me = shift;
1068 my @fields =
1069 grep { !$_->is_foreign_key and !$_->is_primary_key } $me->get_fields;
1070 return wantarray ? @fields : \@fields;
1071}
1072
1073# ----------------------------------------------------------------------
1074sub unique_fields {
1075 my $me = shift;
1076 my @fields;
1077 push @fields, $_->fields foreach $me->unique_constraints;
1078 return wantarray ? @fields : \@fields;
1079}
1080
1081# ----------------------------------------------------------------------
1082sub unique_constraints {
1083 my $me = shift;
1084 my @cons = grep { $_->type eq UNIQUE } $me->get_constraints;
1085 return wantarray ? @cons : \@cons;
1086}
1087
1088# ----------------------------------------------------------------------
1089sub fkey_constraints {
1090 my $me = shift;
1091 my @cons = grep { $_->type eq FOREIGN_KEY } $me->get_constraints;
1092 return wantarray ? @cons : \@cons;
1093}
1094
1095# ----------------------------------------------------------------------
30f4ec44 1096sub DESTROY {
1097 my $self = shift;
1098 undef $self->{'schema'}; # destroy cyclical reference
1099 undef $_ for @{ $self->{'constraints'} };
1100 undef $_ for @{ $self->{'indices'} };
1101 undef $_ for values %{ $self->{'fields'} };
1102}
1103
3c5de62a 11041;
1105
1106# ----------------------------------------------------------------------
1107
1108=pod
1109
870024f3 1110=head1 AUTHORS
3c5de62a 1111
870024f3 1112Ken Y. Clark E<lt>kclark@cpan.orgE<gt>,
1113Allen Day E<lt>allenday@ucla.eduE<gt>.
3c5de62a 1114
1115=cut