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