File Coverage

blib/lib/SQL/Abstract/mysql.pm
Criterion Covered Total %
statement 81 84 96.4
branch 48 50 96.0
condition 8 9 88.8
subroutine 13 15 86.6
pod 1 1 100.0
total 151 159 94.9


line stmt bran cond sub pod time code
1             package SQL::Abstract::mysql;
2 18     18   142 use Mojo::Base 'SQL::Abstract';
  18         48  
  18         129  
3              
4 18     18   434672 BEGIN { *puke = \&SQL::Abstract::puke }
5              
6             sub insert {
7 6   100 6 1 16816 my ($self, $options) = (shift, $_[2] || {}); # ($self, $table, $data, $options)
8 6         27 my ($sql, @bind) = $self->SUPER::insert(@_);
9              
10             # options
11 6 100       6292 if (exists $options->{on_conflict}) {
12 5   100     21 my $on_conflict = $options->{on_conflict} // '';
13 5 100       28 if (ref $on_conflict eq 'HASH') {
    100          
    100          
14 1         12 my ($s, @b) = $self->_update_set_values($on_conflict);
15 1         646 $sql .= $self->_sqlcase(' on duplicate key update ') . $s;
16 1         7 push @bind, @b;
17             }
18             elsif ($on_conflict eq 'ignore') {
19 1         8 $sql =~ s/^(\w+)/{$self->_sqlcase('insert ignore')}/e;
  1         2  
  1         5  
20             }
21             elsif ($on_conflict eq 'replace') {
22 1         7 $sql =~ s/^(\w+)/{$self->_sqlcase('replace')}/e;
  1         3  
  1         3  
23             }
24             else {
25 2         12 puke qq{on_conflict value "$on_conflict" is not allowed};
26             }
27             }
28              
29 4 50       36 return wantarray ? ($sql, @bind) : $sql;
30             }
31              
32             sub _mysql_for {
33 6     6   13 my ($self, $param) = @_;
34              
35             return $self->_SWITCH_refkind(
36             $param => {
37             SCALAR => sub {
38 4 100   4   116 return $self->_sqlcase('lock in share mode') if $param eq 'share';
39 2 100       9 return $self->_sqlcase('for update') if $param eq 'update';
40 1         5 puke qq{for value "$param" is not allowed};
41             },
42 1     1   34 SCALARREF => sub { $self->_sqlcase('for ') . $$param },
43             }
44 6         50 );
45             }
46              
47             sub _mysql_group_by {
48 6     6   38 my ($self, $param) = @_;
49              
50             return $self->_SWITCH_refkind(
51 6     4   48 $param => {ARRAYREF => sub { join ', ', map $self->_quote($_), @$param }, SCALARREF => sub {$$param},});
  4         142  
  1         35  
52             }
53              
54             sub _order_by {
55 16     16   5449 my ($self, $options) = @_;
56 16         36 my ($sql, @bind) = ('');
57              
58             # Legacy
59 16 100 100     91 return $self->SUPER::_order_by($options) if ref $options ne 'HASH' or grep {/^-(?:desc|asc)/i} keys %$options;
  23         108  
60              
61             # GROUP BY
62 13 100       47 $sql .= $self->_sqlcase(' group by ') . $self->_mysql_group_by($options->{group_by}) if defined $options->{group_by};
63              
64             # HAVING
65 12 100       162 if (defined($options->{having})) {
66 3         17 my ($s, @b) = $self->_recurse_where($options->{having});
67 3         1329 $sql .= $self->_sqlcase(' having ') . $s;
68 3         17 push @bind, @b;
69             }
70              
71             # ORDER BY
72 12 100       57 $sql .= $self->_order_by($options->{order_by}) if defined $options->{order_by};
73              
74             # LIMIT / OFFSET
75 12         694 for my $name (qw(limit offset)) {
76 24 100       58 next unless defined $options->{$name};
77 4         15 $sql .= $self->_sqlcase(" $name ") . '?';
78 4         22 push @bind, $options->{$name};
79             }
80              
81             # FOR
82 12 100       78 $sql .= ' ' . $self->_mysql_for($options->{for}) if defined $options->{for};
83              
84 10         75 return $sql, @bind;
85             }
86              
87             sub _select_fields {
88 29     29   7532 my ($self, $fields) = @_;
89              
90 29 100       123 return $fields unless ref $fields eq 'ARRAY';
91              
92 3         7 my (@fields, @bind);
93 3         7 for my $field (@$fields) {
94             $self->_SWITCH_refkind(
95             $field => {
96             ARRAYREF => sub {
97 2 50   2   90 puke 'field alias must be in the form [$name => $alias]' if @$field < 2;
98 2         8 push @fields, $self->_quote($field->[0]) . $self->_sqlcase(' as ') . $self->_quote($field->[1]);
99             },
100             ARRAYREFREF => sub {
101 0     0   0 push @fields, shift @$$field;
102 0         0 push @bind, @$$field;
103             },
104 0     0   0 SCALARREF => sub { push @fields, $$field },
105 3     3   92 FALLBACK => sub { push @fields, $self->_quote($field) }
106             }
107 5         179 );
108             }
109              
110 3         98 return join(', ', @fields), @bind;
111             }
112              
113             sub _table {
114 32     32   98737 my ($self, $table) = @_;
115              
116 32 100       172 return $self->SUPER::_table($table) unless ref $table eq 'ARRAY';
117              
118 15         25 my (@tables, @joins);
119 15         33 for my $jt (@$table) {
120 33 100       71 if (ref $jt eq 'ARRAY') { push @joins, $jt }
  14         28  
121 19         39 else { push @tables, $jt }
122             }
123              
124 15         119 my $sql = $self->SUPER::_table(\@tables);
125 15   50     4515 my $sep = $self->{name_sep} // '';
126 15         35 for my $join (@joins) {
127              
128 14         32 my $type = '';
129 14 100       52 if ($join->[0] =~ /^-(.+)/) {
130 6         23 $type = " $1";
131 6         24 shift @$join;
132             }
133              
134 14         27 my $name = shift @$join;
135 14         48 $sql .= $self->_sqlcase("$type join ") . $self->_quote($name);
136              
137             # NATURAL JOIN
138 14 100       359 if ($type eq ' natural') {
    100          
139 2 100       11 puke 'natural join must be in the form [-natural => $table]' if @$join;
140             }
141              
142             # JOIN USING
143             elsif (@$join == 1) {
144 1         4 $sql .= $self->_sqlcase(' using (') . $self->_quote($join->[0]) . ')';
145             }
146              
147             # others
148             else {
149 11 100       33 puke 'join must be in the form [$table, $fk => $pk]' if @$join < 2;
150 10 100       28 puke 'join requires an even number of keys' if @$join % 2;
151              
152 9         10 my @keys;
153 9         33 while (my ($fk, $pk) = splice @$join, 0, 2) {
154 12 100       189 push @keys,
    100          
155             $self->_quote(index($fk, $sep) > 0 ? $fk : "$name.$fk") . ' = '
156             . $self->_quote(index($pk, $sep) > 0 ? $pk : "$tables[0].$pk");
157             }
158              
159 9         489 $sql .= $self->_sqlcase(' on ') . '(' . join($self->_sqlcase(' and '), @keys) . ')';
160             }
161              
162             }
163              
164 12         134 return $sql;
165             }
166              
167             1;
168              
169             =encoding utf8
170              
171             =head1 NAME
172              
173             SQL::Abstract::mysql - Generate SQL from Perl data structures for MySQL and MariaDB
174              
175             =head1 SYNOPSIS
176              
177             use SQL::Abstract::mysql;
178              
179             my $abstract = SQL::Abstract::mysql->new(quote_char => chr(96), name_sep => '.');
180             # The same as
181             use Mojo::mysql;
182             my $mysql = Mojo::mysql->new;
183             my $abstract = $mysql->abstract;
184              
185             say $abstract->insert('some_table', \%some_values, \%some_options);
186             say $abstract->select('some_table');
187              
188             =head1 DESCRIPTION
189              
190             L extends L with a few MySQL / MariaDB
191             features used by L. It was inspired by L.
192              
193             =head1 METHODS
194              
195             L inherits all methods from L.
196              
197             =head2 insert
198              
199             my ($stmt, @bind) = $abstract->insert($table, \@values || \%fieldvals, \%options);
200              
201             This method extends L with the following functionality:
202              
203             =head3 ON CONFLICT
204              
205             The C option can be used to generate C, C and
206             C queries.
207             So far C<'ignore'> to pass C, C<'replace'> to pass C and
208             hash references to pass C with conflict targets are supported.
209              
210             # "insert ignore into t (id, a) values (123, 'b')"
211             $abstract->insert('t', {id => 123, a => 'b'}, {on_conflict => 'ignore'});
212              
213             # "replace into t (id, a) values (123, 'b')"
214             $abstract->insert('t', {id => 123, a => 'b'}, {on_conflict => 'replace'});
215              
216             # "insert into t (id, a) values (123, 'b') on duplicate key update c='d'"
217             $abstract->insert('t', {id => 123, a => 'b'}, {on_conflict => {c => 'd'}});
218              
219             =head2 select
220              
221             my ($stmt, @bind) = $abstract->select($source, $fields, $where, $order);
222             my ($stmt, @bind) = $abstract->select($source, $fields, $where, \%options);
223              
224             This method extends L with the following functionality:
225              
226             =head3 AS
227              
228             The C<$fields> argument accepts array references containing array references
229             with field names and aliases, as well as array references containing scalar
230             references to pass literal SQL and array reference references to pass literal
231             SQL with bind values.
232              
233             # "select foo as bar from some_table"
234             $abstract->select('some_table', [[foo => 'bar']]);
235              
236             # "select foo, bar as baz, yada from some_table"
237             $abstract->select('some_table', ['foo', [bar => 'baz'], 'yada']);
238              
239             # "select extract(epoch from foo) as foo, bar from some_table"
240             $abstract->select('some_table', [\'extract(epoch from foo) as foo', 'bar']);
241              
242             # "select 'test' as foo, bar from some_table"
243             $abstract->select('some_table', [\['? as foo', 'test'], 'bar']);
244              
245             =head3 JOIN
246              
247             The C<$source> argument accepts array references containing not only table
248             names, but also array references with tables to generate C clauses for.
249              
250             # "select * from foo join bar on (bar.foo_id = foo.id)"
251             $abstract->select(['foo', ['bar', foo_id => 'id']]);
252              
253             # "select * from foo join bar on (foo.id = bar.foo_id)"
254             $abstract->select(['foo', ['bar', 'foo.id' => 'bar.foo_id']]);
255              
256             # -left, -right, -inner
257             # "select * from foo left join bar on (bar.foo_id = foo.id)"
258             $abstract->select(['foo', [-left => 'bar', foo_id => 'id']]);
259              
260             # -natural
261             # "select * from foo natural join bar"
262             $abstract->select(['foo', [-natural => 'bar']]);
263              
264             # join using
265             # "select * from foo join bar using (foo_id)"
266             $abstract->select(['foo', [bar => 'foo_id']]);
267              
268             # more than one table
269             # "select * from foo join bar on (bar.foo_id = foo.id) join baz on (baz.foo_id = foo.id)"
270             $abstract->select(['foo', ['bar', foo_id => 'id'], ['baz', foo_id => 'id']]);
271              
272             # more than one field
273             # "select * from foo left join bar on (bar.foo_id = foo.id and bar.foo_id2 = foo.id2)"
274             $abstract->select(['foo', [-left => 'bar', foo_id => 'id', foo_id2 => 'id2']]);
275              
276             =head2 where
277              
278             my ($stmt, @bind) = $abstract->where($where, \%options);
279              
280             This method extends L with the following functionality:
281              
282             =head3 FOR
283              
284             The C option can be used to generate C
285             or C clauses. So far the scalar values C and
286             C and scalar references to pass literal SQL are supported.
287              
288             # "select * from some_table for update"
289             $abstract->select('some_table', '*', undef, {for => 'update'});
290              
291             # "select * from some_table lock in share mode"
292             $abstract->select('some_table', '*', undef, {for => 'share'});
293              
294             # "select * from some_table for share"
295             $abstract->select('some_table', '*', undef, {for => \'share'});
296              
297             # "select * from some_table for update skip locked"
298             $abstract->select('some_table', '*', undef, {for => \'update skip locked'});
299              
300             =head3 GROUP BY
301              
302             The C option can be used to generate C
303             BY> clauses. So far array references to pass a list of fields and scalar
304             references to pass literal SQL are supported.
305              
306             # "select * from some_table group by foo, bar"
307             $abstract->select('some_table', '*', undef, {group_by => ['foo', 'bar']});
308              
309             # "select * from some_table group by foo, bar"
310             $abstract->select('some_table', '*', undef, {group_by => \'foo, bar'});
311              
312             =head3 HAVING
313              
314             The C option can be used to generate C
315             clauses, which takes the same values as the C<$where> argument.
316              
317             # "select * from t group by a having b = 'c'"
318             $abstract->select('t', '*', undef, {group_by => ['a'], having => {b => 'c'}});
319              
320             =head3 ORDER BY
321              
322             In addition to the C<$order> argument accepted by L you can pass
323             a hash reference with various options. This includes C, which takes
324             the same values as the C<$order> argument.
325              
326             # "select * from some_table order by foo desc"
327             $abstract->select('some_table', '*', undef, {order_by => {-desc => 'foo'}});
328              
329             =head3 LIMIT / OFFSET
330              
331             The C and C options can be used to generate C
332             with C and C clauses.
333              
334             # "select * from some_table limit 10"
335             $abstract->select('some_table', '*', undef, {limit => 10});
336              
337             # "select * from some_table offset 5"
338             $abstract->select('some_table', '*', undef, {offset => 5});
339              
340             # "select * from some_table limit 10 offset 5"
341             $abstract->select('some_table', '*', undef, {limit => 10, offset => 5});
342              
343             =head1 SEE ALSO
344              
345             L, L, L, L.
346              
347             =cut