db46ce28758f496a494d5c49e549a0443774c0bf
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Storage / DBI / SQLite.pm
1 package DBIx::Class::Storage::DBI::SQLite;
2
3 use strict;
4 use warnings;
5
6 use base qw/DBIx::Class::Storage::DBI/;
7 use mro 'c3';
8
9 use DBIx::Class::Carp;
10 use Try::Tiny;
11 use namespace::clean;
12
13 __PACKAGE__->sql_maker_class('DBIx::Class::SQLMaker::SQLite');
14 __PACKAGE__->sql_limit_dialect ('LimitOffset');
15 __PACKAGE__->sql_quote_char ('"');
16 __PACKAGE__->datetime_parser_type ('DateTime::Format::SQLite');
17
18 =head1 NAME
19
20 DBIx::Class::Storage::DBI::SQLite - Automatic primary key class for SQLite
21
22 =head1 SYNOPSIS
23
24   # In your table classes
25   use base 'DBIx::Class::Core';
26   __PACKAGE__->set_primary_key('id');
27
28 =head1 DESCRIPTION
29
30 This class implements autoincrements for SQLite.
31
32 =head2 Known Issues
33
34 =over
35
36 =item RT79576
37
38  NOTE - This section applies to you only if ALL of these are true:
39
40   * You are or were using DBD::SQLite with a version lesser than 1.38_01
41
42   * You are or were using DBIx::Class versions between 0.08191 and 0.08209
43     (inclusive) or between 0.08240-TRIAL and 0.08242-TRIAL (also inclusive)
44
45   * You use objects with overloaded stringification and are feeding them
46     to DBIC CRUD methods directly
47
48 An unfortunate chain of events led to DBIx::Class silently hitting the problem
49 described in L<RT#79576|https://rt.cpan.org/Public/Bug/Display.html?id=79576>.
50
51 In order to trigger the bug condition one needs to supply B<more than one>
52 bind value that is an object with overloaded stringification (nummification
53 is not relevant, only stringification is). When this is the case the internal
54 DBIx::Class call to C<< $sth->bind_param >> would be executed in a way that
55 triggers the above-mentioned DBD::SQLite bug. As a result all the logs and
56 tracers will contain the expected values, however SQLite will receive B<all>
57 these bind positions being set to the value of the B<last> supplied
58 stringifiable object.
59
60 Even if you upgrade DBIx::Class (which works around the bug starting from
61 version 0.08210) you may still have corrupted/incorrect data in your database.
62 DBIx::Class will currently detect when this condition (more than one
63 stringifiable object in one CRUD call) is encountered and will issue a warning
64 pointing to this section. This warning will be removed 2 years from now,
65 around April 2015, You can disable it after you've audited your data by
66 setting the C<DBIC_RT79576_NOWARN> environment variable. Note - the warning
67 is emited only once per callsite per process and only when the condition in
68 question is encountered. Thus it is very unlikey that your logsystem will be
69 flooded as a result of this.
70
71 =back
72
73 =head1 METHODS
74
75 =cut
76
77 sub backup {
78
79   require File::Spec;
80   require File::Copy;
81   require POSIX;
82
83   my ($self, $dir) = @_;
84   $dir ||= './';
85
86   ## Where is the db file?
87   my $dsn = $self->_dbi_connect_info()->[0];
88
89   my $dbname = $1 if($dsn =~ /dbname=([^;]+)/);
90   if(!$dbname)
91   {
92     $dbname = $1 if($dsn =~ /^dbi:SQLite:(.+)$/i);
93   }
94   $self->throw_exception("Cannot determine name of SQLite db file")
95     if(!$dbname || !-f $dbname);
96
97 #  print "Found database: $dbname\n";
98 #  my $dbfile = file($dbname);
99   my ($vol, $dbdir, $file) = File::Spec->splitpath($dbname);
100 #  my $file = $dbfile->basename();
101   $file = POSIX::strftime("%Y-%m-%d-%H_%M_%S", localtime()) . $file;
102   $file = "B$file" while(-f $file);
103
104   mkdir($dir) unless -f $dir;
105   my $backupfile = File::Spec->catfile($dir, $file);
106
107   my $res = File::Copy::copy($dbname, $backupfile);
108   $self->throw_exception("Backup failed! ($!)") if(!$res);
109
110   return $backupfile;
111 }
112
113 sub _exec_svp_begin {
114   my ($self, $name) = @_;
115
116   $self->_dbh->do("SAVEPOINT $name");
117 }
118
119 sub _exec_svp_release {
120   my ($self, $name) = @_;
121
122   $self->_dbh->do("RELEASE SAVEPOINT $name");
123 }
124
125 sub _exec_svp_rollback {
126   my ($self, $name) = @_;
127
128   # For some reason this statement changes the value of $dbh->{AutoCommit}, so
129   # we localize it here to preserve the original value.
130   local $self->_dbh->{AutoCommit} = $self->_dbh->{AutoCommit};
131
132   $self->_dbh->do("ROLLBACK TRANSACTION TO SAVEPOINT $name");
133 }
134
135 sub _ping {
136   my $self = shift;
137
138   # Be extremely careful what we do here. SQLite is notoriously bad at
139   # synchronizing its internal transaction state with {AutoCommit}
140   # https://metacpan.org/source/ADAMK/DBD-SQLite-1.37/lib/DBD/SQLite.pm#L921
141   # There is a function http://www.sqlite.org/c3ref/get_autocommit.html
142   # but DBD::SQLite does not expose it (nor does it seem to properly use it)
143
144   # Therefore only execute a "ping" when we have no other choice *AND*
145   # scrutinize the thrown exceptions to make sure we are where we think we are
146   my $dbh = $self->_dbh or return undef;
147   return undef unless $dbh->FETCH('Active');
148   return undef unless $dbh->ping;
149
150   # since we do not have access to sqlite3_get_autocommit(), do a trick
151   # to attempt to *safely* determine what state are we *actually* in.
152   # FIXME
153   # also using T::T here leads to bizarre leaks - will figure it out later
154   my $really_not_in_txn = do {
155     local $@;
156
157     # older versions of DBD::SQLite do not properly detect multiline BEGIN/COMMIT
158     # statements to adjust their {AutoCommit} state. Hence use such a statement
159     # pair here as well, in order to escape from poking {AutoCommit} needlessly
160     # https://rt.cpan.org/Public/Bug/Display.html?id=80087
161     eval {
162       # will fail instantly if already in a txn
163       $dbh->do("-- multiline\nBEGIN");
164       $dbh->do("-- multiline\nCOMMIT");
165       1;
166     } or do {
167       ($@ =~ /transaction within a transaction/)
168         ? 0
169         : undef
170       ;
171     };
172   };
173
174   my $ping_fail;
175
176   # if we were unable to determine this - we may very well be dead
177   if (not defined $really_not_in_txn) {
178     $ping_fail = 1;
179   }
180   # check the AC sync-state
181   elsif ($really_not_in_txn xor $dbh->{AutoCommit}) {
182     carp_unique (sprintf
183       'Internal transaction state of handle %s (apparently %s a transaction) does not seem to '
184     . 'match its AutoCommit attribute setting of %s - this is an indication of a '
185     . 'potentially serious bug in your transaction handling logic',
186       $dbh,
187       $really_not_in_txn ? 'NOT in' : 'in',
188       $dbh->{AutoCommit} ? 'TRUE' : 'FALSE',
189     );
190
191     # it is too dangerous to execute anything else in this state
192     # assume everything works (safer - worst case scenario next statement throws)
193     return 1;
194   }
195   else {
196     # do the actual test
197     $ping_fail = ! try { $dbh->do('SELECT * FROM sqlite_master LIMIT 1'); 1 };
198   }
199
200   if ($ping_fail) {
201     # it is possible to have a proper "connection", and have "ping" return
202     # false anyway (e.g. corrupted file). In such cases DBD::SQLite still
203     # keeps the actual file handle open. We don't really want this to happen,
204     # so force-close the handle via DBI itself
205     #
206     local $@; # so that we do not clober the real error as set above
207     eval { $dbh->disconnect }; # if it fails - it fails
208     return undef # the actual RV of _ping()
209   }
210   else {
211     return 1;
212   }
213 }
214
215 sub deployment_statements {
216   my $self = shift;
217   my ($schema, $type, $version, $dir, $sqltargs, @rest) = @_;
218
219   $sqltargs ||= {};
220
221   if (
222     ! exists $sqltargs->{producer_args}{sqlite_version}
223       and
224     my $dver = $self->_server_info->{normalized_dbms_version}
225   ) {
226     $sqltargs->{producer_args}{sqlite_version} = $dver;
227   }
228
229   $self->next::method($schema, $type, $version, $dir, $sqltargs, @rest);
230 }
231
232 sub bind_attribute_by_data_type {
233   $_[1] =~ /^ (?: int(?:eger)? | (?:tiny|small|medium)int ) $/ix
234     ? DBI::SQL_INTEGER()
235     : undef
236   ;
237 }
238
239 # DBD::SQLite (at least up to version 1.31 has a bug where it will
240 # non-fatally nummify a string value bound as an integer, resulting
241 # in insertions of '0' into supposed-to-be-numeric fields
242 # Since this can result in severe data inconsistency, remove the
243 # bind attr if such a sitation is detected
244 #
245 # FIXME - when a DBD::SQLite version is released that eventually fixes
246 # this sutiation (somehow) - no-op this override once a proper DBD
247 # version is detected
248 sub _dbi_attrs_for_bind {
249   my ($self, $ident, $bind) = @_;
250
251   my $bindattrs = $self->next::method($ident, $bind);
252
253   # an attempt to detect former effects of RT#79576, bug itself present between
254   # 0.08191 and 0.08209 inclusive (fixed in 0.08210 and higher)
255   my $stringifiable = 0;
256
257   for (0.. $#$bindattrs) {
258
259     $stringifiable++ if ( length ref $bind->[$_][1] and overload::Method($bind->[$_][1], '""') );
260
261     if (
262       defined $bindattrs->[$_]
263         and
264       defined $bind->[$_][1]
265         and
266       $bindattrs->[$_] eq DBI::SQL_INTEGER()
267         and
268       $bind->[$_][1] !~ /^ [\+\-]? [0-9]+ (?: \. 0* )? $/x
269     ) {
270       carp_unique( sprintf (
271         "Non-integer value supplied for column '%s' despite the integer datatype",
272         $bind->[$_][0]{dbic_colname} || "# $_"
273       ) );
274       undef $bindattrs->[$_];
275     }
276   }
277
278   carp_unique(
279     'POSSIBLE *PAST* DATA CORRUPTION detected - see '
280   . 'DBIx::Class::Storage::DBI::SQLite/RT79576 or '
281   . 'http://v.gd/DBIC_SQLite_RT79576 for further details or set '
282   . '$ENV{DBIC_RT79576_NOWARN} to disable this warning. Trigger '
283   . 'condition encountered'
284   ) if (!$ENV{DBIC_RT79576_NOWARN} and $stringifiable > 1);
285
286   return $bindattrs;
287 }
288
289 =head2 connect_call_use_foreign_keys
290
291 Used as:
292
293     on_connect_call => 'use_foreign_keys'
294
295 In L<connect_info|DBIx::Class::Storage::DBI/connect_info> to turn on foreign key
296 (including cascading) support for recent versions of SQLite and L<DBD::SQLite>.
297
298 Executes:
299
300   PRAGMA foreign_keys = ON
301
302 See L<http://www.sqlite.org/foreignkeys.html> for more information.
303
304 =cut
305
306 sub connect_call_use_foreign_keys {
307   my $self = shift;
308
309   $self->_do_query(
310     'PRAGMA foreign_keys = ON'
311   );
312 }
313
314 1;
315
316 =head1 AUTHOR AND CONTRIBUTORS
317
318 See L<AUTHOR|DBIx::Class/AUTHOR> and L<CONTRIBUTORS|DBIx::Class/CONTRIBUTORS> in DBIx::Class
319
320 =head1 LICENSE
321
322 You may distribute this code under the same terms as Perl itself.
323
324 =cut