2015-12-27 18:11 GMT+01:00 Yuriy M. Kaminskiy <yumkam at gmail.com>:
> (I know, I'm a bit late for discussion, but...)
>
> Cecil Westerhof <cldwesterhof at gmail.com>
> writes:
>
> > 2015-12-14 12:40 GMT+01:00 Clemens Ladisch <clemens at ladisch.de>:
> >
> >> Cecil Westerhof wrote:
> >> > I have a crontab job which uses a SQLite database. Sometimes this
> database
> >> > is locked because I used SQLite DB Browser, but did not Write or
> Revert
> >> > Changes. It looks like that when a database is locked there is a file
> with
> >> > the database name with -journal appended to it. Can I count on this?
> >>
> >> The -journal file exists when there is some changed data that might
> >> need to be rolled back. It is possible for the DB to be locked before
> >> some changed data is actually written.
> >>
> >> In WAL mode, there is no journal. (And if your job is read only, it
> >> then would not be blocked by concurrent writes.)
> >>
> >
> > ?No, I need to write also.?
>
> Still, if read-only concurrent transactions are more likely, it will get
> rid of (some) waiting on locks.
>
?I know and in a program where I am only reading there is no problem.
?
> >> > Then I could write a script to warn me about the lock.
> >>
> >> You could use SQLite to check whether the DB is locked:
> >>
> >> if ! sqlite3 my.db "begin immediate"; then
> >> echo "is locked"
> >> fi
> >>
> >
> > ?Thanks, I improved a little on it:
> > sqlite3 "${DATABASE}" "begin immediate" 2>/dev/null
>
> ***[1]
>
> > errorCode="${?}"
> > if [[ "${errorCode}" -eq 5 ]] ; then
> > printf "${DATABASE} is locked\n"
> > elif [[ "${errorCode}" -ne 0 ]] ; then
> > printf "Error ${errorCode} while accessing ${DATABASE}\n"
> > else
> > printf "${DATABASE} is free\n"
> > fi
>
> ***[2]
>
> This check is racy and must not be used (What will happen if you will
> start concurrent queries in DB Browser somewhere between [1] and [2]?
> Right,
> this check won't detect database is locked, but your later (actual) code
> *will* fail on attempt to lock).
>
?You did not read the problem correctly. The program is only needed to
check if a database is inadvertently locked. In this way I can be notified,
so that the database is not locked when the cron job is going to send
proverbs and need to change the database.
?
> 1) You should set reasonable sqlite3_busy_timeout (e.g. with `sqlite3
> -cmd '.timeout 120000' ...` [for 2 minutes timeout]). By default, it is 0,
> so even if database is only shortly locked, it will still fail, even though
> waiting a bit would help.
>
?My experience is that there is a wait of around two seconds. At least in
Bash, Python and Java.
?
> 2b) If you cannot express your DB change as static SQL statement(s)
> (e.g., if some statements use results obtained in previous SELECT
> queries, so you cannot feed your whole SQL code into single sqlite3
> invocation), you should use SQLite binding to some real program language
> (perl, tcl, python,...) instead of sh and sqlite3.
?Well, for just checking if a database is locked I think it is good enough.
But I also wrote a Java program for it:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
import java.sql.SQLException;
public class CheckProverbsLocked {
// We use this class as a program,
// so we make sure the class cannot be initiated
private CheckProverbsLocked() {
}
// Check if the database is locked.
// If it is give message and return 1
// On other error give message about the error and return 2
// Otherwise no message and return 0
public static void main(String [] args) throws Exception {
Connection conn;
Statement stmt;
Class.forName("org.sqlite.JDBC");
conn =
DriverManager.getConnection("jdbc:sqlite:proverbs.sqlite");
stmt = conn.createStatement();
try {
stmt.executeUpdate("BEGIN IMMEDIATE");
} catch (SQLException e) {
// In a stand-alone program the next two statements are not
necessary,
// but I prefer well written code, so I use them
stmt.close();
conn.close();
if (e.getMessage().equals("database is locked")) {
System.out.println("The database is locked.");
System.exit(1);
} else {
System.err.printf("Unexpected error: %s\n",
e.getMessage());
System.exit(2);
}
}
// In a stand-alone program these statements are not necessary,
// but I prefer well written code, so I use them
stmt.executeUpdate("ROLLBACK");
stmt.close();
conn.close();
}
}
?
?The reason I use getMessage instead of getErrorCode is that getErrorCode
return 0 instead of 5.?
I should generalise it, so it can be used for any SQLite database.
--
Cecil Westerhof