File Coverage

blib/lib/SQL/Maker.pm
Criterion Covered Total %
statement 162 198 81.8
branch 77 98 78.5
condition 11 20 55.0
subroutine 25 28 89.2
pod 8 11 72.7
total 283 355 79.7


line stmt bran cond sub pod time code
1             package SQL::Maker;
2 14     14   471930 use strict;
  14         48  
  14         994  
3 14     14   94 use warnings;
  14         29  
  14         426  
4 14     14   693 use 5.008001;
  14         55  
  14         1060  
5             our $VERSION = '1.20';
6             use Class::Accessor::Lite 0.05 (
7 14         128 ro => [qw/quote_char name_sep new_line strict driver select_class/],
8 14     14   20839 );
  14         20578  
9              
10 14     14   2303 use Carp ();
  14         32  
  14         228  
11 14     14   11276 use SQL::Maker::Select;
  14         124  
  14         557  
12 14     14   15588 use SQL::Maker::Select::Oracle;
  14         42  
  14         624  
13 14     14   3235 use SQL::Maker::Condition;
  14         27  
  14         398  
14 14     14   459 use SQL::Maker::Util;
  14         27  
  14         359  
15 14     14   37689 use Module::Load ();
  14         30167  
  14         353  
16 14     14   360 use Scalar::Util ();
  14         27  
  14         1657  
17              
18             sub load_plugin {
19 1     1 0 13 my ($class, $role) = @_;
20 1 50       7 $role = $role =~ s/^\+// ? $role : "SQL::Maker::Plugin::$role";
21 1         7 Module::Load::load($role);
22              
23 14     14   74 no strict 'refs';
  14         43  
  14         55377  
24 1         12 for (@{"${role}::EXPORT"}) {
  1         9  
25 1         3 *{"${class}::$_"} = *{"${role}::$_"};
  1         11  
  1         5  
26             }
27             }
28              
29             sub new {
30 30     30 1 116815 my $class = shift;
31 30 50       192 my %args = @_ == 1 ? %{$_[0]} : @_;
  0         0  
32 30 50       361 unless ($args{driver}) {
33 0         0 Carp::croak("'driver' is required for creating new instance of $class");
34             }
35 30         65 my $driver = $args{driver};
36 30 100       103 unless ( defined $args{quote_char} ) {
37 19         28 $args{quote_char} = do{
38 19 100       62 if ($driver eq 'mysql') {
39 10         34 q{`}
40             } else {
41 9         25 q{"}
42             }
43             };
44             }
45 30 50       103 $args{select_class} = $driver eq 'Oracle' ? 'SQL::Maker::Select::Oracle' : 'SQL::Maker::Select';
46              
47 30         290 return bless {
48             name_sep => '.',
49             new_line => "\n",
50             strict => 0,
51             %args
52             }, $class;
53             }
54              
55             sub new_condition {
56 92     92 1 26521 my $self = shift;
57              
58 92         757 SQL::Maker::Condition->new(
59             quote_char => $self->{quote_char},
60             name_sep => $self->{name_sep},
61             strict => $self->{strict},
62             );
63             }
64              
65             sub new_select {
66 87     87 1 3471 my $self = shift;
67 87 50       392 my %args = @_==1 ? %{$_[0]} : @_;
  0         0  
68              
69 87         355 return $self->select_class->new(
70             name_sep => $self->name_sep,
71             quote_char => $self->quote_char,
72             new_line => $self->new_line,
73             strict => $self->strict,
74             %args,
75             );
76             }
77              
78             # $builder->insert($table, \%values, \%opt);
79             # $builder->insert($table, \@values, \%opt);
80             sub insert {
81 4     4 1 3219 my ($self, $table, $values, $opt) = @_;
82 4   50     63 my $prefix = $opt->{prefix} || 'INSERT INTO';
83              
84 4         17 my $quoted_table = $self->_quote($table);
85              
86 4         10 my (@columns, @bind_columns, @quoted_columns, @values);
87 4 100       24 @values = ref $values eq 'HASH' ? %$values : @$values;
88 4         57 while (my ($col, $val) = splice(@values, 0, 2)) {
89 4         11 push @quoted_columns, $self->_quote($col);
90 4 50       20 if (Scalar::Util::blessed($val)) {
91 0 0       0 if ($val->can('as_sql')) {
92 0     0   0 push @columns, $val->as_sql(undef, sub { $self->_quote($_[0]) });
  0         0  
93 0         0 push @bind_columns, $val->bind();
94             } else {
95 0         0 push @columns, '?';
96 0         0 push @bind_columns, $val;
97             }
98             } else {
99 4 100 66     32 Carp::croak("cannot pass in an unblessed ref as an argument in strict mode")
100             if ref($val) && $self->strict;
101 3 50 33     20 if (ref($val) eq 'SCALAR') {
    50          
102             # $builder->insert(foo => { created_on => \"NOW()" });
103 0         0 push @columns, $$val;
104             }
105             elsif (ref($val) eq 'REF' && ref($$val) eq 'ARRAY') {
106             # $builder->insert( foo => \[ 'UNIX_TIMESTAMP(?)', '2011-04-12 00:34:12' ] );
107 0         0 my ( $stmt, @sub_bind ) = @{$$val};
  0         0  
108 0         0 push @columns, $stmt;
109 0         0 push @bind_columns, @sub_bind;
110             }
111             else {
112             # normal values
113 3         63 push @columns, '?';
114 3         16 push @bind_columns, $val;
115             }
116             }
117             }
118              
119             # Insert an empty record in SQLite.
120             # ref. https://github.com/tokuhirom/SQL-Maker/issues/11
121 3 100 66     12 if ($self->driver eq 'SQLite' && @columns==0) {
122 1         14 my $sql = "$prefix $quoted_table" . $self->new_line . 'DEFAULT VALUES';
123 1         7 return ($sql);
124             }
125              
126 2         25 my $sql = "$prefix $quoted_table" . $self->new_line;
127 2         21 $sql .= '(' . join(', ', @quoted_columns) .')' . $self->new_line .
128             'VALUES (' . join(', ', @columns) . ')';
129              
130 2         23 return ($sql, @bind_columns);
131             }
132              
133             sub _quote {
134 36     36   57 my ($self, $label) = @_;
135              
136 36         154 SQL::Maker::Util::quote_identifier($label, $self->quote_char(), $self->name_sep());
137             }
138              
139             sub delete {
140 19     19 1 10000 my ($self, $table, $where, $opt) = @_;
141              
142 19         67 my $w = $self->_make_where_clause($where);
143 18         78 my $quoted_table = $self->_quote($table);
144 18         43 my $sql = "DELETE FROM $quoted_table";
145 18 100       56 if ($opt->{using}) {
146             # $bulder->delete('foo', \%where, { using => 'bar' });
147             # $bulder->delete('foo', \%where, { using => ['bar', 'qux'] });
148 4 100       17 my $tables = ref($opt->{using}) eq 'ARRAY' ? $opt->{using} : [$opt->{using}];
149 4         9 my $using = join(', ', map { $self->_quote($_) } @$tables);
  6         15  
150 4         11 $sql .= " USING " . $using;
151             }
152 18         38 $sql .= $w->[0];
153 18         29 return ($sql, @{$w->[1]});
  18         570  
154             }
155              
156             sub update {
157 2     2 1 5962 my ($self, $table, $args, $where) = @_;
158              
159 2         9 my ($columns, $bind_columns) = $self->make_set_clause($args);
160              
161 1         4 my $w = $self->_make_where_clause($where);
162 0         0 push @$bind_columns, @{$w->[1]};
  0         0  
163              
164 0         0 my $quoted_table = $self->_quote($table);
165 0         0 my $sql = "UPDATE $quoted_table SET " . join(', ', @$columns) . $w->[0];
166 0         0 return ($sql, @$bind_columns);
167             }
168              
169             # make "SET" clause.
170             sub make_set_clause {
171 3     3 0 8 my ($self, $args) = @_;
172              
173 3         7 my (@columns, @bind_columns);
174 3 100       18 my @args = ref $args eq 'HASH' ? %$args : @$args;
175 3         44 while (my ($col, $val) = splice @args, 0, 2) {
176 4         13 my $quoted_col = $self->_quote($col);
177 4 50       18 if (Scalar::Util::blessed($val)) {
178 0 0       0 if ($val->can('as_sql')) {
179 0     0   0 push @columns, "$quoted_col = " . $val->as_sql(undef, sub { $self->_quote($_[0]) });
  0         0  
180 0         0 push @bind_columns, $val->bind();
181             } else {
182 0         0 push @columns, "$quoted_col = ?";
183 0         0 push @bind_columns, $val;
184             }
185             } else {
186 4 100 66     22 Carp::croak("cannot pass in an unblessed ref as an argument in strict mode")
187             if ref($val) && $self->strict;
188 3 50 33     21 if (ref $val eq 'SCALAR') {
    50          
189             # $builder->update(foo => { created_on => \"NOW()" });
190 0         0 push @columns, "$quoted_col = " . $$val;
191             }
192             elsif (ref $val eq 'REF' && ref $$val eq 'ARRAY' ) {
193             # $builder->update( foo => \[ 'VALUES(foo) + ?', 10 ] );
194 0         0 my ( $stmt, @sub_bind ) = @{$$val};
  0         0  
195 0         0 push @columns, "$quoted_col = " . $stmt;
196 0         0 push @bind_columns, @sub_bind;
197             }
198             else {
199             # normal values
200 3         8 push @columns, "$quoted_col = ?";
201 3         15 push @bind_columns, $val;
202             }
203             }
204             }
205 2         8 return (\@columns, \@bind_columns);
206             }
207              
208             sub where {
209 0     0 1 0 my ($self, $where) = @_;
210 0         0 my $cond = $self->_make_where_condition($where);
211 0         0 return ($cond->as_sql(), $cond->bind());
212             }
213              
214             sub _make_where_condition {
215 93     93   143 my ($self, $where) = @_;
216              
217 93 50       225 return $self->new_condition unless $where;
218 93 100 66     1042 if ( Scalar::Util::blessed( $where ) and $where->can('as_sql') ) {
219 12         46 return $where;
220             }
221              
222 81         216 my $w = $self->new_condition;
223 81 100       619 my @w = ref $where eq 'ARRAY' ? @$where : %$where;
224 81         972 while (my ($col, $val) = splice @w, 0, 2) {
225 62         225 $w->add($col => $val);
226             }
227 78         296 return $w;
228             }
229              
230             sub _make_where_clause {
231 20     20   35 my ($self, $where) = @_;
232              
233 20 100       100 return ['', []] unless $where;
234              
235 17         55 my $w = $self->_make_where_condition($where);
236 15         60 my $sql = $w->as_sql();
237 15 50       124 return [$sql ? " WHERE $sql" : '', [$w->bind]];
238             }
239              
240             # my($stmt, @bind) = $sql−>select($table, \@fields, \%where, \%opt);
241             sub select {
242 78     78 1 240011 my $stmt = shift->select_query(@_);
243 77         282 return ($stmt->as_sql,@{$stmt->bind});
  74         256  
244             }
245              
246             sub select_query {
247 83     83 0 1373 my ($self, $table, $fields, $where, $opt) = @_;
248              
249 83 50       325 unless (ref $fields eq 'ARRAY') {
250 0         0 Carp::croak("SQL::Maker::select_query: \$fields should be ArrayRef[Str]");
251             }
252              
253 83         391 my $stmt = $self->new_select;
254 83         700 for my $field (@$fields) {
255 115 100       536 $stmt->add_select(ref $field eq 'ARRAY' ? @$field : $field);
256             }
257              
258 83 100       364 if ( defined $table ) {
259 80 100       437 unless ( ref $table ) {
260             # $table = 'foo'
261 72         243 $stmt->add_from( $table );
262             }
263             else {
264             # $table = [ 'foo', [ bar => 'b' ] ]
265 8         48 for ( @$table ) {
266 14 100       163 $stmt->add_from( ref $_ eq 'ARRAY' ? @$_ : $_ );
267             }
268             }
269             }
270              
271 83 100       366 $stmt->prefix($opt->{prefix}) if $opt->{prefix};
272              
273 83 100       256 if ( $where ) {
274 76         344 $stmt->set_where($self->_make_where_condition($where));
275             }
276              
277 82 100       305 if ( my $joins = $opt->{joins} ) {
278 3         8 for my $join ( @$joins ) {
279 3 50       18 $stmt->add_join(ref $join eq 'ARRAY' ? @$join : $join);
280             }
281             }
282              
283 82 100       385 if (my $o = $opt->{order_by}) {
284 27 100       160 if (ref $o eq 'ARRAY') {
    100          
285 6         14 for my $order (@$o) {
286 12 100       42 if (ref $order eq 'HASH') {
287             # Skinny-ish [{foo => 'DESC'}, {bar => 'ASC'}]
288 3         20 $stmt->add_order_by(%$order);
289             } else {
290             # just ['foo DESC', 'bar ASC']
291 9         35 $stmt->add_order_by(\$order);
292             }
293             }
294             } elsif (ref $o eq 'HASH') {
295             # Skinny-ish {foo => 'DESC'}
296 3         15 $stmt->add_order_by(%$o);
297             } else {
298             # just 'foo DESC, bar ASC'
299 18         80 $stmt->add_order_by(\$o);
300             }
301             }
302 82 100       226 if (my $o = $opt->{group_by}) {
303 12 100       42 if (ref $o eq 'ARRAY') {
    100          
304 6         10 for my $group (@$o) {
305 12 100       27 if (ref $group eq 'HASH') {
306             # Skinny-ish [{foo => 'DESC'}, {bar => 'ASC'}]
307 3         12 $stmt->add_group_by(%$group);
308             } else {
309             # just ['foo DESC', 'bar ASC']
310 9         22 $stmt->add_group_by(\$group);
311             }
312             }
313             } elsif (ref $o eq 'HASH') {
314             # Skinny-ish {foo => 'DESC'}
315 3         11 $stmt->add_group_by(%$o);
316             } else {
317             # just 'foo DESC, bar ASC'
318 3         14 $stmt->add_group_by(\$o);
319             }
320             }
321 82 100       232 if (my $o = $opt->{index_hint}) {
322 3         13 $stmt->add_index_hint($table, $o);
323             }
324              
325 82 100       238 $stmt->limit( $opt->{limit} ) if defined $opt->{limit};
326 82 100       218 $stmt->offset( $opt->{offset} ) if $opt->{offset};
327              
328 82 50       318 if (my $terms = $opt->{having}) {
329 0         0 while (my ($col, $val) = each %$terms) {
330 0         0 $stmt->add_having($col => $val);
331             }
332             }
333              
334 82 50       179 $stmt->for_update(1) if $opt->{for_update};
335 82         352 return $stmt;
336             }
337              
338             1;
339             __END__