Fixed case-insensitivity matching for SQL Server and field names
[dbsrgits/SQL-Translator.git] / lib / SQL / Translator / Schema / Table.pm
CommitLineData
3c5de62a 1package SQL::Translator::Schema::Table;
2
3# ----------------------------------------------------------------------
3a7eb46e 4# $Id: Table.pm,v 1.34 2005-07-15 23:36:13 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
3a7eb46e 54$VERSION = sprintf "%d.%02d", q$Revision: 1.34 $ =~ /(\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}} ) {
497 return $self->{fields}{$field_name} if $field_name eq uc($field);
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;
927
928 return 0 unless $self->SUPER::equals($other);
929 return 0 unless $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 ) {
937 my $otherField = $other->get_field($field->name);
938 return 0 unless $field->equals($otherField);
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 ) {
953 if ( $constraint->equals($otherConstraint) ) {
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 ) {
965 if ( $otherConstraint->equals($constraint) ) {
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 ) {
978 if ( $index->equals($otherIndex) ) {
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 ) {
990 if ( $otherIndex->equals($index) ) {
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