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