DBD-SQLcipher
view release on metacpan or search on metacpan
See <http://www.sqlite.org/foreignkeys.html> for details.
Pragma
SQLcipher has a set of "Pragma"s to modifiy its operation or to query for
its internal data. These are specific to SQLcipher and are not likely to
work with other DBD libraries, but you may find some of these are quite
useful. DBD::SQLcipher actually sets some (like "show_datatypes") for you
when you connect to a database. See <http://www.sqlite.org/pragma.html>
for details.
Transactions
DBI/DBD::SQLcipher's transactions may be a bit confusing. They behave
differently according to the status of the "AutoCommit" flag:
When the AutoCommit flag is on
You're supposed to always use the auto-commit mode, except you
explicitly begin a transaction, and when the transaction ended,
you're supposed to go back to the auto-commit mode. To begin a
transaction, call "begin_work" method, or issue a "BEGIN" statement.
To end it, call "commit/rollback" methods, or issue the
corresponding statements.
$dbh->{AutoCommit} = 1;
$dbh->begin_work; # or $dbh->do('BEGIN TRANSACTION');
# $dbh->{AutoCommit} is turned off temporarily during a transaction;
$dbh->commit; # or $dbh->do('COMMIT');
# $dbh->{AutoCommit} is turned on again;
When the AutoCommit flag is off
You're supposed to always use the transactional mode, until you
explicitly turn on the AutoCommit flag. You can explicitly issue a
"BEGIN" statement (only when an actual transaction has not begun
yet) but you're not allowed to call "begin_work" method (if you
don't issue a "BEGIN", it will be issued internally). You can commit
or roll it back freely. Another transaction will automatically
begins if you execute another statement.
$dbh->{AutoCommit} = 0;
# $dbh->do('BEGIN TRANSACTION') is not necessary, but possible
...
$dbh->commit; # or $dbh->do('COMMIT');
# $dbh->{AutoCommit} stays intact;
$dbh->{AutoCommit} = 1; # ends the transactional mode
This "AutoCommit" mode is independent from the autocommit mode of the
internal SQLcipher library, which always begins by a "BEGIN" statement, and
ends by a "COMMIT" or a <ROLLBACK>.
Transaction and Database Locking
Transaction by "AutoCommit" or "begin_work" is nice and handy, but
sometimes you may get an annoying "database is locked" error. This
typically happens when someone begins a transaction, and tries to write
to a database while other person is reading from the database (in
another transaction). You might be surprised but SQLcipher doesn't lock a
database when you just begin a normal (deferred) transaction to maximize
concurrency. It reserves a lock when you issue a statement to write, but
until you actually try to write with a "commit" statement, it allows
other people to read from the database. However, reading from the
database also requires "shared lock", and that prevents to give you the
"exclusive lock" you reserved, thus you get the "database is locked"
error, and other people will get the same error if they try to write
afterwards, as you still have a "pending" lock. "busy_timeout" doesn't
help in this case.
To avoid this, set a transaction type explicitly. You can issue a "begin
immediate transaction" (or "begin exclusive transaction") for each
transaction, or set "sqlite_use_immediate_transaction" database handle
attribute to true (since 1.30_02) to always use an immediate transaction
(even when you simply use "begin_work" or turn off the "AutoCommit".).
my $dbh = DBI->connect("dbi:SQLcipher::memory:", "", "", {
sqlite_use_immediate_transaction => 1,
});
Note that this works only when all of the connections use the same
(non-deferred) transaction. See <http://sqlite.org/lockingv3.html> for
locking details.
"$sth->finish" and Transaction Rollback
As the DBI doc says, you almost certainly do not need to call "finish"
in DBI method if you fetch all rows (probably in a loop). However, there
are several exceptions to this rule, and rolling-back of an unfinished
"SELECT" statement is one of such exceptional cases.
SQLcipher prohibits "ROLLBACK" of unfinished "SELECT" statements in a
transaction (See <http://sqlite.org/lang_transaction.html> for details).
So you need to call "finish" before you issue a rollback.
$sth = $dbh->prepare("SELECT * FROM t");
$dbh->begin_work;
eval {
$sth->execute;
$row = $sth->fetch;
...
die "For some reason";
...
};
if($@) {
$sth->finish; # You need this for SQLcipher
$dbh->rollback;
} else {
$dbh->commit;
}
Processing Multiple Statements At A Time
DBI's statement handle is not supposed to process multiple statements at
a time. So if you pass a string that contains multiple statements (a
"dump") to a statement handle (via "prepare" or "do"), DBD::SQLcipher only
processes the first statement, and discards the rest.
Since 1.30_01, you can retrieve those ignored (unprepared) statements
via "$sth->{sqlite_unprepared_statements}". It usually contains nothing
but white spaces, but if you really care, you can check this attribute
to see if there's anything left undone. Also, if you set a
"sqlite_allow_multiple_statements" attribute of a database handle to
true when you connect to a database, "do" method automatically checks
the "sqlite_unprepared_statements" attribute, and if it finds anything
undone (even if what's left is just a single white space), it repeats
the process again, to the end.
( run in 0.965 second using v1.01-cache-2.11-cpan-140bd7fdf52 )