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