File Coverage

blib/lib/Mojo/MySQL5.pm
Criterion Covered Total %
statement 25 57 43.8
branch 5 24 20.8
condition 1 8 12.5
subroutine 8 14 57.1
pod 6 6 100.0
total 45 109 41.2


line stmt bran cond sub pod time code
1             package Mojo::MySQL5;
2 6     6   414067 use Mojo::Base 'Mojo::EventEmitter';
  6         7  
  6         31  
3              
4 6     6   5423 use Carp 'croak';
  6         9  
  6         269  
5 6     6   2150 use Mojo::MySQL5::Migrations;
  6         11  
  6         44  
6 6     6   2313 use Mojo::MySQL5::URL;
  6         19  
  6         53  
7 6     6   2515 use Mojo::MySQL5::Database;
  6         14  
  6         67  
8 6     6   160 use Scalar::Util 'weaken';
  6         5  
  6         3461  
9              
10             has max_connections => 5;
11             has migrations => sub {
12             my $migrations = Mojo::MySQL5::Migrations->new(mysql => shift);
13             weaken $migrations->{mysql};
14             return $migrations;
15             };
16             has url => sub { Mojo::MySQL5::URL->new('mysql:///test') };
17              
18             our $VERSION = '0.07';
19              
20             sub db {
21 0     0 1 0 my $self = shift;
22              
23             # Fork safety
24 0 0 0     0 delete @$self{qw(pid queue)} unless ($self->{pid} //= $$) eq $$;
25              
26 0         0 my $c = $self->_dequeue;
27 0         0 my $db = Mojo::MySQL5::Database->new(connection => $c, mysql => $self);
28              
29 0 0       0 if (!$c) {
30 0         0 $db->connect;
31 0 0       0 croak 'connect failed' unless $db->connection->state eq 'idle';
32 0         0 $self->emit(connection => $db);
33             }
34 0         0 return $db;
35             }
36              
37             sub from_string {
38 6     6 1 30 my ($self, $str) = @_;
39 6         15 my $url = Mojo::MySQL5::URL->new($str);
40 6 50 33     14 croak qq{Invalid MySQL connection string "$str"}
41             unless $url->protocol eq 'mysql' or $url->protocol eq 'mysql5';
42              
43 6 50       41 $url->options->{utf8} = 1 unless exists $url->options->{utf8};
44 6 50       9 $url->options->{found_rows} = 1 unless exists $url->options->{found_rows};
45              
46 6         11 return $self->url($url);
47             }
48              
49 7 100   7 1 48 sub new { @_ == 2 ? shift->SUPER::new->from_string(@_) : shift->SUPER::new(@_) }
50              
51             sub _dequeue {
52 0     0     my $self = shift;
53              
54 0 0         while (my $c = shift @{$self->{queue} || []}) { return $c if $c->ping }
  0 0          
  0            
55 0           return undef;
56             }
57              
58             sub _enqueue {
59 0     0     my ($self, $c) = @_;
60 0   0       my $queue = $self->{queue} ||= [];
61 0           push @$queue, $c;
62 0           shift @{$self->{queue}} while @{$self->{queue}} > $self->max_connections;
  0            
  0            
63             }
64              
65             # deprecated attributes
66              
67             sub password {
68 0     0 1   my $self = shift;
69 0 0         return $self->url->password unless @_;
70 0           $self->url->password(@_);
71 0           return $self;
72             }
73              
74             sub username {
75 0     0 1   my $self = shift;
76 0 0         return $self->url->username unless @_;
77 0           $self->url->username(@_);
78 0           return $self;
79             }
80              
81             sub options {
82 0     0 1   my $self = shift;
83 0 0         return $self->url->options unless @_;
84 0           $self->url->options(@_);
85 0           return $self;
86             }
87              
88             1;
89              
90             =encoding utf8
91              
92             =head1 NAME
93              
94             Mojo::MySQL5 - Pure-Perl non-blocking I/O MySQL Connector
95              
96             =head1 SYNOPSIS
97              
98             use Mojo::MySQL5;
99              
100             # Create a table
101             my $mysql = Mojo::MySQL5->new('mysql://username@/test');
102             $mysql->db->query(
103             'create table names (id integer auto_increment primary key, name text)');
104              
105             # Insert a few rows
106             my $db = $mysql->db;
107             $db->query('insert into names (name) values (?)', 'Sara');
108             $db->query('insert into names (name) values (?)', 'Stefan');
109              
110             # Insert more rows in a transaction
111             {
112             my $tx = $db->begin;
113             $db->query('insert into names (name) values (?)', 'Baerbel');
114             $db->query('insert into names (name) values (?)', 'Wolfgang');
115             $tx->commit;
116             };
117              
118             # Insert another row and return the generated id
119             say $db->query('insert into names (name) values (?)', 'Daniel')
120             ->last_insert_id;
121              
122             # Select one row at a time
123             my $results = $db->query('select * from names');
124             while (my $next = $results->hash) {
125             say $next->{name};
126             }
127              
128             # Select all rows blocking
129             $db->query('select * from names')
130             ->hashes->map(sub { $_->{name} })->join("\n")->say;
131              
132             # Select all rows non-blocking
133             Mojo::IOLoop->delay(
134             sub {
135             my $delay = shift;
136             $db->query('select * from names' => $delay->begin);
137             },
138             sub {
139             my ($delay, $err, $results) = @_;
140             $results->hashes->map(sub { $_->{name} })->join("\n")->say;
141             }
142             )->wait;
143              
144             =head1 DESCRIPTION
145              
146             L makes L a lot of fun to use with the
147             L real-time web framework.
148              
149             Database handles are cached automatically, so they can be reused transparently
150             to increase performance. And you can handle connection timeouts gracefully by
151             holding on to them only for short amounts of time.
152              
153             use Mojolicious::Lite;
154             use Mojo::MySQL5;
155              
156             helper mysql =>
157             sub { state $mysql = Mojo::MySQL5->new('mysql://sri:s3cret@localhost/db') };
158              
159             get '/' => sub {
160             my $c = shift;
161             my $db = $c->mysql->db;
162             $c->render(json => $db->query('select now() as time')->hash);
163             };
164              
165             app->start;
166              
167             Every database connection can only handle one active query at a time, this
168             includes asynchronous ones. So if you start more than one, they will be put on
169             a waiting list and performed sequentially. To perform multiple queries
170             concurrently, you have to use multiple connections.
171            
172             # Performed sequentially (10 seconds)
173             my $db = $mysql->db;
174             $db->query('select sleep(5)' => sub {...});
175             $db->query('select sleep(5)' => sub {...});
176            
177             # Performed concurrently (5 seconds)
178             $mysql->db->query('select sleep(5)' => sub {...});
179             $mysql->db->query('select sleep(5)' => sub {...});
180            
181             All cached database handles will be reset automatically if a new process has
182             been forked, this allows multiple processes to share the same L
183             object safely.
184              
185              
186             Note that this whole distribution is EXPERIMENTAL and will change without
187             warning!
188              
189             =head1 EVENTS
190              
191             L inherits all events from L and can emit the
192             following new ones.
193              
194             =head2 connection
195              
196             $mysql->on(connection => sub {
197             my ($mysql, $db) = @_;
198             ...
199             });
200              
201             Emitted when a new database connection has been established.
202              
203             =head1 ATTRIBUTES
204              
205             L implements the following attributes.
206              
207             =head2 max_connections
208              
209             my $max = $mysql->max_connections;
210             $mysql = $mysql->max_connections(3);
211              
212             Maximum number of idle database handles to cache for future use, defaults to
213             C<5>.
214              
215             =head2 migrations
216              
217             my $migrations = $mysql->migrations;
218             $mysql = $mysql->migrations(Mojo::MySQL5::Migrations->new);
219              
220             L object you can use to change your database schema more
221             easily.
222              
223             # Load migrations from file and migrate to latest version
224             $mysql->migrations->from_file('/home/sri/migrations.sql')->migrate;
225              
226             MySQL does not support nested transactions and DDL transactions.
227             DDL statements cause implicit C. C will be called if
228             any step of migration script fails, but only DML statements after the
229             last implicit or explicit C can be reverted.
230             Not all MySQL storage engines (like C) support transactions.
231              
232             This means database will most likely be left in unknown state if migration script fails.
233             Use this feature with caution and remember to always backup your database.
234              
235             =head2 url
236              
237             my $url = $mysql->url;
238             $url = $mysql->url(
239             Mojo::MySQL5::URL->new('mysql://user@host/test?connect_timeout=0'));
240              
241             Connection L.
242              
243             =head2 options
244              
245             Use L->options.
246              
247             See L for list of supported options.
248              
249             =head2 password
250              
251             Use L->password.
252              
253             =head2 username
254              
255             Use L->username.
256              
257              
258             =head1 METHODS
259              
260             L inherits all methods from L and implements the
261             following new ones.
262              
263             =head2 db
264              
265             my $db = $mysql->db;
266              
267             Get L object for a cached or newly created database
268             handle. The database handle will be automatically cached again when that
269             object is destroyed, so you can handle connection timeouts gracefully by
270             holding on to it only for short amounts of time.
271              
272             # Add up all the money
273             say $mysql->db->query('select * from accounts')
274             ->hashes->reduce(sub { $a->{money} + $b->{money} });
275              
276             =head2 from_string
277              
278             $mysql = $mysql->from_string('mysql://user@/test');
279              
280             Parse configuration from connection string.
281              
282             # Just a database
283             $mysql->from_string('mysql:///db1');
284              
285             # Username and database
286             $mysql->from_string('mysql://batman@/db2');
287              
288             # Username, password, host and database
289             $mysql->from_string('mysql://batman:s3cret@localhost/db3');
290              
291             # Username, domain socket and database
292             $mysql->from_string('mysql://batman@%2ftmp%2fmysql.sock/db4');
293              
294             # Username, database and additional options
295             $mysql->from_string('mysql://batman@/db5?PrintError=1');
296              
297             =head2 new
298              
299             my $mysql = Mojo::MySQL5->new;
300             my $mysql = Mojo::MySQL5->new('mysql://user:s3cret@host:port/database');
301             my $mysql = Mojo::MySQL5->new(
302             url => Mojo::MySQL5::URL->new(
303             host => 'localhost',
304             port => 3306,
305             username => 'user',
306             password => 's3cret',
307             options => { utf8 => 1, found_rows => 1 }
308             )
309             );
310              
311             Construct a new L object and parse connection string with
312             L if necessary.
313              
314             =head1 REFERENCE
315              
316             This is the class hierarchy of the L distribution.
317              
318             =over 2
319              
320             =item * L
321              
322             =item * L
323              
324             =item * L
325              
326             =item * L
327              
328             =item * L
329              
330             =item * L
331              
332             =item * L
333              
334             =back
335              
336             =head1 AUTHOR
337              
338             Jan Henning Thorsen, C.
339              
340             Svetoslav Naydenov, C.
341              
342             A lot of code in this module is taken from Sebastian Riedel's L.
343              
344             =head1 COPYRIGHT AND LICENSE
345              
346             Copyright (C) 2015, Svetoslav Naydenov.
347              
348             This program is free software, you can redistribute it and/or modify it under
349             the terms of the Artistic License version 2.0.
350              
351             =head1 SEE ALSO
352              
353             L,
354              
355             L Async Connector for PostgreSQL using L, L,
356              
357             L Async Connector for MySQL using L, L,
358              
359             L, L.
360              
361             =cut