Use weak refs for schema object attributes
[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
da0136cd 31use Moo;
32use SQL::Translator::Utils qw(parse_list_arg ex2err throw);
33use SQL::Translator::Types qw(schema_obj);
34use List::MoreUtils qw(uniq);
e2cf647c 35
da0136cd 36with qw(
46ad748f 37 SQL::Translator::Schema::Role::BuildArgs
da0136cd 38 SQL::Translator::Schema::Role::Extra
39 SQL::Translator::Schema::Role::Error
40 SQL::Translator::Schema::Role::Compare
41);
b6a880d1 42
0c04c5a2 43our $VERSION = '1.59';
e2cf647c 44
e2cf647c 45=head2 new
46
47Object constructor.
48
49 my $schema = SQL::Translator::Schema::Trigger->new;
50
51=cut
52
da0136cd 53around BUILDARGS => sub {
54 my ($orig, $self, @args) = @_;
55 my $args = $self->$orig(@args);
56 if (exists $args->{on_table}) {
57 my $arg = delete $args->{on_table};
58 my $table = $args->{schema}->get_table($arg)
59 or die "Table named $arg doesn't exist";
60 $args->{table} = $table;
61 }
62 if (exists $args->{database_event}) {
63 $args->{database_events} = delete $args->{database_event};
64 }
65 return $args;
66};
e2cf647c 67
68=head2 perform_action_when
69
ea93df61 70Gets or sets whether the event happens "before" or "after" the
e2cf647c 71C<database_event>.
72
73 $trigger->perform_action_when('after');
74
75=cut
76
da0136cd 77has perform_action_when => (
78 is => 'rw',
79 coerce => sub { defined $_[0] ? lc $_[0] : $_[0] },
80 isa => sub {
81 throw("Invalid argument '$_[0]' to perform_action_when")
82 if defined $_[0] and $_[0] !~ m/^(before|after)$/i;
83 },
84);
e2cf647c 85
da0136cd 86around perform_action_when => \&ex2err;
e2cf647c 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 103=head2 database_events
e2cf647c 104
8742e408 105Gets or sets the events that triggers the trigger.
106
d0fcb05d 107 my $ok = $trigger->database_events('insert');
8742e408 108
109=cut
110
da0136cd 111has database_events => (
112 is => 'rw',
113 coerce => sub { [ map { lc } ref $_[0] eq 'ARRAY' ? @{$_[0]} : ($_[0]) ] },
114 isa => sub {
115 my @args = @{$_[0]};
4348b2b4 116 my %valid = map { $_, 1 } qw[ insert update update_on delete ];
117 my @invalid = grep { !defined $valid{ $_ } } @args;
ea93df61 118
4348b2b4 119 if ( @invalid ) {
da0136cd 120 throw(
4348b2b4 121 sprintf("Invalid events '%s' in database_events",
122 join(', ', @invalid)
123 )
124 );
125 }
da0136cd 126 },
127);
4348b2b4 128
da0136cd 129around database_events => sub {
130 my ($orig,$self) = (shift, shift);
131
132 if (@_) {
133 ex2err($orig, $self, ref $_[0] eq 'ARRAY' ? $_[0] : \@_)
134 or return;
4348b2b4 135 }
136
ea93df61 137 return wantarray
da0136cd 138 ? @{ $self->$orig || [] }
139 : $self->$orig;
140};
e2cf647c 141
142=head2 fields
143
144Gets and set which fields to monitor for C<database_event>.
145
146 $view->fields('id');
147 $view->fields('id', 'name');
148 $view->fields( 'id, name' );
149 $view->fields( [ 'id', 'name' ] );
150 $view->fields( qw[ id name ] );
151
152 my @fields = $view->fields;
153
154=cut
155
da0136cd 156has fields => (
157 is => 'rw',
158 coerce => sub {
159 my @fields = uniq @{parse_list_arg($_[0])};
160 @fields ? \@fields : undef;
161 },
162);
163
164around fields => sub {
165 my $orig = shift;
166 my $self = shift;
e2cf647c 167 my $fields = parse_list_arg( @_ );
da0136cd 168 $self->$orig($fields) if @$fields;
e2cf647c 169
da0136cd 170 return wantarray ? @{ $self->$orig || [] } : $self->$orig;
171};
8ce5d615 172
173=head2 table
174
175Gets or set the table on which the trigger works, as a L<SQL::Translator::Schema::Table> object.
176 $trigger->table($triggered_table);
177
178=cut
179
a5bfeba8 180has table => ( is => 'rw', isa => schema_obj('Table'), weak_ref => 1 );
da0136cd 181
182around table => \&ex2err;
8ce5d615 183
e2cf647c 184sub on_table {
185
186=pod
187
188=head2 on_table
189
8ce5d615 190Gets or set the table name on which the trigger works, as a string.
191 $trigger->on_table('foo');
e2cf647c 192
193=cut
194
8ce5d615 195 my ($self, $arg) = @_;
196 if ( @_ == 2 ) {
197 my $table = $self->schema->get_table($arg);
198 die "Table named $arg doesn't exist"
199 if !$table;
200 $self->table($table);
201 }
202 return $self->table->name;
e2cf647c 203}
204
e2cf647c 205=head2 action
206
cb490ce5 207Gets or set the action of the trigger.
e2cf647c 208
cb490ce5 209 $trigger->action(
e2cf647c 210 q[
211 BEGIN
212 select ...;
213 update ...;
214 END
215 ]
216 );
217
218=cut
219
da0136cd 220has action => ( is => 'rw', default => sub { '' } );
e2cf647c 221
e2cf647c 222sub is_valid {
223
224=pod
225
226=head2 is_valid
227
228Determine whether the trigger is valid or not.
229
230 my $ok = $trigger->is_valid;
231
232=cut
233
234 my $self = shift;
235
ea93df61 236 for my $attr (
237 qw[ name perform_action_when database_events on_table action ]
e2cf647c 238 ) {
4348b2b4 239 return $self->error("Invalid: missing '$attr'") unless $self->$attr();
e2cf647c 240 }
ea93df61 241
242 return $self->error("Missing fields for UPDATE ON") if
e2cf647c 243 $self->database_event eq 'update_on' && !$self->fields;
244
245 return 1;
246}
247
e2cf647c 248=head2 name
249
250Get or set the trigger's name.
251
252 my $name = $trigger->name('foo');
253
254=cut
255
da0136cd 256has name => ( is => 'rw', default => sub { '' } );
e2cf647c 257
258=head2 order
259
260Get or set the trigger's order.
261
262 my $order = $trigger->order(3);
263
264=cut
265
da0136cd 266has order => ( is => 'rw', default => sub { 0 } );
267
268around order => sub {
269 my ( $orig, $self, $arg ) = @_;
e2cf647c 270
271 if ( defined $arg && $arg =~ /^\d+$/ ) {
da0136cd 272 return $self->$orig($arg);
e2cf647c 273 }
274
da0136cd 275 return $self->$orig;
276};
c96cd4a8 277
278=head2 scope
279
280Get or set the trigger's scope (row or statement).
281
282 my $scope = $trigger->scope('statement');
283
284=cut
285
da0136cd 286has scope => (
287 is => 'rw',
288 isa => sub {
289 my ($arg) = @_;
290 throw( "Invalid scope '$arg'" )
291 if defined $arg and $arg !~ /^(row|statement)$/i;
292 },
293);
c96cd4a8 294
da0136cd 295around scope => \&ex2err;
c96cd4a8 296
39ad1787 297
298=head2 schema
299
300Get or set the trigger's schema object.
301
302 $trigger->schema( $schema );
303 my $schema = $trigger->schema;
304
305=cut
306
a5bfeba8 307has schema => (is => 'rw', isa => schema_obj('Schema'), weak_ref => 1 );
39ad1787 308
da0136cd 309around schema => \&ex2err;
39ad1787 310
8742e408 311sub compare_arrays {
312
313=pod
314
315=head2 compare_arrays
316
317Compare two arrays.
318
319=cut
ea93df61 320
4348b2b4 321 my ($first, $second) = @_;
322 no warnings; # silence spurious -w undef complaints
323
324 return 0 unless (ref $first eq 'ARRAY' and ref $second eq 'ARRAY' ) ;
325
326 return 0 unless @$first == @$second;
327
328 my @first = sort @$first;
329
330 my @second = sort @$second;
331
332 for (my $i = 0; $i < scalar @first; $i++) {
333 return 0 if @first[$i] ne @second[$i];
334 }
335
336 return 1;
8742e408 337}
338
abf315bb 339=head2 equals
340
341Determines if this trigger is the same as another
342
4348b2b4 343 my $is_identical = $trigger1->equals( $trigger2 );
abf315bb 344
345=cut
346
da0136cd 347around equals => sub {
348 my $orig = shift;
4348b2b4 349 my $self = shift;
350 my $other = shift;
6be9534b 351 my $case_insensitive = shift;
ea93df61 352
da0136cd 353 return 0 unless $self->$orig($other);
4348b2b4 354
7f275a61 355 my %names;
ea93df61 356 for my $name ( $self->name, $other->name ) {
7f275a61 357 $name = lc $name if $case_insensitive;
358 $names{ $name }++;
359 }
4348b2b4 360
7f275a61 361 if ( keys %names > 1 ) {
362 return $self->error('Names not equal');
363 }
4348b2b4 364
7f275a61 365 if ( !$self->perform_action_when eq $other->perform_action_when ) {
366 return $self->error('perform_action_when differs');
367 }
368
ea93df61 369 if (
370 !compare_arrays( [$self->database_events], [$other->database_events] )
7f275a61 371 ) {
372 return $self->error('database_events differ');
373 }
4348b2b4 374
7f275a61 375 if ( $self->on_table ne $other->on_table ) {
376 return $self->error('on_table differs');
377 }
4348b2b4 378
7f275a61 379 if ( $self->action ne $other->action ) {
380 return $self->error('action differs');
381 }
4348b2b4 382
ea93df61 383 if (
7f275a61 384 !$self->_compare_objects( scalar $self->extra, scalar $other->extra )
385 ) {
386 return $self->error('extras differ');
387 }
4348b2b4 388
abf315bb 389 return 1;
da0136cd 390};
abf315bb 391
da0136cd 392# Must come after all 'has' declarations
393around new => \&ex2err;
394
e2cf647c 3951;
396
e2cf647c 397=pod
398
4348b2b4 399=head1 AUTHORS
e2cf647c 400
4348b2b4 401Anonymous,
402Ken Youens-Clark E<lt>kclark@cpan.orgE<gt>.
e2cf647c 403
404=cut