File Coverage

blib/lib/Mojo/Pg.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package Mojo::Pg;
2 6     6   1258767 use Mojo::Base 'Mojo::EventEmitter';
  6         16  
  6         45  
3              
4 6     6   5344 use Carp 'croak';
  6         17  
  6         343  
5 6     6   7896 use DBI;
  6         94533  
  6         446  
6 6     6   3442 use Mojo::Pg::Database;
  0            
  0            
7             use Mojo::Pg::Migrations;
8             use Mojo::Pg::PubSub;
9             use Mojo::URL;
10             use Scalar::Util qw(blessed weaken);
11             use SQL::Abstract;
12              
13             has abstract => sub {
14             SQL::Abstract->new(array_datatypes => 1, name_sep => '.', quote_char => '"');
15             };
16             has [qw(auto_migrate parent search_path)];
17             has database_class => 'Mojo::Pg::Database';
18             has dsn => 'dbi:Pg:';
19             has max_connections => 1;
20             has migrations => sub {
21             my $migrations = Mojo::Pg::Migrations->new(pg => shift);
22             weaken $migrations->{pg};
23             return $migrations;
24             };
25             has options => sub {
26             {
27             AutoCommit => 1,
28             AutoInactiveDestroy => 1,
29             PrintError => 0,
30             PrintWarn => 0,
31             RaiseError => 1
32             };
33             };
34             has [qw(password username)] => '';
35             has pubsub => sub {
36             my $pubsub = Mojo::Pg::PubSub->new(pg => shift);
37             weaken $pubsub->{pg};
38             return $pubsub;
39             };
40              
41             our $VERSION = '4.01';
42              
43             sub db { $_[0]->database_class->new(dbh => $_[0]->_prepare, pg => $_[0]) }
44              
45             sub from_string {
46             my ($self, $str) = @_;
47              
48             # Parent
49             return $self unless $str;
50             return $self->parent($str) if blessed $str && $str->isa('Mojo::Pg');
51              
52             # Protocol
53             my $url = Mojo::URL->new($str);
54             croak qq{Invalid PostgreSQL connection string "$str"}
55             unless $url->protocol =~ /^postgres(?:ql)?$/;
56              
57             # Connection information
58             my $db = $url->path->parts->[0];
59             my $dsn = defined $db ? "dbi:Pg:dbname=$db" : 'dbi:Pg:';
60             if (my $host = $url->host) { $dsn .= ";host=$host" }
61             if (my $port = $url->port) { $dsn .= ";port=$port" }
62             if (defined(my $username = $url->username)) { $self->username($username) }
63             if (defined(my $password = $url->password)) { $self->password($password) }
64              
65             # Service and search_path
66             my $hash = $url->query->to_hash;
67             if (my $service = delete $hash->{service}) { $dsn .= "service=$service" }
68             if (my $path = delete $hash->{search_path}) {
69             $self->search_path(ref $path ? $path : [$path]);
70             }
71              
72             # Options
73             @{$self->options}{keys %$hash} = values %$hash;
74              
75             return $self->dsn($dsn);
76             }
77              
78             sub new { @_ > 1 ? shift->SUPER::new->from_string(@_) : shift->SUPER::new }
79              
80             sub _dequeue {
81             my $self = shift;
82              
83             # Fork-safety
84             delete @$self{qw(pid queue)} unless ($self->{pid} //= $$) eq $$;
85              
86             while (my $dbh = shift @{$self->{queue} || []}) { return $dbh if $dbh->ping }
87             my $dbh = DBI->connect(map { $self->$_ } qw(dsn username password options));
88              
89             # Search path
90             if (my $path = $self->search_path) {
91             my $search_path = join ', ', map { $dbh->quote_identifier($_) } @$path;
92             $dbh->do("set search_path to $search_path");
93             }
94              
95             $self->emit(connection => $dbh);
96              
97             return $dbh;
98             }
99              
100             sub _enqueue {
101             my ($self, $dbh) = @_;
102              
103             if (my $parent = $self->parent) { return $parent->_enqueue($dbh) }
104              
105             my $queue = $self->{queue} ||= [];
106             push @$queue, $dbh if $dbh->{Active};
107             shift @$queue while @$queue > $self->max_connections;
108             }
109              
110             sub _prepare {
111             my $self = shift;
112              
113             # Automatic migrations
114             ++$self->{migrated} and $self->migrations->migrate
115             if !$self->{migrated} && $self->auto_migrate;
116              
117             my $parent = $self->parent;
118             return $parent ? $parent->_prepare : $self->_dequeue;
119             }
120              
121             1;
122              
123             =encoding utf8
124              
125             =head1 NAME
126              
127             Mojo::Pg - Mojolicious ♥ PostgreSQL
128              
129             =head1 SYNOPSIS
130              
131             use Mojo::Pg;
132              
133             # Use a PostgreSQL connection string for configuration
134             my $pg = Mojo::Pg->new('postgresql://postgres@/test');
135              
136             # Select the server version
137             say $pg->db->query('select version() as version')->hash->{version};
138              
139             # Use migrations to create a table
140             $pg->migrations->name('my_names_app')->from_string(<migrate;
141             -- 1 up
142             create table names (id serial primary key, name text);
143             -- 1 down
144             drop table names;
145             EOF
146              
147             # Use migrations to drop and recreate the table
148             $pg->migrations->migrate(0)->migrate;
149              
150             # Get a database handle from the cache for multiple queries
151             my $db = $pg->db;
152              
153             # Use SQL::Abstract to generate simple CRUD queries for you
154             $db->insert('names', {name => 'Isabell'});
155             my $id = $db->select('names', ['id'], {name => 'Isabell'})->hash->{id};
156             $db->update('names', {name => 'Belle'}, {id => $id});
157             $db->delete('names', {name => 'Belle'});
158              
159             # Insert a few rows in a transaction with SQL and placeholders
160             eval {
161             my $tx = $db->begin;
162             $db->query('insert into names (name) values (?)', 'Sara');
163             $db->query('insert into names (name) values (?)', 'Stefan');
164             $tx->commit;
165             };
166             say $@ if $@;
167              
168             # Insert another row with SQL::Abstract and return the generated id
169             say $db->insert('names', {name => 'Daniel'}, {returning => 'id'})->hash->{id};
170              
171             # JSON roundtrip
172             say $db->query('select ?::json as foo', {json => {bar => 'baz'}})
173             ->expand->hash->{foo}{bar};
174              
175             # Select all rows blocking with SQL::Abstract
176             say $_->{name} for $db->select('names')->hashes->each;
177              
178             # Select all rows non-blocking with SQL::Abstract
179             $db->select('names' => sub {
180             my ($db, $err, $results) = @_;
181             die $err if $err;
182             say $_->{name} for $results->hashes->each;
183             });
184             Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
185              
186             # Concurrent non-blocking queries (synchronized with a delay)
187             Mojo::IOLoop->delay(
188             sub {
189             my $delay = shift;
190             $pg->db->query('select now() as now' => $delay->begin);
191             $pg->db->query('select * from names' => $delay->begin);
192             },
193             sub {
194             my ($delay, $time_err, $time, $names_err, $names) = @_;
195             if (my $err = $time_err || $names_err) { die $err }
196             say $time->hash->{now};
197             say $_->{name} for $names->hashes->each;
198             }
199             )->wait;
200              
201             # Send and receive notifications non-blocking
202             $pg->pubsub->listen(foo => sub {
203             my ($pubsub, $payload) = @_;
204             say "foo: $payload";
205             $pubsub->notify(bar => $payload);
206             });
207             $pg->pubsub->listen(bar => sub {
208             my ($pubsub, $payload) = @_;
209             say "bar: $payload";
210             });
211             $pg->pubsub->notify(foo => 'PostgreSQL rocks!');
212             Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
213              
214             =head1 DESCRIPTION
215              
216             L is a tiny wrapper around L that makes
217             L a lot of fun to use with the
218             L real-time web framework. Perform queries
219             blocking and non-blocking, use all
220             L
221             PostgreSQL has to offer, generate CRUD queries from data structures, manage your
222             database schema with migrations and build scalable real-time web applications
223             with the publish/subscribe pattern.
224              
225             Database and statement handles are cached automatically, and will be reused
226             transparently to increase performance. You can handle connection timeouts
227             gracefully by holding on to them only for short amounts of time.
228              
229             use Mojolicious::Lite;
230             use Mojo::Pg;
231              
232             helper pg => sub { state $pg = Mojo::Pg->new('postgresql://postgres@/test') };
233              
234             get '/' => sub {
235             my $c = shift;
236             my $db = $c->pg->db;
237             $c->render(json => $db->query('select now() as now')->hash);
238             };
239              
240             app->start;
241              
242             In this example application, we create a C helper to store a L
243             object. Our action calls that helper and uses the method L to
244             dequeue a L object from the connection pool. Then we use the
245             method L to execute an
246             L statement, which
247             returns a L object. And finally we call the method
248             L to retrieve the first row as a hash reference.
249              
250             While all I/O operations are performed blocking, you can wait for long running
251             queries asynchronously, allowing the L event loop to perform
252             other tasks in the meantime. Since database connections usually have a very low
253             latency, this often results in very good performance.
254              
255             Every database connection can only handle one active query at a time, this
256             includes asynchronous ones. To perform multiple queries concurrently, you have
257             to use multiple connections.
258              
259             # Performed concurrently (5 seconds)
260             $pg->db->query('select pg_sleep(5)' => sub {...});
261             $pg->db->query('select pg_sleep(5)' => sub {...});
262              
263             All cached database handles will be reset automatically if a new process has
264             been forked, this allows multiple processes to share the same L
265             object safely.
266              
267             =head1 GROWING
268              
269             And as your application grows, you can move queries into model classes.
270              
271             package MyApp::Model::Time;
272             use Mojo::Base -base;
273              
274             has 'pg';
275              
276             sub now { shift->pg->db->query('select now() as now')->hash }
277              
278             1;
279              
280             Which get integrated into your application with helpers.
281              
282             use Mojolicious::Lite;
283             use Mojo::Pg;
284             use MyApp::Model::Time;
285              
286             helper pg => sub { state $pg = Mojo::Pg->new('postgresql://postgres@/test') };
287             helper time => sub { state $time = MyApp::Model::Time->new(pg => shift->pg) };
288              
289             get '/' => sub {
290             my $c = shift;
291             $c->render(json => $c->time->now);
292             };
293              
294             app->start;
295              
296             =head1 EXAMPLES
297              
298             This distribution also contains two great
299             L
300             you can use for inspiration. The minimal
301             L
302             application will show you how to scale WebSockets to multiple servers, and the
303             well-structured
304             L application
305             how to apply the MVC design pattern in practice.
306              
307             =head1 EVENTS
308              
309             L inherits all events from L and can emit the
310             following new ones.
311              
312             =head2 connection
313              
314             $pg->on(connection => sub {
315             my ($pg, $dbh) = @_;
316             ...
317             });
318              
319             Emitted when a new database connection has been established.
320              
321             $pg->on(connection => sub {
322             my ($pg, $dbh) = @_;
323             $dbh->do('set search_path to my_schema');
324             });
325              
326             =head1 ATTRIBUTES
327              
328             L implements the following attributes.
329              
330             =head2 abstract
331              
332             my $abstract = $pg->abstract;
333             $pg = $pg->abstract(SQL::Abstract->new);
334              
335             L object used to generate CRUD queries for L,
336             defaults to enabling C and setting C to C<.> and
337             C to C<">.
338              
339             # Generate WHERE clause and bind values
340             my($stmt, @bind) = $pg->abstract->where({foo => 'bar', baz => 'yada'});
341              
342             =head2 auto_migrate
343              
344             my $bool = $pg->auto_migrate;
345             $pg = $pg->auto_migrate($bool);
346              
347             Automatically migrate to the latest database schema with L, as
348             soon as L has been called for the first time.
349              
350             =head2 database_class
351              
352             my $class = $pg->database_class;
353             $pg = $pg->database_class('MyApp::Database');
354              
355             Class to be used by L, defaults to L. Note that this
356             class needs to have already been loaded before L is called.
357              
358             =head2 dsn
359              
360             my $dsn = $pg->dsn;
361             $pg = $pg->dsn('dbi:Pg:dbname=foo');
362              
363             Data source name, defaults to C.
364              
365             =head2 max_connections
366              
367             my $max = $pg->max_connections;
368             $pg = $pg->max_connections(3);
369              
370             Maximum number of idle database handles to cache for future use, defaults to
371             C<1>.
372              
373             =head2 migrations
374              
375             my $migrations = $pg->migrations;
376             $pg = $pg->migrations(Mojo::Pg::Migrations->new);
377              
378             L object you can use to change your database schema more
379             easily.
380              
381             # Load migrations from file and migrate to latest version
382             $pg->migrations->from_file('/home/sri/migrations.sql')->migrate;
383              
384             =head2 options
385              
386             my $options = $pg->options;
387             $pg = $pg->options({AutoCommit => 1, RaiseError => 1});
388              
389             Options for database handles, defaults to activating C,
390             C as well as C and deactivating C
391             as well as C. Note that C and C are
392             considered mandatory, so deactivating them would be very dangerous.
393              
394             =head2 parent
395              
396             my $parent = $pg->parent;
397             $pg = $pg->parent(Mojo::Pg->new);
398              
399             Another L object to use for connection management, instead of
400             establishing and caching our own database connections.
401              
402             =head2 password
403              
404             my $password = $pg->password;
405             $pg = $pg->password('s3cret');
406              
407             Database password, defaults to an empty string.
408              
409             =head2 pubsub
410              
411             my $pubsub = $pg->pubsub;
412             $pg = $pg->pubsub(Mojo::Pg::PubSub->new);
413              
414             L object you can use to send and receive notifications very
415             efficiently, by sharing a single database connection with many consumers.
416              
417             # Subscribe to a channel
418             $pg->pubsub->listen(news => sub {
419             my ($pubsub, $payload) = @_;
420             say "Received: $payload";
421             });
422              
423             # Notify a channel
424             $pg->pubsub->notify(news => 'PostgreSQL rocks!');
425              
426             =head2 search_path
427              
428             my $path = $pg->search_path;
429             $pg = $pg->search_path(['$user', 'foo', 'public']);
430              
431             Schema search path assigned to all new connections.
432              
433             # Isolate tests and avoid race conditions when running them in parallel
434             my $pg = Mojo::Pg->new('postgresql:///test')->search_path(['test_one']);
435             $pg->db->query('drop schema if exists test_one cascade');
436             $pg->db->query('create schema test_one');
437             ...
438             $pg->db->query('drop schema test_one cascade');
439              
440             =head2 username
441              
442             my $username = $pg->username;
443             $pg = $pg->username('sri');
444              
445             Database username, defaults to an empty string.
446              
447             =head1 METHODS
448              
449             L inherits all methods from L and implements the
450             following new ones.
451              
452             =head2 db
453              
454             my $db = $pg->db;
455              
456             Get a database object based on L (which is usually
457             L) for a cached or newly established database connection.
458             The L database handle will be automatically cached again when that
459             object is destroyed, so you can handle problems like connection timeouts
460             gracefully by holding on to it only for short amounts of time.
461              
462             # Add up all the money
463             say $pg->db->select('accounts')
464             ->hashes->reduce(sub { $a->{money} + $b->{money} });
465              
466             =head2 from_string
467              
468             $pg = $pg->from_string('postgresql://postgres@/test');
469             $pg = $pg->from_string(Mojo::Pg->new);
470              
471             Parse configuration from connection string or use another L object as
472             L.
473              
474             # Just a database
475             $pg->from_string('postgresql:///db1');
476              
477             # Just a service
478             $pg->from_string('postgresql://?service=foo');
479              
480             # Username and database
481             $pg->from_string('postgresql://sri@/db2');
482              
483             # Short scheme, username, password, host and database
484             $pg->from_string('postgres://sri:s3cret@localhost/db3');
485              
486             # Username, domain socket and database
487             $pg->from_string('postgresql://sri@%2ftmp%2fpg.sock/db4');
488              
489             # Username, database and additional options
490             $pg->from_string('postgresql://sri@/db5?PrintError=1&pg_server_prepare=0');
491              
492             # Service and additional options
493             $pg->from_string('postgresql://?service=foo&PrintError=1&RaiseError=0');
494              
495             # Username, database, an option and search_path
496             $pg->from_string('postgres://sri@/db6?&PrintError=1&search_path=test_schema');
497              
498             =head2 new
499              
500             my $pg = Mojo::Pg->new;
501             my $pg = Mojo::Pg->new('postgresql://postgres@/test');
502             my $pg = Mojo::Pg->new(Mojo::Pg->new);
503              
504             Construct a new L object and parse connection string with
505             L if necessary.
506              
507             # Customize configuration further
508             my $pg = Mojo::Pg->new->dsn('dbi:Pg:service=foo');
509              
510             =head1 DEBUGGING
511              
512             You can set the C environment variable to get some advanced
513             diagnostics information printed by L.
514              
515             DBI_TRACE=1
516             DBI_TRACE=15
517             DBI_TRACE=SQL
518              
519             =head1 REFERENCE
520              
521             This is the class hierarchy of the L distribution.
522              
523             =over 2
524              
525             =item * L
526              
527             =item * L
528              
529             =item * L
530              
531             =item * L
532              
533             =item * L
534              
535             =item * L
536              
537             =back
538              
539             =head1 AUTHOR
540              
541             Sebastian Riedel, C.
542              
543             =head1 CREDITS
544              
545             In alphabetical order:
546              
547             =over 2
548              
549             Christopher Eveland
550              
551             Dan Book
552              
553             Flavio Poletti
554              
555             Hernan Lopes
556              
557             William Lindley
558              
559             =back
560              
561             =head1 COPYRIGHT AND LICENSE
562              
563             Copyright (C) 2014-2017, Sebastian Riedel and others.
564              
565             This program is free software, you can redistribute it and/or modify it under
566             the terms of the Artistic License version 2.0.
567              
568             =head1 SEE ALSO
569              
570             L, L,
571             L.
572              
573             =cut