take out duplicate docs
[dbsrgits/SQL-Translator.git] / lib / SQL / Translator / Schema / Constraint.pm
CommitLineData
3c5de62a 1package SQL::Translator::Schema::Constraint;
2
44659089 3# ----------------------------------------------------------------------
4# Copyright (C) 2002-2009 SQLFairy Authors
5#
6# This program is free software; you can redistribute it and/or
7# modify it under the terms of the GNU General Public License as
8# published by the Free Software Foundation; version 2.
9#
10# This program is distributed in the hope that it will be useful, but
11# WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18# 02111-1307 USA
19# -------------------------------------------------------------------
20
3c5de62a 21=pod
22
23=head1 NAME
24
25SQL::Translator::Schema::Constraint - SQL::Translator constraint object
26
27=head1 SYNOPSIS
28
29 use SQL::Translator::Schema::Constraint;
30 my $constraint = SQL::Translator::Schema::Constraint->new(
31 name => 'foo',
32 fields => [ id ],
43b9dc7a 33 type => PRIMARY_KEY,
3c5de62a 34 );
35
36=head1 DESCRIPTION
37
38C<SQL::Translator::Schema::Constraint> is the constraint object.
39
40=head1 METHODS
41
42=cut
43
44use strict;
43b9dc7a 45use SQL::Translator::Schema::Constants;
752608d5 46use SQL::Translator::Utils 'parse_list_arg';
3c5de62a 47
b6a880d1 48use base 'SQL::Translator::Schema::Object';
49
da06ac74 50use vars qw($VERSION $TABLE_COUNT $VIEW_COUNT);
51
11ad2df9 52$VERSION = '1.59';
3c5de62a 53
695c2da2 54my %VALID_CONSTRAINT_TYPE = (
43b9dc7a 55 PRIMARY_KEY, 1,
56 UNIQUE, 1,
57 CHECK_C, 1,
58 FOREIGN_KEY, 1,
695c2da2 59 NOT_NULL, 1,
60);
3c5de62a 61
9371be50 62__PACKAGE__->_attributes( qw/
ea93df61 63 table name type fields reference_fields reference_table
9371be50 64 match_type on_delete on_update expression deferrable
65/);
66
67# Override to remove empty arrays from args.
68# t/14postgres-parser breaks without this.
69sub init {
ea93df61 70
3c5de62a 71=pod
72
73=head2 new
74
75Object constructor.
76
77 my $schema = SQL::Translator::Schema::Constraint->new(
dedb8f3b 78 table => $table, # table to which it belongs
3c5de62a 79 type => 'foreign_key', # type of table constraint
dedb8f3b 80 name => 'fk_phone_id', # name of the constraint
81 fields => 'phone_id', # field in the referring table
65157eda 82 reference_fields => 'phone_id', # referenced field
83 reference_table => 'phone', # referenced table
3c5de62a 84 match_type => 'full', # how to match
2d034ab4 85 on_delete => 'cascade', # what to do on deletes
86 on_update => '', # what to do on updates
3c5de62a 87 );
88
89=cut
90
9371be50 91 my $self = shift;
92 foreach ( values %{$_[0]} ) { $_ = undef if ref($_) eq "ARRAY" && ! @$_; }
93 $self->SUPER::init(@_);
3c5de62a 94}
95
43b9dc7a 96sub deferrable {
97
98=pod
99
100=head2 deferrable
101
2d034ab4 102Get or set whether the constraint is deferrable. If not defined,
43b9dc7a 103then returns "1." The argument is evaluated by Perl for True or
104False, so the following are eqivalent:
105
106 $deferrable = $field->deferrable(0);
107 $deferrable = $field->deferrable('');
108 $deferrable = $field->deferrable('0');
109
110=cut
111
112 my ( $self, $arg ) = @_;
113
114 if ( defined $arg ) {
115 $self->{'deferrable'} = $arg ? 1 : 0;
116 }
117
118 return defined $self->{'deferrable'} ? $self->{'deferrable'} : 1;
119}
120
43b9dc7a 121sub expression {
122
123=pod
124
125=head2 expression
126
127Gets and set the expression used in a CHECK constraint.
128
129 my $expression = $constraint->expression('...');
130
131=cut
132
133 my $self = shift;
ea93df61 134
43b9dc7a 135 if ( my $arg = shift ) {
136 # check arg here?
137 $self->{'expression'} = $arg;
138 }
139
140 return $self->{'expression'} || '';
141}
142
43b9dc7a 143sub is_valid {
144
145=pod
146
147=head2 is_valid
148
149Determine whether the constraint is valid or not.
150
151 my $ok = $constraint->is_valid;
152
153=cut
154
155 my $self = shift;
156 my $type = $self->type or return $self->error('No type');
157 my $table = $self->table or return $self->error('No table');
158 my @fields = $self->fields or return $self->error('No fields');
159 my $table_name = $table->name or return $self->error('No table name');
160
161 for my $f ( @fields ) {
162 next if $table->get_field( $f );
163 return $self->error(
164 "Constraint references non-existent field '$f' ",
165 "in table '$table_name'"
166 );
167 }
168
169 my $schema = $table->schema or return $self->error(
170 'Table ', $table->name, ' has no schema object'
171 );
172
173 if ( $type eq FOREIGN_KEY ) {
174 return $self->error('Only one field allowed for foreign key')
175 if scalar @fields > 1;
176
ea93df61 177 my $ref_table_name = $self->reference_table or
43b9dc7a 178 return $self->error('No reference table');
179
180 my $ref_table = $schema->get_table( $ref_table_name ) or
181 return $self->error("No table named '$ref_table_name' in schema");
182
183 my @ref_fields = $self->reference_fields or return;
184
185 return $self->error('Only one field allowed for foreign key reference')
186 if scalar @ref_fields > 1;
187
188 for my $ref_field ( @ref_fields ) {
189 next if $ref_table->get_field( $ref_field );
190 return $self->error(
ea93df61 191 "Constraint from field(s) ",
43b9dc7a 192 join(', ', map {qq['$table_name.$_']} @fields),
193 " to non-existent field '$ref_table_name.$ref_field'"
194 );
195 }
196 }
197 elsif ( $type eq CHECK_C ) {
ea93df61 198 return $self->error('No expression for CHECK') unless
43b9dc7a 199 $self->expression;
200 }
201
202 return 1;
203}
204
3c5de62a 205sub fields {
206
207=pod
208
209=head2 fields
210
43b9dc7a 211Gets and set the fields the constraint is on. Accepts a string, list or
212arrayref; returns an array or array reference. Will unique the field
213names and keep them in order by the first occurrence of a field name.
214
ac095e5e 215The fields are returned as Field objects if they exist or as plain
216names if not. (If you just want the names and want to avoid the Field's overload
217magic use L<field_names>).
218
219Returns undef or an empty list if the constraint has no fields set.
220
43b9dc7a 221 $constraint->fields('id');
222 $constraint->fields('id', 'name');
223 $constraint->fields( 'id, name' );
224 $constraint->fields( [ 'id', 'name' ] );
225 $constraint->fields( qw[ id name ] );
3c5de62a 226
43b9dc7a 227 my @fields = $constraint->fields;
3c5de62a 228
229=cut
230
231 my $self = shift;
752608d5 232 my $fields = parse_list_arg( @_ );
3c5de62a 233
234 if ( @$fields ) {
43b9dc7a 235 my ( %unique, @unique );
236 for my $f ( @$fields ) {
237 next if $unique{ $f };
238 $unique{ $f } = 1;
239 push @unique, $f;
240 }
241
242 $self->{'fields'} = \@unique;
3c5de62a 243 }
244
53ded04a 245 if ( @{ $self->{'fields'} || [] } ) {
ac095e5e 246 # We have to return fields that don't exist on the table as names in
247 # case those fields havn't been created yet.
248 my @ret = map {
249 $self->table->get_field($_) || $_ } @{ $self->{'fields'} };
250 return wantarray ? @ret : \@ret;
53ded04a 251 }
252 else {
253 return wantarray ? () : undef;
254 }
3c5de62a 255}
256
ac095e5e 257sub field_names {
258
259=head2 field_names
260
261Read-only method to return a list or array ref of the field names. Returns undef
10f70490 262or an empty list if the constraint has no fields set. Useful if you want to
ac095e5e 263avoid the overload magic of the Field objects returned by the fields method.
264
265 my @names = $constraint->field_names;
266
267=cut
268
269 my $self = shift;
4598b71c 270 return wantarray ? @{ $self->{'fields'} || [] } : ($self->{'fields'} || '');
ac095e5e 271}
272
752608d5 273sub match_type {
274
275=pod
276
277=head2 match_type
278
279Get or set the constraint's match_type. Only valid values are "full"
840447a5 280"partial" and "simple"
752608d5 281
282 my $match_type = $constraint->match_type('FULL');
283
284=cut
285
c3b0b535 286 my ( $self, $arg ) = @_;
ea93df61 287
c3b0b535 288 if ( $arg ) {
289 $arg = lc $arg;
752608d5 290 return $self->error("Invalid match type: $arg")
840447a5 291 unless $arg eq 'full' || $arg eq 'partial' || $arg eq 'simple';
752608d5 292 $self->{'match_type'} = $arg;
293 }
294
295 return $self->{'match_type'} || '';
296}
297
3c5de62a 298sub name {
299
300=pod
301
302=head2 name
303
304Get or set the constraint's name.
305
306 my $name = $constraint->name('foo');
307
308=cut
309
310 my $self = shift;
695c2da2 311 my $arg = shift || '';
312 $self->{'name'} = $arg if $arg;
3c5de62a 313 return $self->{'name'} || '';
314}
315
dedb8f3b 316sub options {
317
318=pod
319
320=head2 options
321
ea93df61 322Gets or adds to the constraints's options (e.g., "INITIALLY IMMEDIATE").
dedb8f3b 323Returns an array or array reference.
324
325 $constraint->options('NORELY');
326 my @options = $constraint->options;
327
328=cut
329
330 my $self = shift;
331 my $options = parse_list_arg( @_ );
332
333 push @{ $self->{'options'} }, @$options;
334
335 if ( ref $self->{'options'} ) {
336 return wantarray ? @{ $self->{'options'} || [] } : $self->{'options'};
337 }
338 else {
339 return wantarray ? () : [];
340 }
341}
342
43b9dc7a 343sub on_delete {
344
345=pod
346
347=head2 on_delete
348
349Get or set the constraint's "on delete" action.
350
351 my $action = $constraint->on_delete('cascade');
352
353=cut
354
355 my $self = shift;
ea93df61 356
43b9dc7a 357 if ( my $arg = shift ) {
358 # validate $arg?
359 $self->{'on_delete'} = $arg;
360 }
361
362 return $self->{'on_delete'} || '';
363}
364
43b9dc7a 365sub on_update {
366
367=pod
368
369=head2 on_update
370
371Get or set the constraint's "on update" action.
372
373 my $action = $constraint->on_update('no action');
374
375=cut
376
377 my $self = shift;
ea93df61 378
43b9dc7a 379 if ( my $arg = shift ) {
380 # validate $arg?
381 $self->{'on_update'} = $arg;
382 }
383
384 return $self->{'on_update'} || '';
385}
386
43b9dc7a 387sub reference_fields {
388
389=pod
390
391=head2 reference_fields
392
393Gets and set the fields in the referred table. Accepts a string, list or
394arrayref; returns an array or array reference.
395
396 $constraint->reference_fields('id');
397 $constraint->reference_fields('id', 'name');
398 $constraint->reference_fields( 'id, name' );
399 $constraint->reference_fields( [ 'id', 'name' ] );
400 $constraint->reference_fields( qw[ id name ] );
401
402 my @reference_fields = $constraint->reference_fields;
403
404=cut
405
406 my $self = shift;
752608d5 407 my $fields = parse_list_arg( @_ );
43b9dc7a 408
409 if ( @$fields ) {
410 $self->{'reference_fields'} = $fields;
411 }
412
2d034ab4 413 # Nothing set so try and derive it from the other constraint data
43b9dc7a 414 unless ( ref $self->{'reference_fields'} ) {
2d034ab4 415 my $table = $self->table or return $self->error('No table');
416 my $schema = $table->schema or return $self->error('No schema');
ea93df61 417 if ( my $ref_table_name = $self->reference_table ) {
2d034ab4 418 my $ref_table = $schema->get_table( $ref_table_name ) or
419 return $self->error("Can't find table '$ref_table_name'");
420
ea93df61 421 if ( my $constraint = $ref_table->primary_key ) {
2d034ab4 422 $self->{'reference_fields'} = [ $constraint->fields ];
423 }
424 else {
425 $self->error(
426 'No reference fields defined and cannot find primary key in ',
427 "reference table '$ref_table_name'"
428 );
429 }
43b9dc7a 430 }
2d034ab4 431 # No ref table so we are not that sort of constraint, hence no ref
432 # fields. So we let the return below return an empty list.
43b9dc7a 433 }
434
435 if ( ref $self->{'reference_fields'} ) {
ea93df61 436 return wantarray
437 ? @{ $self->{'reference_fields'} }
43b9dc7a 438 : $self->{'reference_fields'};
439 }
440 else {
441 return wantarray ? () : [];
442 }
443}
444
43b9dc7a 445sub reference_table {
446
447=pod
448
449=head2 reference_table
450
451Get or set the table referred to by the constraint.
452
453 my $reference_table = $constraint->reference_table('foo');
454
455=cut
456
457 my $self = shift;
458 $self->{'reference_table'} = shift if @_;
459 return $self->{'reference_table'} || '';
460}
461
43b9dc7a 462sub table {
3c5de62a 463
464=pod
465
43b9dc7a 466=head2 table
3c5de62a 467
2d034ab4 468Get or set the constraint's table object.
3c5de62a 469
43b9dc7a 470 my $table = $field->table;
3c5de62a 471
472=cut
473
474 my $self = shift;
43b9dc7a 475 if ( my $arg = shift ) {
476 return $self->error('Not a table object') unless
477 UNIVERSAL::isa( $arg, 'SQL::Translator::Schema::Table' );
478 $self->{'table'} = $arg;
479 }
480
481 return $self->{'table'};
482}
483
dedb8f3b 484sub type {
43b9dc7a 485
486=pod
487
dedb8f3b 488=head2 type
43b9dc7a 489
dedb8f3b 490Get or set the constraint's type.
43b9dc7a 491
dedb8f3b 492 my $type = $constraint->type( PRIMARY_KEY );
43b9dc7a 493
494=cut
495
c3b0b535 496 my ( $self, $type ) = @_;
43b9dc7a 497
c3b0b535 498 if ( $type ) {
499 $type = uc $type;
dedb8f3b 500 $type =~ s/_/ /g;
ea93df61 501 return $self->error("Invalid constraint type: $type")
695c2da2 502 unless $VALID_CONSTRAINT_TYPE{ $type };
dedb8f3b 503 $self->{'type'} = $type;
43b9dc7a 504 }
3c5de62a 505
dedb8f3b 506 return $self->{'type'} || '';
507}
abf315bb 508
abf315bb 509sub equals {
510
511=pod
512
513=head2 equals
514
515Determines if this constraint is the same as another
516
517 my $isIdentical = $constraint1->equals( $constraint2 );
518
519=cut
520
521 my $self = shift;
522 my $other = shift;
523 my $case_insensitive = shift;
d990d84b 524 my $ignore_constraint_names = shift;
ea93df61 525
abf315bb 526 return 0 unless $self->SUPER::equals($other);
b8d24485 527 return 0 unless $self->type eq $other->type;
d990d84b 528 unless ($ignore_constraint_names) {
529 return 0 unless $case_insensitive ? uc($self->name) eq uc($other->name) : $self->name eq $other->name;
530 }
abf315bb 531 return 0 unless $self->deferrable eq $other->deferrable;
4598b71c 532 #return 0 unless $self->is_valid eq $other->is_valid;
abf315bb 533 return 0 unless $case_insensitive ? uc($self->table->name) eq uc($other->table->name)
ea93df61 534 : $self->table->name eq $other->table->name;
abf315bb 535 return 0 unless $self->expression eq $other->expression;
ea93df61 536
6a0f3000 537 # Check fields, regardless of order
ea93df61 538 my %otherFields = (); # create a hash of the other fields
6a0f3000 539 foreach my $otherField ($other->fields) {
ea93df61 540 $otherField = uc($otherField) if $case_insensitive;
541 $otherFields{$otherField} = 1;
6a0f3000 542 }
543 foreach my $selfField ($self->fields) { # check for self fields in hash
ea93df61 544 $selfField = uc($selfField) if $case_insensitive;
545 return 0 unless $otherFields{$selfField};
546 delete $otherFields{$selfField};
6a0f3000 547 }
548 # Check all other fields were accounted for
549 return 0 unless keys %otherFields == 0;
550
551 # Check reference fields, regardless of order
ea93df61 552 my %otherRefFields = (); # create a hash of the other reference fields
6a0f3000 553 foreach my $otherRefField ($other->reference_fields) {
ea93df61 554 $otherRefField = uc($otherRefField) if $case_insensitive;
555 $otherRefFields{$otherRefField} = 1;
6a0f3000 556 }
557 foreach my $selfRefField ($self->reference_fields) { # check for self reference fields in hash
ea93df61 558 $selfRefField = uc($selfRefField) if $case_insensitive;
559 return 0 unless $otherRefFields{$selfRefField};
560 delete $otherRefFields{$selfRefField};
6a0f3000 561 }
562 # Check all other reference fields were accounted for
563 return 0 unless keys %otherRefFields == 0;
564
b8d24485 565 return 0 unless $case_insensitive ? uc($self->reference_table) eq uc($other->reference_table) : $self->reference_table eq $other->reference_table;
abf315bb 566 return 0 unless $self->match_type eq $other->match_type;
567 return 0 unless $self->on_delete eq $other->on_delete;
568 return 0 unless $self->on_update eq $other->on_update;
4598b71c 569 return 0 unless $self->_compare_objects(scalar $self->options, scalar $other->options);
570 return 0 unless $self->_compare_objects(scalar $self->extra, scalar $other->extra);
abf315bb 571 return 1;
572}
573
752608d5 574sub DESTROY {
575 my $self = shift;
576 undef $self->{'table'}; # destroy cyclical reference
577}
578
3c5de62a 5791;
580
3c5de62a 581=pod
582
583=head1 AUTHOR
584
c3b0b535 585Ken Youens-Clark E<lt>kclark@cpan.orgE<gt>.
3c5de62a 586
587=cut