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   119 use Mojo::Base 'SQL::Abstract';
  18         35  
  18         102  
3              
4 18     18   362048 BEGIN { *puke = \&SQL::Abstract::puke }
5              
6             sub insert {
7 6   100 6 1 18522 my ($self, $options) = (shift, $_[2] || {}); # ($self, $table, $data, $options)
8 6         40 my ($sql, @bind) = $self->SUPER::insert(@_);
9              
10             # options
11 6 100       6308 if (exists $options->{on_conflict}) {
12 5   100     23 my $on_conflict = $options->{on_conflict} // '';
13 5 100       31 if (ref $on_conflict eq 'HASH') {
    100          
    100          
14 1         9 my ($s, @b) = $self->_update_set_values($on_conflict);
15 1         534 $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         3  
20             }
21             elsif ($on_conflict eq 'replace') {
22 1         7 $sql =~ s/^(\w+)/{$self->_sqlcase('replace')}/e;
  1         3  
  1         4  
23             }
24             else {
25 2         9 puke qq{on_conflict value "$on_conflict" is not allowed};
26             }
27             }
28              
29 4 50       88 return wantarray ? ($sql, @bind) : $sql;
30             }
31              
32             sub _mysql_for {
33 6     6   19 my ($self, $param) = @_;
34              
35             return $self->_SWITCH_refkind(
36             $param => {
37             SCALAR => sub {
38 4 100   4   119 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         7 puke qq{for value "$param" is not allowed};
41             },
42 1     1   40 SCALARREF => sub { $self->_sqlcase('for ') . $$param },
43             }
44 6         56 );
45             }
46              
47             sub _mysql_group_by {
48 6     6   48 my ($self, $param) = @_;
49              
50             return $self->_SWITCH_refkind(
51 6     1   68 $param => {ARRAYREF => sub { join ', ', map $self->_quote($_), @$param }, SCALARREF => sub {$$param},});
  4         161  
  1         41  
52             }
53              
54             sub _order_by {
55 16     16   5110 my ($self, $options) = @_;
56 16         38 my ($sql, @bind) = ('');
57              
58             # Legacy
59 16 100 100     92 return $self->SUPER::_order_by($options) if ref $options ne 'HASH' or grep {/^-(?:desc|asc)/i} keys %$options;
  23         123  
60              
61             # GROUP BY
62 13 100       52 $sql .= $self->_sqlcase(' group by ') . $self->_mysql_group_by($options->{group_by}) if defined $options->{group_by};
63              
64             # HAVING
65 12 100       157 if (defined($options->{having})) {
66 3         17 my ($s, @b) = $self->_recurse_where($options->{having});
67 3         1168 $sql .= $self->_sqlcase(' having ') . $s;
68 3         17 push @bind, @b;
69             }
70              
71             # ORDER BY
72 12 100       47 $sql .= $self->_order_by($options->{order_by}) if defined $options->{order_by};
73              
74             # LIMIT / OFFSET
75 12         619 for my $name (qw(limit offset)) {
76 24 100       61 next unless defined $options->{$name};
77 4         13 $sql .= $self->_sqlcase(" $name ") . '?';
78 4         17 push @bind, $options->{$name};
79             }
80              
81             # FOR
82 12 100       84 $sql .= ' ' . $self->_mysql_for($options->{for}) if defined $options->{for};
83              
84 10         71 return $sql, @bind;
85             }
86              
87             sub _select_fields {
88 29     29   7762 my ($self, $fields) = @_;
89              
90 29 100       147 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   75 puke 'field alias must be in the form [$name => $alias]' if @$field < 2;
98 2         6 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   88 FALLBACK => sub { push @fields, $self->_quote($field) }
106             }
107 5         157 );
108             }
109              
110 3         92 return join(', ', @fields), @bind;
111             }
112              
113             sub _table {
114 32     32   100904 my ($self, $table) = @_;
115              
116 32 100       231 return $self->SUPER::_table($table) unless ref $table eq 'ARRAY';
117              
118 15         31 my (@tables, @joins);
119 15         41 for my $jt (@$table) {
120 33 100       85 if (ref $jt eq 'ARRAY') { push @joins, $jt }
  14         33  
121 19         43 else { push @tables, $jt }
122             }
123              
124 15         66 my $sql = $self->SUPER::_table(\@tables);
125 15   50     4614 my $sep = $self->{name_sep} // '';
126 15         38 for my $join (@joins) {
127              
128 14         39 my $type = '';
129 14 100       65 if ($join->[0] =~ /^-(.+)/) {
130 6         21 $type = " $1";
131 6         13 shift @$join;
132             }
133              
134 14         32 my $name = shift @$join;
135 14         54 $sql .= $self->_sqlcase("$type join ") . $self->_quote($name);
136              
137             # NATURAL JOIN
138 14 100       345 if ($type eq ' natural') {
    100          
139 2 100       25 puke 'natural join must be in the form [-natural => $table]' if @$join;
140             }
141              
142             # JOIN USING
143             elsif (@$join == 1) {
144 1         5 $sql .= $self->_sqlcase(' using (') . $self->_quote($join->[0]) . ')';
145             }
146              
147             # others
148             else {
149 11 100       38 puke 'join must be in the form [$table, $fk => $pk]' if @$join < 2;
150 10 100       36 puke 'join requires an even number of keys' if @$join % 2;
151              
152 9         12 my @keys;
153 9         43 while (my ($fk, $pk) = splice @$join, 0, 2) {
154 12 100       169 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         382 $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