File Coverage

blib/lib/DBI/Easy/SQL.pm
Criterion Covered Total %
statement 194 242 80.1
branch 71 108 65.7
condition 33 79 41.7
subroutine 14 18 77.7
pod 0 16 0.0
total 312 463 67.3


line stmt bran cond sub pod time code
1             package DBI::Easy;
2             # $Id: SQL.pm,v 1.6 2009/07/20 18:00:08 apla Exp $
3              
4 6     6   2746 use Class::Easy;
  6         1421  
  6         1425  
5              
6 6     6   801 use DBI;
  6         9  
  6         23715  
7              
8             # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
9             # sql generation stuff
10             # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
11              
12             our %BIND_TYPES = (
13             # VARCHAR2 => {ora_type => 1},
14             CLOB => {ora_type => 112},
15             BLOB => {ora_type => 113}
16             );
17              
18             sub sql_range {
19 2     2 0 1395 my $self = shift;
20 2   50     8 my $length = shift || 0;
21            
22 2 100       18 if (ref $length eq 'ARRAY') {
23 1         4 $length = $#$length + 1;
24             }
25            
26             return
27 2 50       8 unless $length > 0;
28              
29 2         11 return join ', ', ('?') x $length;
30             }
31              
32             sub sql_names_range { # now with AI
33 0     0 0 0 my $self = shift;
34 0         0 my $list = shift;
35 0         0 my $fields = shift;
36            
37 0         0 return join ', ', @$list;
38             }
39              
40             sub sql_order {
41 0     0 0 0 my $self = shift;
42 0         0 my $field = shift;
43 0         0 my $dir = shift;
44            
45 0         0 my $sort_col = $self->fields->{$field}->{quoted_column_name};
46            
47 0 0 0     0 if (!$field || !$sort_col || $dir !~ /^(?:asc|desc)$/i) {
      0        
48 0         0 return '';
49             }
50            
51 0         0 return "order by $sort_col $dir";
52             }
53              
54             sub sql_limit {
55 0     0 0 0 my $self = shift;
56            
57 0         0 my $s = $#_;
58 0 0 0     0 die if $s > 1 || $s == -1;
59            
60 0         0 return "limit " . join ', ', @_;
61             }
62              
63             sub sql_chunks_for_fields {
64 73     73 0 126 my $self = shift;
65 73         120 my $hash = shift;
66 73   50     239 my $mode = shift || 'where'; # also 'set' and 'insert'
67              
68 73         200 my @sql;
69             my @bind;
70            
71 73         238 foreach my $k (keys %$hash) {
72 105 100       301 next if $k =~ /^\:/;
73 103         346 my $v = $hash->{$k};
74            
75 103         138 my $is_sql = 0;
76 103 100       297 if ($k =~ /^_(\w+)$/) { # when we get param as _param, then we interpret it as sql
77 11         22 $is_sql = 1;
78 11         86 $k = $1;
79             }
80            
81 103         356 my $type = $self->columns->{$k}->{type_name};
82            
83 103         1081 my $qk = $self->dbh->quote_identifier ($k);
84 103 50       5114 $qk = $k
85             if $self->dbh_vendor eq 'oracle';
86            
87 103 100       963 if (ref $v eq 'ARRAY') {
    100          
88            
89 1 50       5 die "can't use sql statement as arrayref"
90             if $is_sql;
91            
92 1 50       21 die "can't set/insert multiple values: " . join (', ', @$v)
93             unless $mode eq 'where';
94            
95 1         3 my $range;
96 1 50       6 if (! scalar @$v) {
97 0         0 $range = 'null';
98             } else {
99 1         5 $range = $self->sql_range ($v);
100             }
101 1         5 push @sql, qq($qk in ($range));
102 1         4 push @bind, @$v;
103             } elsif ($is_sql) {
104 11         19 my @ph;
105 11         21 my $re = '(^|[\=\,\s\(])(:\w+)([\=\,\s\)]|$)';
106 11         382 while ($v =~ /$re/gs) {
107 2         19 push @ph, $2;
108             }
109            
110 11         228 $v =~ s/$re/ \? /gs;
111            
112 11 50       50 if ($mode eq 'insert') {
113 0         0 push @{$sql[0]}, $qk;
  0         0  
114 0         0 push @{$sql[1]}, $v;
  0         0  
115             } else {
116 11         44 push @sql, qq($qk $v);
117             }
118            
119 11         37 push @bind, map {$hash->{$_}} @ph;
  2         14  
120             } else {
121 91 100       201 if ($mode eq 'insert') {
122 41         51 push @{$sql[0]}, $qk;
  41         98  
123 41         67 push @{$sql[1]}, '?';
  41         133  
124             } else {
125 50         163 push @sql, qq($qk = ?);
126             }
127            
128 91 50 66     535 $v = [$v, $type, $k]
129             if defined $type and exists $BIND_TYPES{$type};
130            
131 91         284 push @bind, $v;
132             }
133             }
134            
135 73         318 return \@sql, \@bind;
136             }
137              
138             # get sql statement for insert
139              
140             sub sql_where {
141 132     132 0 1690 my $self = shift;
142 132         159 my $where_hash = shift;
143            
144 132 100       526 if (ref $where_hash eq 'ARRAY') {
145            
146 39         62 my (@where_acc, @bind_acc);
147            
148 39         124 foreach (@$where_hash) {
149 78         528 my ($where, $bind) = $self->sql_where ($_);
150 78 100 66     964 push @where_acc, $where
151             if defined $where and $where ne '';
152 78 100       94 push @bind_acc, @{$bind || []};
  78         13226  
153             }
154            
155 39         191 return join (' and ', @where_acc), \@bind_acc;
156             }
157            
158 93 100 66     477 return if ! defined $where_hash or $where_hash eq '';
159 74 100       365 return $where_hash if !ref $where_hash;
160 65 100 66     475 return if ref $where_hash ne 'HASH' || scalar keys %$where_hash == 0;
161            
162 50         283 my ($where, $bind) = $self->sql_chunks_for_fields ($where_hash, 'where');
163              
164 50         247 return join (' and ', @$where), $bind;
165             }
166              
167             # Получаем список выражений для SET
168             # как строку вида 'param1 = ?, param2 = ?'
169             # и массив значений для bind,
170             # построенные на основе заданного хэша
171             sub sql_set {
172 5     5 0 11 my $self = shift;
173 5         9 my $param_hash = shift;
174 5         116 my $where_hash = shift;
175            
176 5 50       21 unless (ref($param_hash) eq 'HASH') {
177 0         0 warn "please specify parameters hash";
178 0         0 return;
179             }
180            
181 5         20 my ($set, $bind) = $self->sql_chunks_for_fields ($param_hash, 'set');
182            
183 5         17 my $sql_set = join ', ', @$set;
184            
185 5 50 33     68 if (!defined $where_hash or ref($where_hash) !~ /HASH|ARRAY/) {
186 0         0 return ($sql_set, $bind);
187             } else {
188 5         25 my ($where_set, $bind_add) = $self->sql_where ($where_hash);
189 5         13 push @$bind, @$bind_add;
190 5         23 return ($sql_set, $bind, $where_set);
191             }
192             }
193              
194             # real sql statements
195              
196             sub sql_insert {
197 18     18 0 2951 my $self = shift;
198 18   33     68 my $hash = shift || $self->field_values;
199            
200 18         135 my ($set, $bind) = $self->sql_chunks_for_fields ($hash, 'insert');
201            
202 18         85 my $table_name = $self->table_quoted;
203 18         80 my $statement = "insert into $table_name (" .
204 18         70 join (', ', @{$set->[0]}) . ") values (" .
205 18         125 join (', ', @{$set->[1]}) . ")";
206            
207 18         264 return $statement, $bind;
208             }
209              
210             sub sql_update {
211 6     6 0 1849 my $self = shift;
212 6         9 my $set_values;
213             my $where_values;
214 6         13 my $suffix = '';
215 6         9 my $update_all = 0;
216            
217 6         10 my $params;
218            
219 6 50       25 if (ref $_[0]) {
220 0         0 $set_values = shift;
221 0         0 $where_values = shift;
222 0   0     0 $suffix = shift || '';
223             } else {
224 6         29 $params = {@_};
225 6         16 $set_values = $params->{set};
226 6         10 $where_values = $params->{where};
227 6   50     38 $suffix = $params->{suffix} || '';
228 6         14 $update_all = $params->{all};
229             }
230            
231 6 50       17 warn ("nothing to set in update"), return unless $set_values;
232 6 100       164 warn ("please use update_all for update everything"), return unless $where_values;
233            
234 5         29 my $table_name = $self->table_quoted;
235            
236 5         56 my ($set_statement, $bind, $where_statement)
237             = $self->sql_set ($set_values, $where_values);
238            
239 5         17 my $statement = "update $table_name set $set_statement";
240 5 50       22 $statement .= " where $where_statement"
241             if $where_statement;
242            
243 5         29 return $statement . ' ' . $suffix, $bind;
244             }
245              
246             sub sql_delete {
247 2     2 0 6 my $self = shift;
248 2         4 my $where_values;
249 2         6 my $suffix = '';
250 2         17 my $update_all = 0;
251            
252 2         6 my $params;
253            
254 2 50       11 if (ref $_[0]) {
255 0         0 $where_values = shift;
256 0   0     0 $suffix = shift || '';
257             } else {
258 2         10 $params = {@_};
259 2         7 $where_values = $params->{where};
260 2   50     17 $suffix = $params->{suffix} || '';
261 2         16 $update_all = $params->{all};
262             }
263            
264 2 50       12 warn ("please use delete_all for delete everything"), return
265             unless $where_values;
266            
267 2         13 my $table_name = $self->table_quoted;
268            
269 2         22 my ($where_statement, $bind)
270             = $self->sql_where ($where_values);
271            
272 2         9 my $statement = "delete from $table_name";
273 2 50       11 if (!$where_statement) {
274 0         0 warn "you can't delete all data from table, use delete_all or truncate";
275 0         0 return;
276             }
277            
278 2         8 $statement .= " where $where_statement";
279 2         14 debug $statement;
280 2         190 return $statement . ' ' . $suffix, $bind;
281             }
282              
283             sub sql_delete_by_pk {
284 1     1 0 5 my $self = shift;
285             # BAD!!! needs to be rewritten
286 1   50     11 my $where = shift || {};
287 1   50     12 my $suffix = shift || '';
288            
289 1         7 my $_pk_column_ = $self->_pk_column_;
290 1         14 my $_pk_ = $self->_pk_;
291 1         17 my $where_hash = {%$where, $_pk_column_ => $self->$_pk_};
292            
293 1         48 return $self->sql_delete (where => $where_hash, suffix => $suffix);
294            
295             }
296              
297              
298             sub sql_select_by_pk {
299 0     0 0 0 my $self = shift;
300 0         0 my %params = @_;
301            
302 0         0 my $where = {};
303            
304 0 0       0 if ($params{where}) {
305 0         0 $where = delete $params{where};
306             }
307            
308 0         0 my $_pk_column_ = $self->_pk_column_;
309            
310 0         0 return $self->sql_select (where => [
311             $where, $_pk_column_ => $self->{$self->_pk_}
312             ], %params);
313            
314             }
315              
316             sub sql_update_by_pk {
317 3     3 0 5 my $self = shift;
318 3         6 my %params = @_;
319            
320 3   33     19 my $set_hash = $params{set} || $self->fields_to_columns;
321            
322 3         12 my $_pk_column_ = $self->_pk_column_;
323 3         21 my $_pk_ = $self->_pk_;
324            
325 3 50 33     26 return unless $self->{column_values}->{$_pk_column_} || $self->{field_values}->{$_pk_};
326            
327 3 50       30 my $where_hash = {
328 3   33     5 %{$params{where} || {}},
329             $_pk_column_ => $self->{column_values}->{$_pk_column_} || $self->{field_values}->{$_pk_}
330             };
331            
332 3         34 my ($sql, $bind) = $self->sql_update (
333             set => $set_hash, where => $where_hash, suffix => $params{suffix}
334             );
335            
336 3         16 return $sql, $bind;
337            
338             }
339              
340             sub sql_column_list {
341 45     45 0 65 my $self = shift;
342 45         71 my $fieldset = shift;
343 45 100       94 unless (defined $fieldset) {
344 28         122 $fieldset = $self->fieldset;
345             }
346            
347 45 100 66     656 return '*'
348             if !defined $fieldset or !$fieldset;
349            
350 23 100       91 return $fieldset
351             unless ref $fieldset;
352            
353 3 50 33     27 die "can't recognize what you want, provide arrayref or string as fetch fields"
354             if ref $fieldset ne 'ARRAY' or ! scalar @$fieldset;
355            
356 3         7 my $col_list = [];
357            
358 3         13 my $fields = $self->fields;
359            
360 3         20 foreach my $field (@$fieldset) {
361 5 50       18 if (exists $fields->{$field}) {
362 5         23 push @$col_list, $fields->{$field}->{quoted_column_name};
363             } else {
364             # may be erratical
365 0         0 push @$col_list, $field;
366             }
367             }
368            
369 3         16 return join ', ', @$col_list;
370             }
371              
372             sub sql_select {
373 45     45 0 91 my $self = shift;
374            
375 45         330 my %params = @_;
376            
377 45   50     155 my $where = $params{where} || {};
378 45   50     217 my $suffix = $params{suffix} || '';
379 45         94 my $fieldset = $params{fieldset};
380            
381 45         81 my $fieldset_method = $params{fieldset_name};
382            
383 45 50 66     181 if (defined $fieldset and defined $fieldset_method) {
384 0         0 critical "you must use only fieldset with field names/expressions or fieldset_name for method who return fieldset";
385             }
386            
387 45 50       109 if (defined $fieldset_method) {
388 0         0 $fieldset = $self->$fieldset_method;
389             }
390            
391 45         210 my $cols_statement = $self->sql_column_list ($fieldset);
392              
393             # TODO: multiple condition/expression (group|order)_by
394              
395 45         85 my $group_by = '';
396              
397 45 100       258 if (defined $params{group_by}) {
398            
399             # try to translate field => column, and copy field if failed
400 1         5 my $group = $self->fields->{$params{group_by}}->{quoted_column_name};
401 1 50       9 $group = $params{group_by}
402             unless $group;
403            
404 1 50       6 $group_by = "group by $group"
405             if $group;
406             }
407              
408 45         71 my $limits = '';
409            
410             # parameter expansion here
411 45 100 66     167 if (defined $params{limit} and $params{limit} =~ /^(\d+)$/) {
412             # TODO: limit/offset by driver
413             # and SQL:2008 OFFSET start { ROW | ROWS }
414             # FETCH { FIRST | NEXT } [ count ] { ROW | ROWS } ONLY
415 1         5 $limits .= "limit $1";
416 1 50 33     6 if (defined $params{offset} and $params{offset} =~ /^(\d+)$/) {
417 0         0 $limits .= " offset $1";
418             }
419            
420 1 50       5 unless ($params{sort_field}) {
421 0         0 warn "you must use sort_(field|order) when you use limit/offset, we decide to set sort_field to primary key";
422 0         0 $params{sort_field} = $self->_pk_;
423             }
424             }
425            
426 45         79 my $order_by = '';
427              
428 45 100       110 if (defined $params{sort_field}) {
429            
430 1         5 my $order_expr = $self->fields->{$params{sort_field}}->{quoted_column_name};
431 1 50       10 $order_expr = $params{sort_field}
432             unless $order_expr;
433            
434 1         2 my $order = $params{sort_order};
435 1 50       5 $order = 'desc' unless $order;
436            
437 1         4 $order_by = "order by $order_expr $order";
438             }
439            
440 45         227 my $table_name = $self->table_quoted;
441            
442 45         396 my $statement = "select $cols_statement from $table_name";
443            
444 45 100       840 if ($self->can ('join_table')) {
445 38         200 my $join = $self->join_table;
446 38 50 33     289 if (defined $join and $join !~ /^\s*$/) {
447 0         0 $statement .= ' ' . $join;
448             }
449             }
450            
451 45         70 my ($where_statement, $bind);
452 45         197 ($where_statement, $bind) = $self->sql_where ($where);
453            
454 45 100 66     601 $statement .= " where $where_statement"
455             if defined $where_statement and $where_statement !~ /^\s*$/;
456            
457             return
458 45         447 join (' ', $statement, $group_by, $suffix, $order_by, $limits),
459             $bind;
460             }
461              
462             sub sql_select_count {
463 16     16 0 33 my $self = shift;
464              
465 16         30 my $where = {};
466 16         32 my $suffix = '';
467            
468 16         21 my %params;
469            
470 16 50       37 if (ref $_[0]) {
471 0         0 $where = shift;
472 0 0       0 if (@_ % 2) {
473 0         0 $suffix = shift;
474             }
475 0         0 %params = (@_);
476             } else {
477 16         417 %params = (@_);
478 16   50     57 $where = $params{where} || {};
479             }
480            
481 16         127 return $self->sql_select (where => $where, suffix => $suffix, %params, fieldset => 'count(*)');
482            
483             }
484              
485              
486             1;
487              
488             =head1 NAME
489              
490             DBI::Easy::SQL - handling sql for DBI::Easy
491              
492             =head1 ABSTRACT
493              
494             This module is a SQL expressions constructor for DBI::Easy.
495             So DBI::Easy::SQL is a wrapper between SQL and the rest part of DBI::Easy
496              
497              
498             =head1 SYNOPSIS
499              
500             SYNOPSIS
501              
502              
503             =head1 FUNCTIONS
504              
505             =head2 sql_column_list
506              
507             returns a list of SQL columns
508              
509             =cut
510              
511             =head2 sql_delete, sql_delete_by_pk
512              
513             creates a SQL DELETE query
514              
515             =cut
516              
517             =head2 sql_insert
518              
519             creates a SQL INSERT query
520              
521             =cut
522              
523             =head2 sql_limit
524              
525             adds limits to SQL query
526              
527             =cut
528              
529             =head2 sql_names_range
530              
531             TODO
532              
533             =cut
534              
535             =head2 sql_order
536              
537             add ORDER BY to SQL query
538              
539             =cut
540              
541             =head2 sql_range
542              
543             create placeholders for ranged sql statements, as example by
544              
545             ... where column in (?, ?) ...
546             insert into table (col1, col2) values (?, ?) ...
547              
548             receive number of placeholders to generate or arrayref, returns
549              
550             join ', ', ('?' x $num)
551              
552             =cut
553              
554             =head2 sql_select, sql_select_by_pk, sql_select_count
555              
556             creates SELECT SQL query
557              
558             =cut
559              
560             =head2 sql_set
561              
562             creates SET SQL expression (for UPDATE query as an example)
563              
564             =cut
565              
566             =head2 sql_update, sql_update_by_pk
567              
568             creates UPDATE SQL query
569              
570             =cut
571              
572             =head2 sql_where
573              
574             creates WHERE SQL expression
575              
576             =cut
577              
578              
579             =head1 AUTHOR
580              
581             Ivan Baktsheev, C<< >>
582              
583             =head1 BUGS
584              
585             Please report any bugs or feature requests to my email address,
586             or through the web interface at L.
587             I will be notified, and then you'll automatically be notified
588             of progress on your bug as I make changes.
589              
590             =head1 SUPPORT
591              
592              
593              
594             =head1 ACKNOWLEDGEMENTS
595              
596              
597              
598             =head1 COPYRIGHT & LICENSE
599              
600             Copyright 2008-2009 Ivan Baktsheev
601              
602             This program is free software; you can redistribute it and/or modify it
603             under the same terms as Perl itself.
604              
605             =cut