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