Moved some code around to fix ordering, convert "type" to match what's
[dbsrgits/SQL-Translator.git] / lib / SQL / Translator / Schema / Constraint.pm
CommitLineData
3c5de62a 1package SQL::Translator::Schema::Constraint;
2
3# ----------------------------------------------------------------------
dedb8f3b 4# $Id: Constraint.pm,v 1.4 2003-06-06 00:08:14 kycl4rk 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
54$VERSION = 1.00;
55
56use constant VALID_TYPE => {
43b9dc7a 57 PRIMARY_KEY, 1,
58 UNIQUE, 1,
59 CHECK_C, 1,
60 FOREIGN_KEY, 1,
3c5de62a 61};
62
63# ----------------------------------------------------------------------
64sub init {
65
66=pod
67
68=head2 new
69
70Object constructor.
71
72 my $schema = SQL::Translator::Schema::Constraint->new(
dedb8f3b 73 table => $table, # table to which it belongs
3c5de62a 74 type => 'foreign_key', # type of table constraint
dedb8f3b 75 name => 'fk_phone_id', # name of the constraint
76 fields => 'phone_id', # field in the referring table
77 reference_fields => 'phone_id', # referenced table
78 reference_table => 'phone', # referenced fields
3c5de62a 79 match_type => 'full', # how to match
80 on_delete_do => 'cascade', # what to do on deletes
81 on_update_do => '', # what to do on updates
82 );
83
84=cut
85
86 my ( $self, $config ) = @_;
752608d5 87 my @fields = qw[
88 table name type fields reference_fields reference_table
89 match_type on_delete on_update
90 ];
3c5de62a 91
92 for my $arg ( @fields ) {
93 next unless $config->{ $arg };
94 $self->$arg( $config->{ $arg } ) or return;
95 }
96
97 return $self;
98}
99
100# ----------------------------------------------------------------------
43b9dc7a 101sub deferrable {
102
103=pod
104
105=head2 deferrable
106
107Get or set the whether the constraint is deferrable. If not defined,
108then returns "1." The argument is evaluated by Perl for True or
109False, so the following are eqivalent:
110
111 $deferrable = $field->deferrable(0);
112 $deferrable = $field->deferrable('');
113 $deferrable = $field->deferrable('0');
114
115=cut
116
117 my ( $self, $arg ) = @_;
118
119 if ( defined $arg ) {
120 $self->{'deferrable'} = $arg ? 1 : 0;
121 }
122
123 return defined $self->{'deferrable'} ? $self->{'deferrable'} : 1;
124}
125
126# ----------------------------------------------------------------------
127sub expression {
128
129=pod
130
131=head2 expression
132
133Gets and set the expression used in a CHECK constraint.
134
135 my $expression = $constraint->expression('...');
136
137=cut
138
139 my $self = shift;
140
141 if ( my $arg = shift ) {
142 # check arg here?
143 $self->{'expression'} = $arg;
144 }
145
146 return $self->{'expression'} || '';
147}
148
149# ----------------------------------------------------------------------
150sub is_valid {
151
152=pod
153
154=head2 is_valid
155
156Determine whether the constraint is valid or not.
157
158 my $ok = $constraint->is_valid;
159
160=cut
161
162 my $self = shift;
163 my $type = $self->type or return $self->error('No type');
164 my $table = $self->table or return $self->error('No table');
165 my @fields = $self->fields or return $self->error('No fields');
166 my $table_name = $table->name or return $self->error('No table name');
167
168 for my $f ( @fields ) {
169 next if $table->get_field( $f );
170 return $self->error(
171 "Constraint references non-existent field '$f' ",
172 "in table '$table_name'"
173 );
174 }
175
176 my $schema = $table->schema or return $self->error(
177 'Table ', $table->name, ' has no schema object'
178 );
179
180 if ( $type eq FOREIGN_KEY ) {
181 return $self->error('Only one field allowed for foreign key')
182 if scalar @fields > 1;
183
184 my $ref_table_name = $self->reference_table or
185 return $self->error('No reference table');
186
187 my $ref_table = $schema->get_table( $ref_table_name ) or
188 return $self->error("No table named '$ref_table_name' in schema");
189
190 my @ref_fields = $self->reference_fields or return;
191
192 return $self->error('Only one field allowed for foreign key reference')
193 if scalar @ref_fields > 1;
194
195 for my $ref_field ( @ref_fields ) {
196 next if $ref_table->get_field( $ref_field );
197 return $self->error(
198 "Constraint from field(s) ",
199 join(', ', map {qq['$table_name.$_']} @fields),
200 " to non-existent field '$ref_table_name.$ref_field'"
201 );
202 }
203 }
204 elsif ( $type eq CHECK_C ) {
205 return $self->error('No expression for CHECK') unless
206 $self->expression;
207 }
208
209 return 1;
210}
211
212# ----------------------------------------------------------------------
3c5de62a 213sub fields {
214
215=pod
216
217=head2 fields
218
43b9dc7a 219Gets and set the fields the constraint is on. Accepts a string, list or
220arrayref; returns an array or array reference. Will unique the field
221names and keep them in order by the first occurrence of a field name.
222
223 $constraint->fields('id');
224 $constraint->fields('id', 'name');
225 $constraint->fields( 'id, name' );
226 $constraint->fields( [ 'id', 'name' ] );
227 $constraint->fields( qw[ id name ] );
3c5de62a 228
43b9dc7a 229 my @fields = $constraint->fields;
3c5de62a 230
231=cut
232
233 my $self = shift;
752608d5 234 my $fields = parse_list_arg( @_ );
3c5de62a 235
236 if ( @$fields ) {
43b9dc7a 237 my ( %unique, @unique );
238 for my $f ( @$fields ) {
239 next if $unique{ $f };
240 $unique{ $f } = 1;
241 push @unique, $f;
242 }
243
244 $self->{'fields'} = \@unique;
3c5de62a 245 }
246
247 return wantarray ? @{ $self->{'fields'} || [] } : $self->{'fields'};
248}
249
250# ----------------------------------------------------------------------
752608d5 251sub match_type {
252
253=pod
254
255=head2 match_type
256
257Get or set the constraint's match_type. Only valid values are "full"
258or "partial."
259
260 my $match_type = $constraint->match_type('FULL');
261
262=cut
263
264 my $self = shift;
265
266 if ( my $arg = lc shift ) {
267 return $self->error("Invalid match type: $arg")
268 unless $arg eq 'full' || $arg eq 'partial';
269 $self->{'match_type'} = $arg;
270 }
271
272 return $self->{'match_type'} || '';
273}
274
275# ----------------------------------------------------------------------
3c5de62a 276sub name {
277
278=pod
279
280=head2 name
281
282Get or set the constraint's name.
283
284 my $name = $constraint->name('foo');
285
286=cut
287
288 my $self = shift;
289 $self->{'name'} = shift if @_;
290 return $self->{'name'} || '';
291}
292
293# ----------------------------------------------------------------------
dedb8f3b 294sub options {
295
296=pod
297
298=head2 options
299
300Gets or adds to the constraints's options (e.g., "INITIALLY IMMEDIATE").
301Returns an array or array reference.
302
303 $constraint->options('NORELY');
304 my @options = $constraint->options;
305
306=cut
307
308 my $self = shift;
309 my $options = parse_list_arg( @_ );
310
311 push @{ $self->{'options'} }, @$options;
312
313 if ( ref $self->{'options'} ) {
314 return wantarray ? @{ $self->{'options'} || [] } : $self->{'options'};
315 }
316 else {
317 return wantarray ? () : [];
318 }
319}
320
321
322# ----------------------------------------------------------------------
43b9dc7a 323sub on_delete {
324
325=pod
326
327=head2 on_delete
328
329Get or set the constraint's "on delete" action.
330
331 my $action = $constraint->on_delete('cascade');
332
333=cut
334
335 my $self = shift;
336
337 if ( my $arg = shift ) {
338 # validate $arg?
339 $self->{'on_delete'} = $arg;
340 }
341
342 return $self->{'on_delete'} || '';
343}
344
345# ----------------------------------------------------------------------
346sub on_update {
347
348=pod
349
350=head2 on_update
351
352Get or set the constraint's "on update" action.
353
354 my $action = $constraint->on_update('no action');
355
356=cut
357
358 my $self = shift;
359
360 if ( my $arg = shift ) {
361 # validate $arg?
362 $self->{'on_update'} = $arg;
363 }
364
365 return $self->{'on_update'} || '';
366}
367
368# ----------------------------------------------------------------------
369sub reference_fields {
370
371=pod
372
373=head2 reference_fields
374
375Gets and set the fields in the referred table. Accepts a string, list or
376arrayref; returns an array or array reference.
377
378 $constraint->reference_fields('id');
379 $constraint->reference_fields('id', 'name');
380 $constraint->reference_fields( 'id, name' );
381 $constraint->reference_fields( [ 'id', 'name' ] );
382 $constraint->reference_fields( qw[ id name ] );
383
384 my @reference_fields = $constraint->reference_fields;
385
386=cut
387
388 my $self = shift;
752608d5 389 my $fields = parse_list_arg( @_ );
43b9dc7a 390
391 if ( @$fields ) {
392 $self->{'reference_fields'} = $fields;
393 }
394
395 unless ( ref $self->{'reference_fields'} ) {
396 my $table = $self->table or return $self->error('No table');
397 my $schema = $table->schema or return $self->error('No schema');
398 my $ref_table_name = $self->reference_table or
399 return $self->error('No table');
400 my $ref_table = $schema->get_table( $ref_table_name ) or
401 return $self->error("Can't find table '$ref_table_name'");
402
403 if ( my $constraint = $ref_table->primary_key ) {
404 $self->{'reference_fields'} = [ $constraint->fields ];
405 }
406 else {
407 $self->error(
408 'No reference fields defined and cannot find primary key in ',
409 "reference table '$ref_table_name'"
410 );
411 }
412 }
413
414 if ( ref $self->{'reference_fields'} ) {
415 return wantarray
416 ? @{ $self->{'reference_fields'} || [] }
417 : $self->{'reference_fields'};
418 }
419 else {
420 return wantarray ? () : [];
421 }
422}
423
424# ----------------------------------------------------------------------
425sub reference_table {
426
427=pod
428
429=head2 reference_table
430
431Get or set the table referred to by the constraint.
432
433 my $reference_table = $constraint->reference_table('foo');
434
435=cut
436
437 my $self = shift;
438 $self->{'reference_table'} = shift if @_;
439 return $self->{'reference_table'} || '';
440}
441
3c5de62a 442# ----------------------------------------------------------------------
43b9dc7a 443sub table {
3c5de62a 444
445=pod
446
43b9dc7a 447=head2 table
3c5de62a 448
43b9dc7a 449Get or set the field's table object.
3c5de62a 450
43b9dc7a 451 my $table = $field->table;
3c5de62a 452
453=cut
454
455 my $self = shift;
43b9dc7a 456 if ( my $arg = shift ) {
457 return $self->error('Not a table object') unless
458 UNIVERSAL::isa( $arg, 'SQL::Translator::Schema::Table' );
459 $self->{'table'} = $arg;
460 }
461
462 return $self->{'table'};
463}
464
465# ----------------------------------------------------------------------
dedb8f3b 466sub type {
43b9dc7a 467
468=pod
469
dedb8f3b 470=head2 type
43b9dc7a 471
dedb8f3b 472Get or set the constraint's type.
43b9dc7a 473
dedb8f3b 474 my $type = $constraint->type( PRIMARY_KEY );
43b9dc7a 475
476=cut
477
dedb8f3b 478 my $self = shift;
43b9dc7a 479
dedb8f3b 480 if ( my $type = uc shift ) {
481 $type =~ s/_/ /g;
482 return $self->error("Invalid constraint type: $type")
483 unless VALID_TYPE->{ $type };
484 $self->{'type'} = $type;
43b9dc7a 485 }
3c5de62a 486
dedb8f3b 487 return $self->{'type'} || '';
488}
752608d5 489# ----------------------------------------------------------------------
490sub DESTROY {
491 my $self = shift;
492 undef $self->{'table'}; # destroy cyclical reference
493}
494
3c5de62a 4951;
496
497# ----------------------------------------------------------------------
498
499=pod
500
501=head1 AUTHOR
502
503Ken Y. Clark E<lt>kclark@cpan.orgE<gt>
504
505=cut