File Coverage

blib/lib/SQL/Script.pm
Criterion Covered Total %
statement 57 69 82.6
branch 15 34 44.1
condition 2 6 33.3
subroutine 13 13 100.0
pod 6 6 100.0
total 93 128 72.6


line stmt bran cond sub pod time code
1             package SQL::Script;
2              
3             =pod
4              
5             =head1 NAME
6              
7             SQL::Script - An object representing a series of SQL statements, normally
8             stored in a file
9              
10             =head1 PREAMBLE
11              
12             For far too long we have been throwing SQL scripts at standalone binary
13             clients, it's about time we had some way to throw them at the DBI instead.
14              
15             Since I'm sick and tired of waiting for someone that knows more about SQL
16             than me to do it properly, I shall implement it myself, and wait for said
17             people to send me patches to fix anything I do wrong.
18              
19             At least this way I know the API will be done in a usable way.
20              
21             =head1 DESCRIPTION
22              
23             This module provides a very simple and straight forward way to work with a
24             file or string that contains a series of SQL statements.
25              
26             In essense, all this module really does is slurp in a file and split it
27             by semi-colon+newline.
28              
29             However, by providing an initial data object and API for this function, my
30             hope is that as more people use this module, better mechanisms can be
31             implemented underneath the same API at a later date to read and split the
32             script in a more thorough and complete way.
33              
34             It may well become the case that SQL::Script acts as a front end for a whole
35             quite of format-specific SQL splitters.
36              
37             =head1 METHODS
38              
39             =cut
40              
41 2     2   36193 use 5.006;
  2         8  
  2         80  
42 2     2   10 use strict;
  2         4  
  2         64  
43 2     2   20 use Carp ();
  2         3  
  2         41  
44 2     2   1834 use Params::Util qw{ _STRING _SCALAR _INSTANCE };
  2         9789  
  2         160  
45              
46 2     2   16 use vars qw{$VERSION};
  2         3  
  2         84  
47             BEGIN {
48 2     2   1555 $VERSION = '1.06';
49             }
50              
51              
52              
53              
54              
55             #####################################################################
56             # Constructor and Accessors
57              
58             =pod
59              
60             =head2 new
61              
62             # Default naive split
63             $script = SQL::Script->new;
64            
65             # Custom split (string)
66             $script = SQL::Script->new( split_by => "\n\n;" );
67            
68             # Custom split (regexp)
69             $script = SQL::Script->new( split_by => qr/\n\n;/ );
70            
71             # Create a script object from pre-split statements
72             $script = SQL::Script->new( statements => \@sql );
73              
74             The C constructor create a new SQL script object, containing zero statements.
75              
76             It takes a single option param or C which can be either a string
77             or regexp by which to split the SQL.
78              
79             Returns a new B object, or throws an exception on error.
80              
81             =cut
82            
83             sub new {
84 2     2 1 2813 my $class = shift;
85 2         9 my $self = bless { statements => [], @_ }, $class;
86              
87             # Check and apply default params
88 2 50       11 unless ( $self->split_by ) {
89 2         7 $self->{split_by} = ";\n";
90             }
91 2 50 33     6 unless ( _STRING($self->split_by) or ref($self->split_by) eq 'Regexp' ) {
92 0         0 Carp::croak("Missing or invalid split_by param");
93             }
94              
95 2         6 return $self;
96             }
97              
98             =pod
99              
100             =head2 read
101              
102             # Read a SQL script from one of several sources
103             $script->read( 'filename.sql' );
104             $script->read( \$sql_string );
105             $script->read( $io_handle );
106              
107             The C method is used to read SQL from an input source (which can
108             be provided as either a file name, a reference to a SCALAR containing the
109             SQL, or as an IO handle) and split it into a set of statements.
110              
111             If the B object already contains a set of statements, they will
112             be overwritten and replaced.
113              
114             Returns true on success, or throw an exception on error.
115              
116             =cut
117              
118             sub read {
119 1     1 1 3 my $self = shift;
120 1 50       4 my $input = _INPUT_SCALAR(shift) or Carp::croak("Missing or invalid param to read");
121 1         5 $self->{statements} = $self->split_sql( $input );
122 1         7 return 1;
123             }
124              
125             =pod
126              
127             =head2 split_by
128              
129             The C accessor returns the string or regexp that will be used to
130             split the SQL into statements.
131              
132             =cut
133              
134             sub split_by {
135 7     7 1 575 $_[0]->{split_by};
136             }
137              
138             =pod
139              
140             =head2 statements
141              
142             In list context, the C method returns a list of all the
143             individual statements for the script.
144              
145             In scalar context, it returns the number of statements.
146              
147             =cut
148              
149             sub statements {
150 7 100   7 1 462 if ( wantarray ) {
151 4         5 return @{$_[0]->{statements}};
  4         26  
152             } else {
153 3         4 return scalar @{$_[0]->{statements}};
  3         20  
154             }
155             }
156              
157              
158              
159              
160              
161             #####################################################################
162             # Main Methods
163              
164             =pod
165              
166             =head2 split_sql
167              
168             The C method takes a reference to a SCALAR containing a string
169             of SQL statements, and splits it into the separate statements, returning
170             them as a reference to an ARRAY, or throwing an exception on error.
171              
172             This method does NOT update the internal state, it simply applies the
173             appropriate parsing rules.
174              
175             =cut
176              
177             sub split_sql {
178 1     1 1 2 my $self = shift;
179 1 50       7 my $sql = _SCALAR(shift) or Carp::croak("Did not pass a SCALAR ref to split_sql");
180              
181             # Find the regex to split by
182 1         714 my $regexp;
183 1 50       4 if ( _STRING($self->split_by) ) {
    0          
184 1         3 $regexp = quotemeta $self->split_by;
185 1         22 $regexp = qr/$regexp/;
186             } elsif ( ref($self->split_by) eq 'Regexp' ) {
187 0         0 $regexp = $self->split_by;
188             } else {
189 0         0 Carp::croak("Unknown or unsupported split_by value");
190             }
191              
192             # Split the sql, clean up and remove empty ones
193 1         6 my @statements = grep { /\S/ } split( $regexp, $$sql );
  4         12  
194 1         2 foreach ( @statements ) {
195 2         7 s/^\s+//;
196 2         9 s/\s+$//;
197             }
198              
199 1         6 return \@statements;
200             }
201              
202             =pod
203              
204             =head2 run
205              
206             The C method executes the SQL statements in the script object.
207              
208             Returns true if ALL queries are executed successfully, or C on error.
209              
210             (These return values may be changed in future, probably to a style where all
211             the successfully executed queries are returned, and the object throws an
212             exception on error)
213              
214             =cut
215              
216             sub run {
217 1     1 1 10 my $self = shift;
218 1 50       8 my $dbh = _INSTANCE(shift, 'DBI::db') or Carp::croak("Did not provide DBI handle to run");
219              
220             # Execute each of the statements
221 1         13 foreach my $sql ( $self->statements ) {
222 2 50       12 $dbh->do($sql) or return undef;
223             }
224 1         9 return 1;
225             }
226              
227              
228              
229              
230              
231             #####################################################################
232             # Support Functions
233              
234             sub _INPUT_SCALAR {
235 1 50   1   6 unless ( defined $_[0] ) {
236 0         0 return undef;
237             }
238 1 50       4 unless ( ref $_[0] ) {
239 1 50 33     28 unless ( -f $_[0] and -r _ ) {
240 0         0 return undef;
241             }
242 1         5 local $/ = undef;
243 1 50       38 open( my $file, '<', $_[0] ) or return undef;
244 1 50       27 defined(my $buffer = <$file>) or return undef;
245 1 50       13 close( $file ) or return undef;
246 1         9 return \$buffer;
247             }
248 0 0         if ( _SCALAR($_[0]) ) {
249 0           return shift;
250             }
251 0 0         if ( _HANDLE($_[0]) ) {
252 0           local $/ = undef;
253 0           my $buffer = <$_[0]>;
254 0           return \$buffer;
255             }
256 0           return undef;
257             }
258              
259             1;
260              
261             =pod
262              
263             =head1 SUPPORT
264              
265             Bugs should be reported via the CPAN bug tracker at
266              
267             L
268              
269             For other issues, contact the author.
270              
271             =head1 AUTHOR
272              
273             Adam Kennedy Eadamk@cpan.orgE
274              
275             =head1 COPYRIGHT
276              
277             Copyright 2007 - 2009 Adam Kennedy.
278              
279             This program is free software; you can redistribute
280             it and/or modify it under the same terms as Perl itself.
281              
282             The full text of the license can be found in the
283             LICENSE file included with this module.
284              
285             =cut