Added use of "parse_list_arg," changed "nullable" method to "is_nullable"
[dbsrgits/SQL-Translator.git] / lib / SQL / Translator / Schema / Constraint.pm
CommitLineData
3c5de62a 1package SQL::Translator::Schema::Constraint;
2
3# ----------------------------------------------------------------------
752608d5 4# $Id: Constraint.pm,v 1.3 2003-05-09 17:06:11 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(
43b9dc7a 73 table => $table, # the table to which it belongs
3c5de62a 74 type => 'foreign_key', # type of table constraint
75 name => 'fk_phone_id', # the name of the constraint
76 fields => 'phone_id', # the field in the referring table
77 reference_fields => 'phone_id', # the referenced table
78 reference_table => 'phone', # the referenced fields
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# ----------------------------------------------------------------------
43b9dc7a 294sub on_delete {
295
296=pod
297
298=head2 on_delete
299
300Get or set the constraint's "on delete" action.
301
302 my $action = $constraint->on_delete('cascade');
303
304=cut
305
306 my $self = shift;
307
308 if ( my $arg = shift ) {
309 # validate $arg?
310 $self->{'on_delete'} = $arg;
311 }
312
313 return $self->{'on_delete'} || '';
314}
315
316# ----------------------------------------------------------------------
317sub on_update {
318
319=pod
320
321=head2 on_update
322
323Get or set the constraint's "on update" action.
324
325 my $action = $constraint->on_update('no action');
326
327=cut
328
329 my $self = shift;
330
331 if ( my $arg = shift ) {
332 # validate $arg?
333 $self->{'on_update'} = $arg;
334 }
335
336 return $self->{'on_update'} || '';
337}
338
339# ----------------------------------------------------------------------
340sub reference_fields {
341
342=pod
343
344=head2 reference_fields
345
346Gets and set the fields in the referred table. Accepts a string, list or
347arrayref; returns an array or array reference.
348
349 $constraint->reference_fields('id');
350 $constraint->reference_fields('id', 'name');
351 $constraint->reference_fields( 'id, name' );
352 $constraint->reference_fields( [ 'id', 'name' ] );
353 $constraint->reference_fields( qw[ id name ] );
354
355 my @reference_fields = $constraint->reference_fields;
356
357=cut
358
359 my $self = shift;
752608d5 360 my $fields = parse_list_arg( @_ );
43b9dc7a 361
362 if ( @$fields ) {
363 $self->{'reference_fields'} = $fields;
364 }
365
366 unless ( ref $self->{'reference_fields'} ) {
367 my $table = $self->table or return $self->error('No table');
368 my $schema = $table->schema or return $self->error('No schema');
369 my $ref_table_name = $self->reference_table or
370 return $self->error('No table');
371 my $ref_table = $schema->get_table( $ref_table_name ) or
372 return $self->error("Can't find table '$ref_table_name'");
373
374 if ( my $constraint = $ref_table->primary_key ) {
375 $self->{'reference_fields'} = [ $constraint->fields ];
376 }
377 else {
378 $self->error(
379 'No reference fields defined and cannot find primary key in ',
380 "reference table '$ref_table_name'"
381 );
382 }
383 }
384
385 if ( ref $self->{'reference_fields'} ) {
386 return wantarray
387 ? @{ $self->{'reference_fields'} || [] }
388 : $self->{'reference_fields'};
389 }
390 else {
391 return wantarray ? () : [];
392 }
393}
394
395# ----------------------------------------------------------------------
396sub reference_table {
397
398=pod
399
400=head2 reference_table
401
402Get or set the table referred to by the constraint.
403
404 my $reference_table = $constraint->reference_table('foo');
405
406=cut
407
408 my $self = shift;
409 $self->{'reference_table'} = shift if @_;
410 return $self->{'reference_table'} || '';
411}
412
413
414# ----------------------------------------------------------------------
3c5de62a 415sub type {
416
417=pod
418
419=head2 type
420
421Get or set the constraint's type.
422
43b9dc7a 423 my $type = $constraint->type( PRIMARY_KEY );
3c5de62a 424
425=cut
426
427 my $self = shift;
428
429 if ( my $type = shift ) {
430 return $self->error("Invalid constraint type: $type")
431 unless VALID_TYPE->{ $type };
432 $self->{'type'} = $type;
433 }
434
435 return $self->{'type'} || '';
436}
437
438
439# ----------------------------------------------------------------------
43b9dc7a 440sub table {
3c5de62a 441
442=pod
443
43b9dc7a 444=head2 table
3c5de62a 445
43b9dc7a 446Get or set the field's table object.
3c5de62a 447
43b9dc7a 448 my $table = $field->table;
3c5de62a 449
450=cut
451
452 my $self = shift;
43b9dc7a 453 if ( my $arg = shift ) {
454 return $self->error('Not a table object') unless
455 UNIVERSAL::isa( $arg, 'SQL::Translator::Schema::Table' );
456 $self->{'table'} = $arg;
457 }
458
459 return $self->{'table'};
460}
461
462# ----------------------------------------------------------------------
463sub options {
464
465=pod
466
467=head2 options
468
469Gets or adds to the constraints's options (e.g., "INITIALLY IMMEDIATE").
470Returns an array or array reference.
471
472 $constraint->options('NORELY');
473 my @options = $constraint->options;
474
475=cut
476
477 my $self = shift;
752608d5 478 my $options = parse_list_arg( @_ );
43b9dc7a 479
480 push @{ $self->{'options'} }, @$options;
481
482 if ( ref $self->{'options'} ) {
483 return wantarray ? @{ $self->{'options'} || [] } : $self->{'options'};
484 }
485 else {
486 return wantarray ? () : [];
487 }
3c5de62a 488}
489
752608d5 490# ----------------------------------------------------------------------
491sub DESTROY {
492 my $self = shift;
493 undef $self->{'table'}; # destroy cyclical reference
494}
495
3c5de62a 4961;
497
498# ----------------------------------------------------------------------
499
500=pod
501
502=head1 AUTHOR
503
504Ken Y. Clark E<lt>kclark@cpan.orgE<gt>
505
506=cut