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