File Coverage

blib/lib/SQL/Tiny.pm
Criterion Covered Total %
statement 98 100 98.0
branch 19 20 95.0
condition 2 2 100.0
subroutine 10 10 100.0
pod 4 4 100.0
total 133 136 97.7


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