File Coverage

blib/lib/Mojo/mysql.pm
Criterion Covered Total %
statement 57 93 61.2
branch 24 48 50.0
condition 4 15 26.6
subroutine 13 19 68.4
pod 5 5 100.0
total 103 180 57.2


line stmt bran cond sub pod time code
1             package Mojo::mysql;
2 18     18   3369728 use Mojo::Base 'Mojo::EventEmitter';
  18         39  
  18         135  
3              
4 18     18   45472 use Carp qw(croak);
  18         35  
  18         1224  
5 18     18   29435 use DBI;
  18         428944  
  18         1765  
6 18     18   11969 use File::Spec::Functions qw(file_name_is_absolute);
  18         11635  
  18         1511  
7 18     18   10207 use Mojo::mysql::Database;
  18         69  
  18         166  
8 18     18   10481 use Mojo::mysql::Migrations;
  18         60  
  18         120  
9 18     18   12830 use Mojo::URL;
  18         182609  
  18         156  
10 18     18   1043 use Scalar::Util qw(blessed weaken);
  18         39  
  18         1173  
11 18     18   10690 use SQL::Abstract::mysql;
  18         87  
  18         2397  
12              
13 18     18   199 use constant MARIADB => !!eval { require DBD::MariaDB; DBD::MariaDB->VERSION(1.21) };
  18         42  
  18         40  
  18         5020  
  0         0  
14 18     18   100 use constant URI => !!eval { require URI::db; URI::db->VERSION(0.10) };
  18         40  
  18         84  
  18         37663  
  0         0  
15              
16             our $VERSION = '1.28';
17              
18             has abstract => sub { SQL::Abstract::mysql->new(quote_char => chr(96), name_sep => '.') };
19             has auto_migrate => 0;
20             has database_class => 'Mojo::mysql::Database';
21             has dsn => 'dbi:mysql:dbname=test';
22             has max_connections => 5;
23              
24             has migrations => sub {
25             my $migrations = Mojo::mysql::Migrations->new(mysql => shift);
26             weaken $migrations->{mysql};
27             return $migrations;
28             };
29              
30             has options => sub {
31             my $self = shift;
32             my $options = {AutoCommit => 1, AutoInactiveDestroy => 1, PrintError => 0, RaiseError => 1};
33             $options->{mysql_enable_utf8} = 1 if $self->dsn =~ m!^dbi:mysql:!;
34             return $options;
35             };
36              
37             has [qw(password username)] => '';
38             has pubsub => sub {
39             require Mojo::mysql::PubSub;
40             my $pubsub = Mojo::mysql::PubSub->new(mysql => shift);
41             warn "Use of Mojo::mysql::PubSub is highly EXPERIMENTAL and should be considered an experiment"
42             unless $ENV{MOJO_PUBSUB_EXPERIMENTAL};
43             weaken $pubsub->{mysql};
44             return $pubsub;
45             };
46              
47             sub close_idle_connections {
48 0   0 0 1 0 my ($self, $keep) = (shift, $_[0] || 0);
49 0   0     0 my $queue = $self->{queue} || [];
50              
51             # The database handle needs to be destroyed before the file handle
52 0         0 shift(@$queue)->[0] = undef while @$queue > $keep;
53 0         0 return $self;
54             }
55              
56             sub db {
57 0     0 1 0 my $self = shift;
58              
59             # Fork safety
60 0 0 0     0 delete @$self{qw(pid queue)} unless ($self->{pid} //= $$) eq $$;
61              
62 0         0 my ($dbh, $handle) = @{$self->_dequeue};
  0         0  
63 0         0 return $self->database_class->new(dbh => $dbh, handle => $handle, mysql => $self);
64             }
65              
66             sub from_string {
67 10     10 1 97 my ($self, $str) = @_;
68              
69             # Protocol
70 10 100       35 return $self unless $str;
71 8 100       52 my $url = blessed $str ? $str : Mojo::URL->new($str);
72              
73 8 50       1267 my $protocol = $url->can('engine') ? $url->engine : $url->protocol;
74 8 100       281 croak qq{Invalid MySQL/MariaDB connection string "$str"} unless $protocol =~ m!^(mariadb|mysql)$!;
75 7 50       21 my $dsn = $protocol eq 'mariadb' ? 'dbi:MariaDB' : 'dbi:mysql';
76              
77             # Database
78 7 50       29 my $dbname = $url->can('dbname') ? $url->dbname : $url->path->parts->[0];
79 7 100       430 $dsn .= ":dbname=$dbname" if length $dbname;
80              
81             # Host and port
82 7 100       18 if (my $host = $url->host) { $dsn .= file_name_is_absolute($host) ? ";mysql_socket=$host" : ";host=$host" }
  2 100       14  
83 7 100       52 if (my $port = $url->port) { $dsn .= ";port=$port" }
  1         4  
84              
85             # Need to set the dsn before reading options
86 7         42 $self->dsn($dsn);
87              
88             # Username and password
89 7 100       45 if ($url->userinfo) {
90 5         25 my @info = split /:/, $url->userinfo, 2;
91 5         35 $self->username($info[0]);
92 5 100 50     35 $self->password($info[1] // '') if defined $info[1];
93             }
94              
95             # Options
96 7 50       56 my $hash = $url->can('query_form_hash') ? $url->query_form_hash : $url->query->to_hash;
97 7         551 @{$self->options}{keys %$hash} = values %$hash;
  7         16  
98              
99 7         76 return $self;
100             }
101              
102             sub new {
103 12 100 100 12 1 607767 @_ > 2 || ref $_[-1] eq 'HASH' ? shift->SUPER::new(@_) : shift->SUPER::new->from_string(@_);
104             }
105              
106             sub strict_mode {
107 0 0   0 1   my $self = ref $_[0] ? shift : shift->new(@_);
108 0 0         $self->{strict_mode} = $_[0] ? 1 : @_ ? 0 : 1;
    0          
109 0 0         warn "[Mojo::mysql] strict_mode($self->{strict_mode})\n" if $ENV{DBI_TRACE};
110 0           $self->close_idle_connections;
111 0           return $self;
112             }
113              
114             sub _dequeue {
115 0     0     my $self = shift;
116 0           my ($dsn, $dbh) = ($self->dsn);
117              
118             # https://github.com/jhthorsen/mojo-mysql/pull/47
119 0 0         die "DBD::MariaDB 1.21 is required for Mojo::mysql to work properly" if !MARIADB && index($dsn, 'dbi:MariaDB') == 0;
120              
121 0 0         while (my $c = shift @{$self->{queue}}) { return $c if $c->[0]->ping }
  0            
  0            
122 0           $dbh = DBI->connect(map { $self->$_ } qw(dsn username password options));
  0            
123              
124             # batman's probably going to have more "fun" than you have ...
125             # especially once he discovers that DBD::mysql randomly reconnects under
126             # you, silently, but only if certain env vars are set
127             # hint: force-set mysql_auto_reconnect or whatever it's called to 0
128 0           Mojo::mysql::Database->_dbh_attr($dbh, mysql_auto_reconnect => 0);
129              
130             # Maintain Commits with Mojo::mysql::Transaction
131 0           $dbh->{AutoCommit} = 1;
132              
133 0 0         $self->_set_strict_mode($dbh) if $self->{strict_mode};
134 0 0 0       $self->migrations->migrate if $self->auto_migrate and !$self->{migrated}++;
135 0           $self->emit(connection => $dbh);
136 0           [$dbh];
137             }
138              
139             sub _enqueue {
140 0     0     my ($self, $dbh, $handle) = @_;
141 0 0         push @{$self->{queue}}, [$dbh, $handle] if $dbh->{Active};
  0            
142 0           $self->close_idle_connections($self->max_connections);
143             }
144              
145             sub _set_strict_mode {
146 0     0     $_[1]->do(q[SET SQL_MODE = CONCAT('ANSI,TRADITIONAL,ONLY_FULL_GROUP_BY,', @@sql_mode)]);
147 0           $_[1]->do(q[SET SQL_AUTO_IS_NULL = 0]);
148             }
149              
150             1;
151              
152             =encoding utf8
153              
154             =head1 NAME
155              
156             Mojo::mysql - Mojolicious and Async MySQL/MariaDB
157              
158             =head1 SYNOPSIS
159              
160             use Mojo::mysql;
161              
162             # Connect to a local database
163             my $mysql = Mojo::mysql->strict_mode('mysql://username@/test');
164              
165             # Connect to a remote database
166             my $mysql = Mojo::mysql->strict_mode('mysql://username:password@hostname/test');
167             # MySQL >= 8.0:
168             my $mysql = Mojo::mysql->strict_mode('mysql://username:password@hostname/test;mysql_ssl=1');
169              
170             # Use DBD::MariaDB instead of DBD::mysql
171             my $mysql = Mojo::mysql->strict_mode('mariadb://username@/test');
172              
173             # Create a table
174             $mysql->db->query(
175             'create table names (id integer auto_increment primary key, name text)');
176              
177             # Insert a few rows
178             my $db = $mysql->db;
179             $db->query('insert into names (name) values (?)', 'Sara');
180             $db->query('insert into names (name) values (?)', 'Stefan');
181              
182             # Insert more rows in a transaction
183             eval {
184             my $tx = $db->begin;
185             $db->query('insert into names (name) values (?)', 'Baerbel');
186             $db->query('insert into names (name) values (?)', 'Wolfgang');
187             $tx->commit;
188             };
189             say $@ if $@;
190              
191             # Insert another row and return the generated id
192             say $db->query('insert into names (name) values (?)', 'Daniel')
193             ->last_insert_id;
194              
195             # Use SQL::Abstract::mysql to generate queries for you
196             $db->insert('names', {name => 'Isabel'});
197             say $db->select('names', undef, {name => 'Isabel'})->hash->{id};
198             $db->update('names', {name => 'Bel'}, {name => 'Isabel'});
199             $db->delete('names', {name => 'Bel'});
200              
201             # Select one row at a time
202             my $results = $db->query('select * from names');
203             while (my $next = $results->hash) {
204             say $next->{name};
205             }
206              
207             # Select all rows blocking
208             $db->query('select * from names')
209             ->hashes->map(sub { $_->{name} })->join("\n")->say;
210              
211             # Select all rows non-blocking
212             $db->query('select * from names' => sub {
213             my ($db, $err, $results) = @_;
214             $results->hashes->map(sub { $_->{name} })->join("\n")->say;
215             });
216              
217             # Concurrent non-blocking queries (synchronized with promises)
218             my $now = $db->query_p('select now() as now');
219             my $names = $db->query_p('select * from names');
220             Mojo::Promise->all($now, $names)->then(sub {
221             my ($now, $names) = @_;
222             say $now->[0]->hash->{now};
223             say $_->{name} for $names->[0]->hashes->each;
224             })->catch(sub {
225             my $err = shift;
226             warn "Something went wrong: $err";
227             })->wait;
228              
229             Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
230              
231             =head1 DESCRIPTION
232              
233             L is a tiny wrapper around L and L that
234             makes L and L a lot
235             of fun to use with the L real-time web
236             framework.
237              
238             The two DBD drivers are compatible with both MySQL and MariaDB, but they offer
239             different L. L should have better unicode support
240             though and might become the default in the future.
241              
242             Database and handles are cached automatically, so they can be reused
243             transparently to increase performance. And you can handle connection timeouts
244             gracefully by holding on to them only for short amounts of time.
245              
246             use Mojolicious::Lite;
247             use Mojo::mysql;
248              
249             helper mysql =>
250             sub { state $mysql = Mojo::mysql->strict_mode('mysql://sri:s3cret@localhost/db') };
251              
252             get '/' => sub {
253             my $c = shift;
254             my $db = $c->mysql->db;
255             $c->render(json => $db->query('select now() as time')->hash);
256             };
257              
258             app->start;
259              
260             While all I/O operations are performed blocking, you can wait for long running
261             queries asynchronously, allowing the L event loop to perform
262             other tasks in the meantime. Since database connections usually have a very low
263             latency, this often results in very good performance.
264              
265             Every database connection can only handle one active query at a time, this
266             includes asynchronous ones. So if you start more than one, they will be put on
267             a waiting list and performed sequentially. To perform multiple queries
268             concurrently, you have to use multiple connections.
269              
270             # Performed sequentially (10 seconds)
271             my $db = $mysql->db;
272             $db->query('select sleep(5)' => sub {...});
273             $db->query('select sleep(5)' => sub {...});
274              
275             # Performed concurrently (5 seconds)
276             $mysql->db->query('select sleep(5)' => sub {...});
277             $mysql->db->query('select sleep(5)' => sub {...});
278              
279             All cached database handles will be reset automatically if a new process has
280             been forked, this allows multiple processes to share the same L
281             object safely.
282              
283             =head1 EVENTS
284              
285             L inherits all events from L and can emit the
286             following new ones.
287              
288             =head2 connection
289              
290             $mysql->on(connection => sub {
291             my ($mysql, $dbh) = @_;
292             ...
293             });
294              
295             Emitted when a new database connection has been established.
296              
297             =head1 ATTRIBUTES
298              
299             L implements the following attributes.
300              
301             =head2 abstract
302              
303             $abstract = $mysql->abstract;
304             $mysql = $mysql->abstract(SQL::Abstract::mysql->new);
305              
306             L object used to generate CRUD queries for L.
307              
308             # Generate statements and bind values
309             my ($stmt, @bind) = $mysql->abstract->select('names');
310              
311             =head2 auto_migrate
312              
313             my $bool = $mysql->auto_migrate;
314             $mysql = $mysql->auto_migrate($bool);
315              
316             Automatically migrate to the latest database schema with L, as
317             soon as the first database connection has been established.
318              
319             Defaults to false.
320              
321             =head2 database_class
322              
323             $class = $mysql->database_class;
324             $mysql = $mysql->database_class("MyApp::Database");
325              
326             Class to be used by L, defaults to L. Note that this
327             class needs to have already been loaded before L is called.
328              
329             =head2 dsn
330              
331             my $dsn = $mysql->dsn;
332             $mysql = $mysql->dsn('dbi:mysql:dbname=foo');
333              
334             Data Source Name, defaults to C.
335              
336             =head2 max_connections
337              
338             my $max = $mysql->max_connections;
339             $mysql = $mysql->max_connections(3);
340              
341             Maximum number of idle database handles to cache for future use, defaults to
342             C<5>.
343              
344             =head2 migrations
345              
346             my $migrations = $mysql->migrations;
347             $mysql = $mysql->migrations(Mojo::mysql::Migrations->new);
348              
349             L object you can use to change your database schema more
350             easily.
351              
352             # Load migrations from file and migrate to latest version
353             $mysql->migrations->from_file('/Users/sri/migrations.sql')->migrate;
354              
355             MySQL and MariaDB does not support nested transactions and DDL transactions.
356             DDL statements cause implicit C. C will be called if any step
357             of migration script fails, but only DML statements after the last implicit or
358             explicit C can be reverted. Not all storage engines (like C)
359             support transactions.
360              
361             This means database will most likely be left in unknown state if migration script fails.
362             Use this feature with caution and remember to always backup your database.
363              
364             =head2 options
365              
366             my $options = $mysql->options;
367             $mysql = $mysql->options({mysql_use_result => 1});
368              
369             Options for database handles, defaults to activating C (only
370             for L), C, C as well as
371             C and deactivating C. C and C
372             are considered mandatory, so deactivating them would be very dangerous.
373              
374             C is never enabled, L takes care of dead connections.
375              
376             C cannot not be disabled, use $db->L to manage transactions.
377              
378             C is enabled for blocking and disabled in event loop for non-blocking queries.
379              
380             About C:
381              
382             The mysql_enable_utf8 sets the utf8 charset which only supports up to 3-byte
383             UTF-8 encodings. mysql_enable_utf8mb4 (as of DBD::mysql 4.032) properly
384             supports encoding unicode characters to up to 4 bytes, such as 𠜎. It means the
385             connection charset will be utf8mb4 (supported back to at least mysql 5.5) and
386             these unicode characters will be supported, but no other changes.
387              
388             See also L
389              
390             =head2 password
391              
392             my $password = $mysql->password;
393             $mysql = $mysql->password('s3cret');
394              
395             Database password, defaults to an empty string.
396              
397             =head2 pubsub
398              
399             my $pubsub = $mysql->pubsub;
400             $mysql = $mysql->pubsub(Mojo::mysql::PubSub->new);
401              
402             L should be considered an EXPERIMENT! See
403             L for more information.
404              
405             =head2 username
406              
407             my $username = $mysql->username;
408             $mysql = $mysql->username('batman');
409              
410             Database username, defaults to an empty string.
411              
412             =head1 METHODS
413              
414             L inherits all methods from L and implements the
415             following new ones.
416              
417             =head2 close_idle_connections
418              
419             $mysql = $mysql->close_idle_connections($keep);
420              
421             Close all connections that are not currently active, or limit the
422             number of idle connections to C<$keep>.
423              
424             =head2 db
425              
426             my $db = $mysql->db;
427              
428             Get L object for a cached or newly created database
429             handle. The database handle will be automatically cached again when that
430             object is destroyed, so you can handle connection timeouts gracefully by
431             holding on to it only for short amounts of time.
432              
433             =head2 from_string
434              
435             $mysql = $mysql->from_string('mysql://user@/test');
436             $mysql = $mysql->from_string(Mojo::URL->new);
437             $mysql = $mysql->from_string(URI::db->new);
438              
439             Parse configuration from connection string or a connection string object.
440              
441             # Just a database
442             $mysql->from_string('mysql:///db1');
443              
444             # Username and database
445             $mysql->from_string('mysql://batman@/db2');
446              
447             # Username, password, host and database
448             $mysql->from_string('mysql://batman:s3cret@localhost/db3');
449              
450             # Username, domain socket and database
451             $mysql->from_string('mysql://batman@%2ftmp%2fmysql.sock/db4');
452              
453             # Username, database and additional options
454             $mysql->from_string('mysql://batman@/db5?PrintError=1&RaiseError=0');
455              
456             =head2 new
457              
458             my $mysql = Mojo::mysql->new;
459             my $mysql = Mojo::mysql->new(%attrs);
460             my $mysql = Mojo::mysql->new(\%attrs);
461             my $mysql = Mojo::mysql->new('mysql://user@/test');
462             my $mysql = Mojo::mysql->new('mariadb://user@/test');
463              
464             Construct a new L object either from L and or parse
465             connection string with L if necessary.
466              
467             Using the "mariadb" scheme requires the optional module L version
468             1.21 (or later) to be installed.
469              
470             =head2 strict_mode
471              
472             my $mysql = Mojo::mysql->strict_mode('mysql://user@/test');
473             my $mysql = $mysql->strict_mode($boolean);
474              
475             This method can act as both a constructor and a method. When called as a
476             constructor, it will be the same as:
477              
478             my $mysql = Mojo::mysql->new('mysql://user@/test')->strict_mode(1);
479              
480             Enabling strict mode will execute the following statement when a new connection
481             is created:
482              
483             SET SQL_MODE = CONCAT('ANSI,TRADITIONAL,ONLY_FULL_GROUP_BY,', @@sql_mode)
484             SET SQL_AUTO_IS_NULL = 0
485              
486             The idea is to set up a connection that makes it harder for MySQL to allow
487             "invalid" data to be inserted.
488              
489             This method will not be removed, but the internal commands is subject to
490             change.
491              
492             =head1 DEBUGGING
493              
494             You can set the C environment variable to get some advanced
495             diagnostics information printed to C by L.
496              
497             DBI_TRACE=1
498             DBI_TRACE=15
499             DBI_TRACE=15=dbitrace.log
500             DBI_TRACE=SQL
501             DBI_PROFILE=2
502              
503             See also L and
504             L.
505              
506             =head1 REFERENCE
507              
508             This is the class hierarchy of the L distribution.
509              
510             =over 2
511              
512             =item * L
513              
514             =item * L
515              
516             =item * L
517              
518             =item * L
519              
520             =item * L
521              
522             =item * L
523              
524             =back
525              
526             =head1 AUTHORS
527              
528             This project is highly inspired by Sebastian Riedel's L.
529              
530             =head2 Project Founder
531              
532             Jan Henning Thorsen - C
533              
534             =head2 Contributors
535              
536             =over 2
537              
538              
539             =item * Adam Hopkins
540              
541             =item * Alexander Karelas
542              
543             =item * Curt Hochwender
544              
545             =item * Dan Book
546              
547             =item * Doug Bell
548              
549             =item * Florian Heyer
550              
551             =item * Hernan Lopes
552              
553             =item * Karl Rune Nilsen
554              
555             =item * Larry Leszczynski
556              
557             =item * Lucas Tiago de Moraes
558              
559             =item * Matt S Trout
560              
561             =item * Mike Magowan
562              
563             =item * Mohammad S Anwar
564              
565             =item * Rolf Stöckli
566              
567             =item * Sebastian Riedel
568              
569             =item * Svetoslav Naydenov
570              
571             =item * Svetoslav Naydenov
572              
573             =item * Tekki
574              
575             =back
576              
577             =head1 COPYRIGHT AND LICENSE
578              
579             Copyright (C) 2014-2019, Jan Henning Thorsen.
580              
581             This program is free software, you can redistribute it and/or modify it under
582             the terms of the Artistic License version 2.0.
583              
584             =head1 SEE ALSO
585              
586             L,
587              
588             L Async Connector for PostgreSQL using L, L,
589              
590             L Pure-Perl non-blocking I/O MySQL Connector, L,
591              
592             L, L.
593              
594             =cut