File Coverage

blib/lib/SQL/Tiny.pm
Criterion Covered Total %
statement 98 100 98.0
branch 23 24 95.8
condition 2 2 100.0
subroutine 9 9 100.0
pod 4 4 100.0
total 136 139 97.8


line stmt bran cond sub pod time code
1             package SQL::Tiny;
2              
3 5     5   328031 use 5.010001;
  5         57  
4 5     5   25 use strict;
  5         9  
  5         150  
5 5     5   30 use warnings;
  5         7  
  5         266  
6              
7             =head1 NAME
8              
9             SQL::Tiny - A very simple SQL-building library
10              
11             =head1 VERSION
12              
13             Version 0.01
14              
15             =cut
16              
17             our $VERSION = '0.01';
18              
19 5     5   2055 use parent 'Exporter';
  5         1461  
  5         26  
20              
21             our @EXPORT_OK = qw(
22             sql_select
23             sql_insert
24             sql_update
25             sql_delete
26             );
27              
28              
29             =head1 SYNOPSIS
30              
31             my ($sql,$bind) = sql_select( 'users', [ 'name', 'status' ], { status => [ 'Deleted', 'Inactive' ] );
32              
33             my ($sql,$bind) = sql_insert( 'users', { name => 'Dave', status => 'Active' } );
34              
35             my ($sql,$bind) = sql_update( 'users', { status => 'Inactive' }, { password => undef } );
36              
37             my ($sql,$bind) = sql_delete( 'users', { status => 'Inactive' } );
38              
39             =head1 DOCUMENTATION
40              
41             A very simple SQL-building library. It's not for all your SQL needs,
42             only the very simple ones.
43              
44             It doesn't handle JOINs. It doesn't handle GROUP BY. It doens't handle
45             subselects. It's only for simple SQL.
46              
47             In my test suites, I have a lot of ad hoc SQL queries, and it drives me
48             nuts to have so much SQL code lying around. SQL::Tiny is for generating
49             SQL code for simple cases.
50              
51             I'd far rather have:
52              
53             my ($sql,$binds) = sql_insert( 'users', { name => 'Dave', status => 'Active' } );
54              
55             than hand-coding:
56              
57             my $sql = 'INSERT INTO users (name,status) VALUES (:name,:status)';
58             my $binds = { ':name' => 'Dave', ':status' => 'Active' };
59              
60             or even the positional:
61              
62             my $sql = 'INSERT INTO users (name,status) VALUES (?,?)';
63             my $binds = [ 'Dave', 'Active' ];
64              
65             You don't want to use SQL::Tiny where speed is essential.
66              
67             =head1 EXPORT
68              
69             All subs can be exported, but none are by default.
70              
71             =head1 SUBROUTINES/METHODS
72              
73             =head2 sql_select( $table, \@columns, \%where [, \%other ] )
74              
75             Creates simple SELECTs and binds.
76              
77             Calling:
78              
79             my ($sql,$binds) = sql_select(
80             'users',
81             [qw( userid name )],
82             { status => 'X' ],
83             { order_by => 'name' },
84             );
85              
86             returns:
87              
88             $sql = 'SELECT userid,name FROM users WHERE status=? ORDER BY name';
89             $binds = [ 'X' ];
90              
91             =cut
92              
93             sub sql_select {
94 4     4 1 12537 my $table = shift;
95 4         6 my $columns = shift;
96 4         6 my $where = shift;
97 4   100     28 my $other = shift // {};
98              
99             my @parts = (
100 4         7 'SELECT ' . join( ',', @{$columns} ),
  4         19  
101             "FROM $table",
102             );
103              
104 4         8 my @binds;
105             my @where_conditions;
106              
107 4         12 _build_where( $where, \@where_conditions, \@binds );
108              
109 4 100       12 if ( @where_conditions ) {
110 3         8 push @parts, 'WHERE ' . join( ' AND ', @where_conditions );
111             }
112              
113 4 100       12 if ( my $order = $other->{order_by} ) {
114 2 100       6 if ( ref($order) eq 'ARRAY' ) {
115 1         4 push @parts, 'ORDER BY ' . join( ',', @{$order} );
  1         4  
116             }
117             else {
118 1         3 push @parts, "ORDER BY $order";
119             }
120             }
121              
122 4         10 my $sql = join( ' ', @parts );
123              
124 4         16 return ( $sql, \@binds );
125             }
126              
127              
128             =head2 sql_insert( $table, \%values )
129              
130             Creates simple INSERTs and binds.
131              
132             Calling:
133              
134             my ($sql,$binds) = sql_insert(
135             'users',
136             {
137             serialno => '12345',
138             name => 'Dave',
139             rank => 'Sergeant',
140             height => undef,
141             date_added => \'SYSDATE()',
142             }
143             );
144              
145             returns:
146              
147             $sql = 'INSERT INTO users (date_added,height,name,rank,serialno) VALUES (SYSDATE(),NULL,?,?,?)';
148             $binds = [ 'Dave', 'Sergeant', 12345 ]
149              
150             =cut
151              
152             sub sql_insert {
153 1     1 1 1690 my $table = shift;
154 1         3 my $values = shift;
155              
156 1         54 my @parts = (
157             "INSERT INTO $table"
158             );
159              
160 1         3 my @columns;
161             my @values;
162 1         0 my @binds;
163              
164 1         2 for my $key ( sort keys %{$values} ) {
  1         7  
165 5         10 my $value = $values->{$key};
166              
167 5         7 push @columns, $key;
168 5 100       22 if ( !defined($value) ) {
    100          
169 1         2 push @values, 'NULL';
170             }
171             elsif ( ref($value) eq 'SCALAR' ) {
172 1         2 push @values, ${$value};
  1         4  
173             }
174             else {
175 3         5 push @values, '?';
176 3         5 push @binds, $value;
177             }
178             }
179              
180 1         6 push @parts, '(' . join( ',', @columns ) . ')';
181 1         4 push @parts, 'VALUES (' . join( ',', @values ) . ')';
182 1         3 my $sql = join( ' ', @parts );
183              
184 1         5 return ( $sql, \@binds );
185             }
186              
187              
188             =head2 sql_update( $table, \%values, \%where )
189              
190             Creates simple UPDATE calls and binds.
191              
192             Calling:
193              
194             my ($sql,$binds) = sql_update(
195             'users',
196             {
197             status => 'X',
198             lockdate => undef,
199             },
200             {
201             orderdate => \'SYSDATE()',
202             },
203             );
204              
205             returns:
206              
207             $sql = 'UPDATE users SET lockdate=NULL, status=? WHERE orderdate=SYSDATE()'
208             $binds = [ 'X' ]
209              
210             =cut
211              
212             sub sql_update {
213 3     3 1 8744 my $table = shift;
214 3         6 my $values = shift;
215 3         4 my $where = shift;
216              
217 3         9 my @parts = (
218             "UPDATE $table"
219             );
220              
221 3         9 my @columns;
222             my @where_conditions;
223 3         0 my @binds;
224              
225 3         4 for my $key ( sort keys %{$values} ) {
  3         13  
226 4         7 my $value = $values->{$key};
227              
228 4 100       23 if ( !defined($value) ) {
    50          
229 1         4 push @columns, "$key=NULL";
230             }
231             elsif ( ref($value) eq 'SCALAR' ) {
232 0         0 push @columns, "$key=${$value}";
  0         0  
233             }
234             else {
235 3         7 push @columns, "$key=?";
236 3         7 push @binds, $value;
237             }
238             }
239 3         12 push @parts, 'SET ' . join( ', ', @columns );
240              
241 3         10 _build_where( $where, \@where_conditions, \@binds );
242              
243 3 100       8 if ( @where_conditions ) {
244 2         6 push @parts, 'WHERE ' . join( ' AND ', @where_conditions );
245             }
246              
247 3         8 my $sql = join( ' ', @parts );
248              
249 3         12 return ( $sql, \@binds );
250             }
251              
252              
253             =head2 sql_delete( $table, \%where )
254              
255             Creates simple DELETE calls and binds.
256              
257             Calling:
258              
259             my ($sql,$binds) = sql_delete(
260             'users',
261             {
262             serialno => 12345,
263             height => undef,
264             date_added => \'SYSDATE()',
265             status => [qw( X Y Z )],
266             },
267             );
268              
269             returns:
270              
271             $sql = 'DELETE FROM users WHERE date_added = SYSDATE() AND height IS NULL AND serialno = ? AND status IN (?,?,?)'
272             $binds = [ 12345, 'X', 'Y', 'Z' ]
273              
274             =cut
275              
276             sub sql_delete {
277 3     3 1 8884 my $table = shift;
278 3         5 my $where = shift;
279              
280 3         9 my @parts = (
281             "DELETE FROM $table"
282             );
283              
284 3         6 my @where_conditions;
285             my @binds;
286              
287 3         9 _build_where( $where, \@where_conditions, \@binds );
288              
289 3 100       8 if ( @where_conditions ) {
290 2         18 push @parts, 'WHERE ' . join( ' AND ', @where_conditions );
291             }
292              
293 3         9 my $sql = join( ' ', @parts );
294              
295 3         12 return ( $sql, \@binds );
296             }
297              
298              
299             sub _build_where {
300 10     10   15 my $where = shift;
301 10         13 my $conditions = shift;
302 10         16 my $binds = shift;
303              
304 10         15 for my $key ( sort keys %{$where} ) {
  10         50  
305 14         28 my $value = $where->{$key};
306 14 100       51 if ( !defined($value) ) {
    100          
    100          
307 2         4 push @{$conditions}, "$key IS NULL";
  2         5  
308             }
309             elsif ( ref($value) eq 'ARRAY' ) {
310 6         11 push @{$conditions}, "$key IN (" . join( ',', ('?') x @{$value} ) . ')';
  6         34  
  6         23  
311 6         14 push @{$binds}, @{$value};
  6         10  
  6         14  
312             }
313             elsif ( ref($value) eq 'SCALAR' ) {
314 3         22 push @{$conditions}, "$key=${$value}";
  3         15  
  3         11  
315             }
316             else {
317 3         3 push @{$conditions}, "$key=?";
  3         41  
318 3         7 push @{$binds}, $value;
  3         8  
319             }
320             }
321              
322 10         18 return;
323             }
324              
325              
326             =head1 AUTHOR
327              
328             Andy Lester, C<< >>
329              
330             =head1 BUGS
331              
332             Please report any bugs or feature requests to
333             L, or email me directly.
334              
335             =head1 SUPPORT
336              
337             You can find documentation for this module with the perldoc command.
338              
339             perldoc SQL::Tiny
340              
341             You can also look for information at:
342              
343             =over 4
344              
345             =item * MetaCPAN
346              
347             L
348              
349             =item * GitHub issue tracker
350              
351             L
352              
353             =back
354              
355             =head1 ACKNOWLEDGEMENTS
356              
357             =head1 LICENSE AND COPYRIGHT
358              
359             Copyright 2019 Andy Lester.
360              
361             This program is free software; you can redistribute it and/or modify it
362             under the terms of the the Artistic License (2.0). You may obtain a
363             copy of the full license at:
364              
365             L
366              
367             =cut
368              
369             1; # End of SQL::Tiny