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