File Coverage

blib/lib/Test/Fixture/DBI.pm
Criterion Covered Total %
statement 21 172 12.2
branch 0 76 0.0
condition 0 45 0.0
subroutine 7 23 30.4
pod 3 3 100.0
total 31 319 9.7


line stmt bran cond sub pod time code
1             package Test::Fixture::DBI;
2              
3 6     6   3425 use strict;
  6         12  
  6         235  
4 6     6   31 use warnings;
  6         11  
  6         244  
5              
6             our $VERSION = '0.07';
7              
8 6     6   33 use Carp;
  6         11  
  6         556  
9 6     6   58 use Exporter qw(import);
  6         11  
  6         210  
10 6     6   33 use Scalar::Util qw(blessed);
  6         10  
  6         692  
11 6     6   7670 use SQL::Abstract;
  6         84578  
  6         243  
12 6     6   6372 use SQL::Abstract::Plugin::InsertMulti;
  6         128450  
  6         63  
13              
14             our @EXPORT = qw(construct_database construct_fixture);
15             our @EXPORT_OK = qw( construct_trigger );
16             our %EXPORT_TAGS = (
17             default => [@EXPORT],
18             all => [ @EXPORT, @EXPORT_OK ],
19             );
20              
21 0     0     sub _SCALAR { 1 };
22 0     0     sub _ARRAYREF { 1 << 1; };
23 0     0     sub _HASHREF { 1 << 2; };
24 0     0     sub _OBJECT { 1 << 3; };
25              
26             sub _validate_with {
27 0     0     my %def = @_;
28              
29 0           my %params = @{$def{params}};
  0            
30 0           my %specs = %{$def{spec}};
  0            
31              
32 0           for my $field ( keys %specs ) {
33 0           my $spec = $specs{$field};
34 0           my $param = $params{$field};
35              
36 0 0 0       if ( exists $spec->{required} && $spec->{required} && !exists $params{$field} ) {
      0        
37 0           croak sprintf( '%s field is required.', $field );
38             }
39              
40 0 0 0       if ( exists $spec->{default} && !defined $param ) {
41 0           $params{$field} = $spec->{default};
42             }
43              
44 0 0         next unless ( defined $param );
45              
46 0 0         if ( exists $spec->{type} ) {
47 0           my $is_valid_type = 0;
48              
49 0 0 0       if ( ( $spec->{type} & _SCALAR ) == _SCALAR && !ref $param) {
50 0           $is_valid_type = 1;
51             }
52 0 0 0       if ( ( $spec->{type} & _ARRAYREF ) == _ARRAYREF && ref $param eq 'ARRAY' ) {
53 0           $is_valid_type = 1;
54             }
55 0 0 0       if ( ( $spec->{type} & _HASHREF ) == _HASHREF && ref $param eq 'HASH' ) {
56 0           $is_valid_type = 1;
57             }
58 0 0 0       if ( ( $spec->{type} & _OBJECT ) == _OBJECT && blessed($param) ) {
59 0           $is_valid_type = 1;
60             }
61              
62 0 0         unless ( $is_valid_type ) {
63 0           croak sprintf( '%s field is not valid type', $field );
64             }
65             }
66              
67 0 0 0       if ( exists $spec->{isa} && !UNIVERSAL::isa( $param, $spec->{isa} ) ) {
68 0           croak sprintf( '%s field is not a %s instance', $field, $spec->{isa} );
69             }
70              
71             }
72              
73 0           return %params;
74             }
75              
76             sub construct_database {
77 0     0 1   my %args = _validate_with(
78             params => \@_,
79             spec => +{
80             dbh => +{
81             type => _OBJECT,
82             isa => 'DBI::db',
83             required => 1,
84             },
85             database => +{
86             type => _SCALAR | _ARRAYREF,
87             required => 1,
88             },
89             schema => +{
90             type => _ARRAYREF,
91             required => 0,
92             default => [],
93             },
94             procedure => +{
95             type => _ARRAYREF,
96             required => 0,
97             default => [],
98             },
99             function => +{
100             type => _ARRAYREF,
101             required => 0,
102             default => [],
103             },
104             event => +{
105             type => _ARRAYREF,
106             required => 0,
107             default => [],
108             },
109             index => +{
110             type => _ARRAYREF,
111             required => 0,
112             default => [],
113             },
114             },
115             );
116              
117 0 0 0       unless ( exists $args{dbh} && UNIVERSAL::isa( $args{dbh}, 'DBI::db' ) ) {
118 0           croak 'dbh field is not exists or is a DBI::db';
119             }
120              
121 0 0 0       unless ( exists $args{database} && ( !ref $args{database} || ref $args{database} eq 'ARRAY' ) ) {
      0        
122 0           croak 'database field is not exists or is a SCALAR or ARRAYREF';
123             }
124              
125 0           my $database = _validate_database( _load_database( $args{database} ) );
126              
127 0           return _setup_database( $args{dbh},
128 0           [ grep { !exists $_->{trigger} } @$database ], \%args );
129             }
130              
131             sub _validate_database {
132 0     0     my $stuff = shift;
133              
134 0           for my $data ( @$stuff ) {
135 0           my @data = %$data;
136              
137 0           _validate_with(
138             params => \@data,
139             spec => +{
140             schema => +{ type => _SCALAR, required => 0, },
141             procedure => +{ type => _SCALAR, required => 0, },
142             function => +{ type => _SCALAR, required => 0, },
143             trigger => +{ type => _SCALAR, required => 0, },
144             event => +{ type => _SCALAR, required => 0, },
145             index => +{ type => _SCALAR, required => 0, },
146             refer => +{ type => _SCALAR, required => 0, },
147             data => +{ type => _SCALAR, required => 1, },
148             },
149             );
150             }
151              
152 0           return $stuff;
153             }
154              
155             sub _load_database {
156 0     0     my $stuff = shift;
157              
158 0 0         if ( ref $stuff ) {
159 0 0         if ( ref $stuff eq 'ARRAY' ) {
160 0           return $stuff;
161             } else {
162 0           croak "invalid fixture stuff. should be ARRAY: $stuff";
163             }
164             } else {
165 0           require YAML::Syck;
166 0           return YAML::Syck::LoadFile($stuff);
167             }
168             }
169              
170             sub _setup_database {
171 0     0     my ( $dbh, $database, $args ) = @_;
172              
173 0           my @databases;
174 0 0         my $enable_schema_filter = @{ $args->{schema} } > 0 ? 1 : 0;
  0            
175              
176 0           my %tables =
177             $enable_schema_filter
178 0           ? map { $_ => undef } @{ $args->{schema} }
  0            
179 0           : map { $_->{schema} => undef }
180 0 0         grep { exists $_->{schema} } @$database;
181              
182 0           for my $def (@$database) {
183             next
184 0 0 0       unless ( exists $def->{schema}
185             && exists $tables{ $def->{schema} } );
186 0 0         $dbh->do( $def->{data} ) or croak( $dbh->errstr );
187 0           push( @databases, $def );
188             }
189              
190 0           my %indexes =
191 0 0 0       map { $_->{index} => undef }
192             grep {
193 0           exists $_->{index}
194             && exists $_->{refer}
195             && exists $tables{ $_->{refer} }
196             } @$database;
197              
198 0           for my $def (@$database) {
199             next
200 0 0 0       unless ( exists $def->{index}
201             && exists $tables{ $def->{refer} } );
202 0 0         $dbh->do( $def->{data} ) or croak( $dbh->errstr );
203 0           push( @databases, $def );
204             }
205              
206             ### TODO: considering index for SQLite
207 0           for my $target (qw/procedure function event/) {
208 0           my %targets =
209 0           @{ $args->{$target} } > 0
210 0           ? map { $_ => undef } @{ $args->{$target} }
  0            
211 0           : map { $_->{$target} => undef }
212 0 0         grep { exists $_->{$target} } @$database;
213              
214 0           for my $def (@$database) {
215             next
216 0 0 0       unless ( exists $def->{$target}
217             && exists $targets{ $def->{$target} } );
218 0 0         $dbh->do( $def->{data} ) or croak( $dbh->errstr );
219 0           push( @databases, $def );
220             }
221             }
222              
223 0           return \@databases;
224             }
225              
226             sub construct_trigger {
227 0     0 1   my %args = _validate_with(
228             params => \@_,
229             spec => +{
230             dbh => +{
231             type => _OBJECT,
232             isa => 'DBI::db',
233             required => 1,
234             },
235             database => +{
236             type => _SCALAR,
237             required => 0,
238             },
239             schema => +{
240             type => _ARRAYREF,
241             required => 0,
242             default => [],
243             },
244             },
245             );
246              
247 0           my $trigger = _validate_database( _load_database( $args{database} ) );
248 0 0         return _setup_trigger( $args{dbh},
249 0           [ grep { exists $_->{trigger} && exists $_->{refer} } @$trigger ],
250             \%args );
251             }
252              
253             sub _setup_trigger {
254 0     0     my ( $dbh, $trigger, $args ) = @_;
255 0           my @triggers;
256              
257 0           my %triggers =
258 0           @{ $args->{schema} } > 0
259 0           ? ( map { $_ => undef } @{ $args->{schema} } )
  0            
260 0 0         : ( map { $_->{refer} => undef } @$trigger );
261              
262 0           for my $def (@$trigger) {
263 0 0         next if ( !exists $triggers{ $def->{refer} } );
264 0 0         $dbh->do( $def->{data} ) or croak( $dbh->errstr );
265 0           push( @triggers, $def );
266             }
267              
268 0           return \@triggers;
269             }
270              
271             sub construct_fixture {
272 0     0 1   my %args = _validate_with(
273             params => \@_,
274             spec => +{
275             dbh => +{
276             type => _OBJECT,
277             isa => 'DBI::db',
278             required => 1,
279             },
280             fixture => +{
281             type => _SCALAR | _ARRAYREF,
282             required => 1,
283             },
284             opts => +{
285             type => _HASHREF,
286             required => 0,
287             default => +{ bulk_insert => 1, },
288             },
289             },
290             );
291              
292 0 0         $args{fixture} = [ $args{fixture} ] unless ( ref $args{fixture} );
293              
294             # $args{opts} ||= +{ bulk_insert => 1, };
295              
296 0           my $fixture = _validate_fixture( _load_fixture( $args{fixture} ) );
297              
298 0           _delete_all( $args{dbh}, $fixture );
299 0           return _insert( $args{dbh}, $fixture, $args{opts} );
300             }
301              
302             sub _validate_fixture {
303 0     0     my $stuff = shift;
304              
305 0           for my $data ( @$stuff ) {
306 0           my @data = %$data;
307 0           _validate_with(
308             params => \@data,
309             spec => +{
310             name => +{ type => _SCALAR, required => 1, },
311             schema => +{ type => _SCALAR, required => 1, },
312             data => +{ type => _SCALAR | _ARRAYREF | _HASHREF, required => 1, },
313             },
314             );
315             }
316              
317 0           return $stuff;
318             }
319              
320             sub _load_fixture {
321 0     0     my $stuff = shift;
322              
323 0 0         if ( ref $stuff ) {
324 0 0         if ( ref $stuff eq 'ARRAY' ) {
325 0 0         if ( ref $stuff->[0] ) {
326 0           return $stuff;
327             } else {
328 0           require YAML::Syck;
329 0           return [ map { @{ YAML::Syck::LoadFile($_) } } @$stuff ];
  0            
  0            
330             }
331             } else {
332 0           croak "invalid fixture stuff. should be ARRAY: $stuff";
333             }
334             } else {
335 0           croak "invalid fixture stuff. should be ARRAY: $stuff";
336             }
337             }
338              
339             sub _delete_all {
340 0     0     my ( $dbh, $fixture ) = @_;
341              
342 0           my %seen;
343 0           my @schema = grep { !$seen{$_}++ } map { $_->{schema} } @$fixture;
  0            
  0            
344              
345 0           for my $schema (@schema) {
346 0 0         $dbh->do( sprintf( 'DELETE FROM %s', $schema ) )
347             or croak( $dbh->errstr );
348             }
349             }
350              
351             sub _insert {
352 0     0     my ( $dbh, $fixture, $opts ) = @_;
353              
354 0           my %seen;
355 0           my @schema = grep { !$seen{$_}++ } map { $_->{schema} } @$fixture;
  0            
  0            
356              
357 0           my $sql = SQL::Abstract->new;
358 0           my ( $stmt, @bind );
359              
360 0           for my $schema (@schema) {
361 0           my @records =
362 0           map { $_->{data} } grep { $_->{schema} eq $schema } @$fixture;
  0            
363 0           my @records_tmp;
364              
365 0 0         if ( $opts->{bulk_insert} ) {
366 0           while ( ( @records_tmp = splice( @records, 0, 1000 ) ) > 0 ) {
367 0           ( $stmt, @bind ) = $sql->insert_multi( $schema, \@records_tmp );
368 0 0         $dbh->do( $stmt, undef, @bind ) or croak( $dbh->errstr );
369 0 0         $dbh->commit or croak( $dbh->errstr );
370             }
371             } else {
372 0           while ( ( @records_tmp = splice( @records, 0, 1000 ) ) > 0 ) {
373 0           for (@records_tmp) {
374 0           ( $stmt, @bind ) = $sql->insert( $schema, $_ );
375 0 0         $dbh->do( $stmt, undef, @bind ) or croak( $dbh->errstr );
376             }
377 0 0         $dbh->commit or croak( $dbh->errstr );
378             }
379             }
380             }
381              
382 0           return $fixture;
383             }
384              
385             1;
386             __END__