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