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