File Coverage

blib/lib/DBIx/Sunny.pm
Criterion Covered Total %
statement 23 109 21.1
branch 0 28 0.0
condition 0 27 0.0
subroutine 8 22 36.3
pod 1 1 100.0
total 32 187 17.1


line stmt bran cond sub pod time code
1             package DBIx::Sunny;
2              
3 10     10   937286 use strict;
  10         94  
  10         266  
4 10     10   46 use warnings;
  10         22  
  10         235  
5 10     10   252 use 5.008005;
  10         38  
6 10     10   13669 use DBI 1.615;
  10         157739  
  10         1148  
7              
8             our $VERSION = '0.9992';
9             our $SKIP_CALLER_REGEX = qr/^(:?DBIx?|DBD|Try::Tiny|Context::Preserve)\b/;
10              
11 10     10   4195 use parent qw/DBI/;
  10         2149  
  10         57  
12              
13             sub connect {
14 0     0 1   my $class = shift;
15 0           my ($dsn, $user, $pass, $attr) = @_;
16 0           $attr->{RaiseError} = 1;
17 0           $attr->{PrintError} = 0;
18 0           $attr->{ShowErrorStatement} = 1;
19 0           $attr->{AutoInactiveDestroy} = 1;
20 0 0         if ($dsn =~ /^(?i:dbi):SQLite:/) {
21 0           $attr->{sqlite_use_immediate_transaction} = 1;
22 0 0         $attr->{sqlite_unicode} = 1 unless exists $attr->{sqlite_unicode};
23             }
24 0 0 0       if ($dsn =~ /^(?i:dbi):mysql:/ && ! exists $attr->{mysql_enable_utf8} && ! exists $attr->{mysql_enable_utf8mb4} ) {
      0        
25 0           $attr->{mysql_enable_utf8} = 1;
26             }
27 0 0 0       if ($dsn =~ /^(?i:dbi):Pg:/ && ! exists $attr->{pg_enable_utf8}) {
28 0           $attr->{pg_enable_utf8} = 1;
29             }
30 0           $class->SUPER::connect($dsn, $user, $pass, $attr);
31             }
32              
33             package DBIx::Sunny::db;
34             our @ISA = qw(DBI::db);
35              
36 10     10   6213 use DBIx::Sunny::Util qw/bind_and_execute expand_placeholder/;
  10         24  
  10         593  
37 10     10   4091 use DBIx::TransactionManager 0.13;
  10         31082  
  10         296  
38 10     10   61 use Scalar::Util qw/weaken/;
  10         18  
  10         9140  
39              
40             sub connected {
41 0     0     my $dbh = shift;
42 0           my ($dsn, $user, $pass, $attr) = @_;
43 0           $dbh->{RaiseError} = 1;
44 0           $dbh->{PrintError} = 0;
45 0           $dbh->{ShowErrorStatement} = 1;
46 0           $dbh->{AutoInactiveDestroy} = 1;
47 0 0         if ($dsn =~ /^dbi:SQLite:/) {
48 0           $dbh->{sqlite_use_immediate_transaction} = 1;
49 0 0         $dbh->{sqlite_unicode} = 1 unless exists $attr->{sqlite_unicode};
50              
51 0           $dbh->do("PRAGMA journal_mode = WAL");
52 0           $dbh->do("PRAGMA synchronous = NORMAL");
53              
54             }
55 0 0 0       if ($dsn =~ /^dbi:mysql:/ && ! exists $attr->{mysql_enable_utf8} && ! exists $attr->{mysql_enable_utf8mb4} ) {
      0        
56 0           $dbh->{mysql_enable_utf8} = 1;
57 0           $dbh->do("SET NAMES utf8");
58             }
59 0 0         if ($dsn =~ /^dbi:mysql:/) {
60 0           $dbh->{mysql_auto_reconnect} = 0;
61             }
62 0           $dbh->{private_connect_info} = [@_];
63 0           $dbh->SUPER::connected(@_);
64             }
65              
66 0     0     sub connect_info { $_[0]->{private_connect_info} }
67              
68             sub txn_scope {
69 0     0     my $self = shift;
70 0 0         if ( ! $self->{private_txt_manager} ) {
71 0           $self->{private_txt_manager} = DBIx::TransactionManager->new($self);
72 0           weaken($self->{private_txt_manager}->{dbh});
73             }
74             $self->{private_txt_manager}->txn_scope(
75 0           caller => [caller(0)]
76             );
77             }
78              
79             sub __set_comment {
80 0     0     my $self = shift;
81 0           my $query = shift;
82              
83 0           my $trace;
84 0           my $i = 0;
85 0           while ( my @caller = caller($i) ) {
86 0           my $file = $caller[1];
87 0           $file =~ s!\*/!*\//!g;
88 0           $trace = "/* $file line $caller[2] */";
89 0 0 0       last if $caller[0] ne ref($self) && $caller[0] !~ $SKIP_CALLER_REGEX;
90 0           $i++;
91             }
92 0           $query =~ s! ! $trace !;
93 0           $query;
94             }
95              
96             sub prepare {
97 0     0     my $self = shift;
98 0           my $query = shift;
99 0           $self->SUPER::prepare($self->__set_comment($query), @_);
100             }
101              
102             sub do {
103 0     0     my $self = shift;
104 0           my ($query, $attr, @bind) = @_;
105 0           $self->SUPER::do($self->__set_comment($query), $attr, @bind);
106             }
107              
108             sub fill_arrayref {
109 0     0     my $self = shift;
110 0           return expand_placeholder(@_);
111             }
112              
113             sub __prepare_and_execute {
114 0     0     my $self = shift;
115 0           my ($query, @bind) = expand_placeholder(@_);
116 0           my $sth = $self->prepare($query);
117 0           my $ret = bind_and_execute($sth, @bind);
118 0           return ($sth, $ret);
119             }
120              
121             sub select_one {
122 0     0     my $self = shift;
123 0           my ($sth, $ret) = $self->__prepare_and_execute(@_);
124 0   0       my $row = $ret && $sth->fetchrow_arrayref;
125 0 0         return unless $row;
126 0           return $row->[0];
127             }
128              
129             sub select_row {
130 0     0     my $self = shift;
131 0           my ($sth, $ret) = $self->__prepare_and_execute(@_);
132 0   0       my $row = $ret && $sth->fetchrow_hashref;
133 0 0         return unless $row;
134 0           return $row;
135             }
136              
137             sub select_all {
138 0     0     my $self = shift;
139 0           my ($sth, $ret) = $self->__prepare_and_execute(@_);
140 0   0       my $rows = $ret && $sth->fetchall_arrayref({});
141 0           return $rows;
142             }
143              
144             sub query {
145 0     0     my $self = shift;
146 0           (undef, my $ret) = $self->__prepare_and_execute(@_);
147 0           return $ret;
148             }
149              
150             sub last_insert_id {
151 0     0     my $self = shift;
152 0           my $dsn = $self->connect_info->[0];
153 0 0         if ($dsn =~ /^(?i:dbi):SQLite:/) {
    0          
154 0           return $self->func('last_insert_rowid');
155             }
156             elsif ( $dsn =~ /^(?i:dbi):mysql:/) {
157 0           return $self->{mysql_insertid};
158             }
159 0           $self->SUPER::last_insert_id(@_);
160             }
161              
162             package DBIx::Sunny::st; # statement handler
163             our @ISA = qw(DBI::st);
164              
165             1;
166              
167             __END__
168              
169             =encoding utf8
170              
171             =head1 NAME
172              
173             DBIx::Sunny - Simple DBI wrapper
174              
175             =head1 SYNOPSIS
176              
177             use DBIx::Sunny;
178              
179             my $dbh = DBIx::Sunny->connect(...);
180              
181             # or
182              
183             use DBI;
184              
185             my $dbh = DBI->connect(.., {
186             RootClass => 'DBIx::Sunny',
187             PrintError => 0,
188             RaiseError => 1,
189             });
190              
191             =head1 DESCRIPTION
192              
193             DBIx::Sunny is a simple DBI wrapper. It provides better usability for you. This module based on Amon2::DBI.
194             DBIx::Sunny supports only SQLite and MySQL.
195              
196             =head1 FEATURES
197              
198             =over 4
199              
200             =item Set AutoInactiveDestroy to true.
201              
202             DBIx::Sunny sets AutoInactiveDestroy as true.
203              
204             =item [SQLite/MySQL/Pg] Auto encode/decode UTF-8
205              
206             DBIx::Sunny sets sqlite_unicode, mysql_enable_utf8 and pg_enable_utf8 automatically.
207              
208             =item [SQLite] Performance tuning
209              
210             DBIx::Sunny sets sqlite_use_immediate_transaction to true, and executes these PRAGMA statements
211              
212             PRAGMA journal_mode = WAL
213             PRAGMA synchronous = NORMAL
214              
215             =item Nested transaction management.
216              
217             DBIx::Sunny supports nested transaction management based on RAII like DBIx::Class or DBIx::Skinny. It uses L<DBIx::TransactionManager> internally.
218              
219             =item Error Handling
220              
221             DBIx::Sunny sets RaiseError and ShowErrorStatement as true. DBIx::Sunny raises exception and shows current statement if your $dbh occurred exception.
222              
223             =item SQL comment
224              
225             DBIx::Sunny adds file name and line number as SQL comment that invokes SQL statement.
226              
227             =item Easy access to last_insert_id
228              
229             DBIx::Sunny's last_insert_id needs no arguments. It's shortcut for mysql_insertid or last_insert_rowid.
230              
231             =item Auto expanding arrayref bind parameters
232              
233             select_(one|row|all) and query methods support auto-expanding arrayref bind parameters.
234              
235             $dbh->select_all('SELECT * FROM id IN (?)', [1 2 3])
236             #SQL: 'SELECT * FROM id IN (?,?,?)'
237             #@BIND: (1, 2, 3)
238              
239             =item Named placeholder
240              
241             select_(one|row|all) and query methods support named placeholder.
242              
243             $dbh->select_all('SELECT * FROM users WHERE id IN (:ids) AND status = :status', {
244             ids => [1,2,3],
245             status => 'active',
246             });
247             #SQL: 'SELECT * FROM users WHERE id IN (?,?,?) AND status = ?'
248             #@BIND: (1, 2, 3, 'active')
249              
250             =item Typed bind parameters
251              
252             DBIx::Sunny allows you to specify data types of bind parameters. If a bind parameter is L<SQL::Maker::SQLType> object, its value is passed as its type, otherwise it is passed as default type (VARCHAR).
253              
254             use SQL::Maker::SQLType qw/sql_type/;
255             use DBI qw/:sql_types/
256              
257             $dbh->query(
258             'INSERT INTO bin_table (bin_col) VALUES (?)',
259             sql_type(\"\xDE\xAD\xBE\xEF", SQL_BINARY)),
260             );
261              
262             =back
263              
264             =head1 ADDITIONAL METHODS
265              
266             =over 4
267              
268             =item C<< $col = $dbh->select_one($query, @bind); >>
269              
270             Shortcut for prepare, execute and fetchrow_arrayref->[0]
271              
272             =item C<< $row = $dbh->select_row($query, @bind); >>
273              
274             Shortcut for prepare, execute and fetchrow_hashref
275              
276             =item C<< $rows = $dbh->select_all($query, @bind); >>
277              
278             Shortcut for prepare, execute and C<< selectall_arrayref(.., { Slice => {} }, ..) >>
279              
280             =item C<< $dbh->query($query, @bind); >>
281              
282             Shortcut for prepare, execute.
283              
284             =back
285              
286             =head1 AUTHOR
287              
288             Masahiro Nagano E<lt>kazeburo KZBRKZBR@ gmail.comE<gt>
289              
290             =head1 SEE ALSO
291              
292             L<DBI>, L<Amon2::DBI>
293              
294             =head1 LICENSE
295              
296             Copyright (C) Masahiro Nagano
297              
298             This library is free software; you can redistribute it and/or modify
299             it under the same terms as Perl itself.
300              
301             =cut