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