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