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