e3358d9a056ee7b3af1a24beb21e679e89872a7c
[dbsrgits/SQL-Translator.git] / lib / SQL / Translator / Schema / Trigger.pm
1 package SQL::Translator::Schema::Trigger;
2
3 =pod
4
5 =head1 NAME
6
7 SQL::Translator::Schema::Trigger - SQL::Translator trigger object
8
9 =head1 SYNOPSIS
10
11   use SQL::Translator::Schema::Trigger;
12   my $trigger = SQL::Translator::Schema::Trigger->new(
13     name                => 'foo',
14     perform_action_when => 'before', # or after
15     database_events     => [qw/update insert/], # also update, update_on, delete
16     fields              => [],       # if event is "update"
17     on_table            => 'foo',    # table name
18     action              => '...',    # text of trigger
19     schema              => $schema,  # Schema object
20   );
21
22 =head1 DESCRIPTION
23
24 C<SQL::Translator::Schema::Trigger> is the trigger object.
25
26 =head1 METHODS
27
28 =cut
29
30 use strict;
31 use SQL::Translator::Utils 'parse_list_arg';
32
33 use base 'SQL::Translator::Schema::Object';
34
35 use Carp;
36
37 use vars qw($VERSION $TABLE_COUNT $VIEW_COUNT);
38
39 $VERSION = '1.59';
40
41 __PACKAGE__->_attributes( qw/
42     name schema perform_action_when database_events database_event
43     fields table on_table action order
44 /);
45
46 =pod
47
48 =head2 new
49
50 Object constructor.
51
52   my $schema = SQL::Translator::Schema::Trigger->new;
53
54 =cut
55
56 sub perform_action_when {
57
58 =pod
59
60 =head2 perform_action_when
61
62 Gets or sets whether the event happens "before" or "after" the
63 C<database_event>.
64
65   $trigger->perform_action_when('after');
66
67 =cut
68
69     my $self = shift;
70
71     if ( my $arg = shift ) {
72         $arg =  lc $arg;
73         $arg =~ s/\s+/ /g;
74         if ( $arg =~ m/^(before|after)$/i ) {
75             $self->{'perform_action_when'} = $arg;
76         }
77         else {
78             return
79                 $self->error("Invalid argument '$arg' to perform_action_when");
80         }
81     }
82
83     return $self->{'perform_action_when'};
84 }
85
86 sub database_event {
87
88 =pod
89
90 =head2 database_event
91
92 Obsolete please use database_events!
93
94 =cut
95
96     my $self = shift;
97
98     return $self->database_events( @_ );
99 }
100
101 sub database_events {
102
103 =pod
104
105 =head2 database_events
106
107 Gets or sets the events that triggers the trigger.
108
109   my $ok = $trigger->database_events('insert');
110
111 =cut
112
113     my $self = shift;
114     my @args = ref $_[0] eq 'ARRAY' ? @{ $_[0] } : @_;
115
116     if ( @args ) {
117         @args       = map { s/\s+/ /g; lc $_ } @args;
118         my %valid   = map { $_, 1 } qw[ insert update update_on delete ];
119         my @invalid = grep { !defined $valid{ $_ } } @args;
120
121         if ( @invalid ) {
122             return $self->error(
123                 sprintf("Invalid events '%s' in database_events",
124                     join(', ', @invalid)
125                 )
126             );
127         }
128
129         $self->{'database_events'} = [ @args ];
130     }
131
132     return wantarray
133         ? @{ $self->{'database_events'} || [] }
134         : $self->{'database_events'};
135 }
136
137 sub fields {
138
139 =pod
140
141 =head2 fields
142
143 Gets and set which fields to monitor for C<database_event>.
144
145   $view->fields('id');
146   $view->fields('id', 'name');
147   $view->fields( 'id, name' );
148   $view->fields( [ 'id', 'name' ] );
149   $view->fields( qw[ id name ] );
150
151   my @fields = $view->fields;
152
153 =cut
154
155     my $self = shift;
156     my $fields = parse_list_arg( @_ );
157
158     if ( @$fields ) {
159         my ( %unique, @unique );
160         for my $f ( @$fields ) {
161             next if $unique{ $f };
162             $unique{ $f } = 1;
163             push @unique, $f;
164         }
165
166         $self->{'fields'} = \@unique;
167     }
168
169     return wantarray ? @{ $self->{'fields'} || [] } : $self->{'fields'};
170 }
171
172 sub table {
173
174 =pod
175
176 =head2 table
177
178 Gets or set the table on which the trigger works, as a L<SQL::Translator::Schema::Table> object.
179   $trigger->table($triggered_table);
180
181 =cut
182
183     my ($self, $arg) = @_;
184     if ( @_ == 2 ) {
185         $self->error("Table attribute of a ".__PACKAGE__.
186                      " must be a SQL::Translator::Schema::Table")
187             unless ref $arg and $arg->isa('SQL::Translator::Schema::Table');
188         $self->{table} = $arg;
189     }
190     return $self->{table};
191 }
192
193 sub on_table {
194
195 =pod
196
197 =head2 on_table
198
199 Gets or set the table name on which the trigger works, as a string.
200   $trigger->on_table('foo');
201
202 =cut
203
204     my ($self, $arg) = @_;
205     if ( @_ == 2 ) {
206         my $table = $self->schema->get_table($arg);
207         die "Table named $arg doesn't exist"
208             if !$table;
209         $self->table($table);
210     }
211     return $self->table->name;
212 }
213
214 sub action {
215
216 =pod
217
218 =head2 action
219
220 Gets or set the action of the trigger.
221
222   $trigger->action(
223       q[
224         BEGIN
225           select ...;
226           update ...;
227         END
228       ]
229   );
230
231 =cut
232
233     my $self = shift;
234     my $arg  = shift || '';
235     $self->{'action'} = $arg if $arg;
236     return $self->{'action'};
237 }
238
239 sub is_valid {
240
241 =pod
242
243 =head2 is_valid
244
245 Determine whether the trigger is valid or not.
246
247   my $ok = $trigger->is_valid;
248
249 =cut
250
251     my $self = shift;
252
253     for my $attr (
254         qw[ name perform_action_when database_events on_table action ]
255     ) {
256         return $self->error("Invalid: missing '$attr'") unless $self->$attr();
257     }
258
259     return $self->error("Missing fields for UPDATE ON") if
260         $self->database_event eq 'update_on' && !$self->fields;
261
262     return 1;
263 }
264
265 sub name {
266
267 =pod
268
269 =head2 name
270
271 Get or set the trigger's name.
272
273   my $name = $trigger->name('foo');
274
275 =cut
276
277     my $self        = shift;
278     $self->{'name'} = shift if @_;
279     return $self->{'name'} || '';
280 }
281
282 sub order {
283
284 =pod
285
286 =head2 order
287
288 Get or set the trigger's order.
289
290   my $order = $trigger->order(3);
291
292 =cut
293
294     my ( $self, $arg ) = @_;
295
296     if ( defined $arg && $arg =~ /^\d+$/ ) {
297         $self->{'order'} = $arg;
298     }
299
300     return $self->{'order'} || 0;
301 }
302
303 sub schema {
304
305 =pod
306
307 =head2 schema
308
309 Get or set the trigger's schema object.
310
311   $trigger->schema( $schema );
312   my $schema = $trigger->schema;
313
314 =cut
315
316     my $self = shift;
317     if ( my $arg = shift ) {
318         return $self->error('Not a schema object') unless
319             UNIVERSAL::isa( $arg, 'SQL::Translator::Schema' );
320         $self->{'schema'} = $arg;
321     }
322
323     return $self->{'schema'};
324 }
325
326 sub compare_arrays {
327
328 =pod
329
330 =head2 compare_arrays
331
332 Compare two arrays.
333
334 =cut
335
336     my ($first, $second) = @_;
337     no warnings;  # silence spurious -w undef complaints
338
339     return 0 unless (ref $first eq 'ARRAY' and ref $second eq 'ARRAY' ) ;
340
341     return 0 unless @$first == @$second;
342
343     my @first = sort @$first;
344
345     my @second = sort @$second;
346
347     for (my $i = 0; $i < scalar @first; $i++) {
348         return 0 if @first[$i] ne @second[$i];
349     }
350
351     return 1;
352 }
353
354 sub equals {
355
356 =pod
357
358 =head2 equals
359
360 Determines if this trigger is the same as another
361
362   my $is_identical = $trigger1->equals( $trigger2 );
363
364 =cut
365
366     my $self             = shift;
367     my $other            = shift;
368     my $case_insensitive = shift;
369
370     return 0 unless $self->SUPER::equals($other);
371
372     my %names;
373     for my $name ( $self->name, $other->name ) {
374         $name = lc $name if $case_insensitive;
375         $names{ $name }++;
376     }
377
378     if ( keys %names > 1 ) {
379         return $self->error('Names not equal');
380     }
381
382     if ( !$self->perform_action_when eq $other->perform_action_when ) {
383         return $self->error('perform_action_when differs');
384     }
385
386     if (
387         !compare_arrays( [$self->database_events], [$other->database_events] )
388     ) {
389         return $self->error('database_events differ');
390     }
391
392     if ( $self->on_table ne $other->on_table ) {
393         return $self->error('on_table differs');
394     }
395
396     if ( $self->action ne $other->action ) {
397         return $self->error('action differs');
398     }
399
400     if (
401         !$self->_compare_objects( scalar $self->extra, scalar $other->extra )
402     ) {
403         return $self->error('extras differ');
404     }
405
406     return 1;
407 }
408
409 sub DESTROY {
410     my $self = shift;
411     undef $self->{'schema'}; # destroy cyclical reference
412 }
413
414 1;
415
416 =pod
417
418 =head1 AUTHORS
419
420 Anonymous,
421 Ken Youens-Clark E<lt>kclark@cpan.orgE<gt>.
422
423 =cut