File Coverage

blib/lib/Test/Fixture/DBI.pm
Criterion Covered Total %
statement 24 135 17.7
branch 0 52 0.0
condition 0 12 0.0
subroutine 8 19 42.1
pod 3 3 100.0
total 35 221 15.8


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