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