File Coverage

blib/lib/ORLite/Migrate/Timeline.pm
Criterion Covered Total %
statement 38 49 77.5
branch 6 12 50.0
condition 2 3 66.6
subroutine 12 19 63.1
pod 13 13 100.0
total 71 96 73.9


line stmt bran cond sub pod time code
1             package ORLite::Migrate::Timeline;
2              
3             =pod
4              
5             =head1 NAME
6              
7             ORLite::Migrate::Timeline - ORLite::Migrate timelines contained in a single class
8              
9             =head1 SYNOPSIS
10              
11             package My::Timeline;
12            
13             use strict;
14             use base 'ORLite::Migrate::Timeline';
15            
16             sub upgrade1 { $_[0]->do(<<'END_SQL') }
17             CREATE TABLE foo (
18             bar INTEGER NOT NULL PRIMARY KEY,
19             )
20             END_SQL
21            
22             sub upgrade2 {
23             my $self = shift;
24             $self->do('TRUNCATE TABLE foo');
25             foreach ( 1 .. 10 ) {
26             $self->do( 'INSERT INTO foo VALUES ( ? )', {}, $_ );
27             }
28             }
29            
30             1;
31              
32             =head1 DESCRIPTION
33              
34             The default L timeline implementation makes use of separate
35             Perl "patch" scripts to move the database schema timeline forwards.
36              
37             This solution is preferred because the separate scripts provide process
38             isolation between your migration and run-time code. That is, the code that
39             migrates the schema a single step forwards is guarenteed to never use the same
40             variables or load the same modules or interact strangely with any other patch
41             scripts, or with the main program.
42              
43             However, to execute a sub-script your program needs to reliably know where the
44             Perl executable that launched it is and in some situations this is difficult or
45             infeasible.
46              
47             B provides an alternative mechanism for specifying the
48             migration timeline which adds the ability to run migration timelines in strange
49             Perl environments at the cost of losing process isolation for your patch code.
50              
51             When using this method, extra caution should be taken to avoid all use of global
52             variables, and to strictly avoid loading large amounts of data into memory or
53             using magic Perl modules such as L or L which might
54             have a global impact on your program.
55              
56             To use this method, create a new class which inherits from
57             L and create a C method. When encountering
58             a new unversioned SQLite database, the migration planner will execute this
59             C method and set the schema version to 1 once completed.
60              
61             To make further changes to the schema, you add additional C,
62             C and so on.
63              
64             =head1 METHODS
65              
66             A series of convenience methods are provided for you by the base class to
67             assist in making your schema patch code simpler and easier.
68              
69             =cut
70              
71 2     2   45401 use 5.006;
  2         9  
  2         82  
72 2     2   11 use strict;
  2         3  
  2         60  
73 2     2   11 use warnings;
  2         4  
  2         59  
74 2     2   2200 use DBI ();
  2         19158  
  2         54  
75 2     2   1505 use DBD::SQLite ();
  2         10770  
  2         52  
76 2     2   1016 use Params::Util ();
  2         6606  
  2         1622  
77              
78             our $VERSION = '1.10';
79              
80              
81              
82              
83              
84             ######################################################################
85             # Constructor
86              
87             =pod
88              
89             =head2 new
90              
91             my $timeline = My::Class->new(
92             dbh => $DBI_db_object,
93             );
94              
95             The C method is called internally by L on the timeline
96             class you specify to construct the timeline object.
97              
98             The constructor takes a single parameter which should be a L
99             database connection to your SQLite database.
100              
101             Returns an instance of your timeline class, or throws an exception (dies) if
102             not passed a DBI connection object, or the database handle is not C.
103              
104             =cut
105              
106             sub new {
107 1     1 1 545 my $class = shift;
108 1         6 my $self = bless { @_ }, $class;
109              
110             # Check the database handle
111 1 50       9 unless ( Params::Util::_INSTANCE( $self->dbh, 'DBI::db' ) ) {
112 0         0 die "Missing or invalid dbh database handle";
113             }
114 1 50       4 unless ( $self->dbh->{AutoCommit} ) {
115 0         0 die "Database connection must be AutoCommit";
116             }
117              
118 1         6 return $self;
119             }
120              
121              
122              
123              
124              
125             #######################################################################
126             # Internal Methods
127              
128             =pod
129              
130             =head2 upgrade
131              
132             $timeline->upgrade(10);
133              
134             The C method is called on the timeline object by L
135             to trigger the sequential execution of the individual C methods.
136              
137             The first method to be called will be the method one greater than the current
138             value of the C pragma, and the last method to be called will be
139             the target revision, the first parameter to the method.
140              
141             As all upgrade methods are contained in a single class, a high level of control
142             is assumed and so the execution plan will not be calculated in advance. The
143             C method will simply start rolling forwards and keep going until it
144             reaches the target version (or die's trying).
145              
146             Returns true if all (zero or more) upgrade methods executed without throwing
147             an exception.
148              
149             Throws an exception (dies) if any C method throws an exception, or
150             if the migration process expects to find a particular numeric C
151             method and cannot do so.
152              
153             =cut
154              
155             sub upgrade {
156 1     1 1 4 my $self = shift;
157 1         44 my $want = Params::Util::_POSINT(shift);
158 1         20 my $have = $self->pragma('user_version');
159              
160             # Roll the schema forwards
161 1   66     160 while ( $want and $want > $have ) {
162              
163             # Find the migration step
164 3         1296 my $method = "upgrade" . ++$have;
165 3 50       34 unless ( $self->can($method) ) {
166 0         0 die "No migration path to user_version $want";
167             }
168              
169             # Run the migration step
170 3 50       113 unless ( eval { $self->$method } ) {
  3         20  
171 0         0 die "Schema migration failed during $method: $@";
172             }
173              
174             # Confirm completion
175 3         45530 $self->pragma( 'user_version' => $have );
176             }
177              
178 1         347 return 1;
179             }
180              
181              
182              
183              
184              
185             ######################################################################
186             # Support Methods
187              
188             =pod
189              
190             =head2 do
191              
192             The C method is a convenience which provides a direct wrapper over the
193             L method C. It takes the same parameters and returns the same results.
194              
195             =cut
196              
197             sub do {
198 7     7 1 91894 shift->dbh->do(@_);
199             }
200              
201             =pod
202              
203             =head2 selectall_arrayref
204              
205             The C method is a convenience which provides a direct
206             wrapper over the L method C. It takes the same parameters
207             and returns the same results.
208              
209             =cut
210              
211             sub selectall_arrayref {
212 0     0 1 0 shift->dbh->selectall_arrayref(@_);
213             }
214              
215             =pod
216              
217             =head2 selectall_hashref
218              
219             The C method is a convenience which provides a direct
220             wrapper over the L method C. It takes the same parameters
221             and returns the same results.
222              
223             =cut
224              
225             sub selectall_hashref {
226 0     0 1 0 shift->dbh->selectall_hashref(@_);
227             }
228              
229             =pod
230              
231             =head2 selectcol_arrayref
232              
233             The C method is a convenience which provides a direct
234             wrapper over the L method C. It takes the same parameters
235             and returns the same results.
236              
237             =cut
238              
239             sub selectcol_arrayref {
240 0     0 1 0 shift->dbh->selectcol_arrayref(@_);
241             }
242              
243             =pod
244              
245             =head2 selectrow_array
246              
247             The C method is a convenience which provides a direct
248             wrapper over the L method C. It takes the same parameters
249             and returns the same results.
250              
251             =cut
252              
253             sub selectrow_array {
254 0     0 1 0 shift->dbh->selectrow_array(@_);
255             }
256              
257             =pod
258              
259             =head2 selectrow_arrayref
260              
261             The C method is a convenience which provides a direct
262             wrapper over the L method C. It takes the same parameters
263             and returns the same results.
264              
265             =cut
266              
267             sub selectrow_arrayref {
268 4     4 1 23 shift->dbh->selectrow_arrayref(@_);
269             }
270              
271             =pod
272              
273             =head2 selectrow_hashref
274              
275             The C method is a convenience which provides a direct
276             wrapper over the L method C. It takes the same parameters
277             and returns the same results.
278              
279             =cut
280              
281             sub selectrow_hashref {
282 0     0 1 0 shift->dbh->selectrow_hashref(@_);
283             }
284              
285             =pod
286              
287             =head2 pragma
288              
289             # Get a pragma value
290             my $locking = $self->pragma('locking_mode');
291            
292             # Set a pragma value
293             $self->pragma( synchronous => 0 );
294              
295             The C method provides a convenience over the top of the C SQL
296             statement, and allows the convenience query and change of SQLite pragmas.
297              
298             For example, if your application wanted to switch SQLite auto vacuuming off
299             and instead control vacuuming of the database manually, you could do something
300             like the following.
301              
302             # Disable auto-vacuuming because we'll only fill this once.
303             # Do a one-time vacuum so we start with a clean empty database.
304             $dbh->pragma( auto_vacuum => 0 );
305             $dbh->do('VACUUM');
306              
307             =cut
308              
309             sub pragma {
310 4 100   4 1 228 $_[0]->do("pragma $_[1] = $_[2]") if @_ > 2;
311 4         47696 $_[0]->selectrow_arrayref("pragma $_[1]")->[0];
312             }
313              
314             =pod
315              
316             =head2 table_exists
317              
318             The C method is a convenience to check for the existance of a
319             table already. Most of the time this isn't going to be needed because the
320             schema revisioning itself guarentees there is or is not an existing table of
321             a particular name.
322              
323             However, occasionally you may encounter a situation where your L module
324             is sharing a SQLite database with other code, or you are taking over control
325             of a table from a plugin, or similar.
326              
327             In these situations it provides a small amount of added safety to be able to
328             say things like.
329              
330             sub upgrade25 {
331             my $self = shift;
332             if ( $self->table_exists('foo') ) {
333             $self->do('DROP TABLE foo');
334             }
335             }
336              
337             Returns true (1) if the table exists or false (0) if not.
338              
339             =cut
340              
341             sub table_exists {
342 0     0 1 0 $_[0]->selectrow_array(
343             "select count(*) from sqlite_master where type = 'table' and name = ?",
344             {}, $_[1],
345             );
346             }
347              
348             =pod
349              
350             =head2 column_exists
351              
352             The C method is a convenience to check for the existance of a
353             column already. It has somewhat less uses than the similar C and
354             is mainly used when a column may exist on various miscellaneous developer
355             versions of databases, or where the table structure may be variable across
356             different groups of users.
357              
358             Returns true (1) if the table exists or false (0) if not.
359              
360             =cut
361              
362             sub column_exists {
363 0 0   0 1 0 $_[0]->table_exists( $_[1] )
364             or $_[0]->selectrow_array( "select count($_[2]) from $_[1]", {} );
365             }
366              
367             =pod
368              
369             =head2 dbh
370              
371             If you need to do something to the database outside the scope of the methods
372             described above, the C method can be used to get access to the database
373             connection directly.
374              
375             This is discouraged as it can allow your migration code to create changes that
376             might cause unexpected problems. However, in the 1% of cases where the methods
377             above are not enough, using it with caution will allow you to make changes that
378             would not otherwise be possible.
379              
380             =cut
381              
382             sub dbh {
383 13     13 1 1146 $_[0]->{dbh};
384             }
385              
386             1;
387              
388             =pod
389              
390             =head1 SUPPORT
391              
392             Bugs should be reported via the CPAN bug tracker at
393              
394             L
395              
396             For other issues, contact the author.
397              
398             =head1 AUTHOR
399              
400             Adam Kennedy Eadamk@cpan.orgE
401              
402             =head1 COPYRIGHT
403              
404             Copyright 2009 - 2012 Adam Kennedy.
405              
406             This program is free software; you can redistribute
407             it and/or modify it under the same terms as Perl itself.
408              
409             The full text of the license can be found in the
410             LICENSE file included with this module.
411              
412             =cut