← Back to team overview

zeitgeist team mailing list archive

[Branch ~zeitgeist/zeitgeist/bluebird] Rev 372: Automatically recover from corrupt database and be more clear

 

Merge authors:
  Siegfried Gevatter (rainct)
------------------------------------------------------------
revno: 372 [merge]
committer: Siegfried-Angel Gevatter Pujals <siegfried@xxxxxxxxxxxx>
branch nick: zeitgeist
timestamp: Wed 2012-01-25 14:24:34 +0100
message:
  Automatically recover from corrupt database and be more clear
  on some other errors.
modified:
  doc/zeitgeist-daemon.1
  src/errors.vala
  src/sql-schema.vala
  src/sql.vala
  src/utils.vala
  src/zeitgeist-daemon.vala


--
lp:zeitgeist
https://code.launchpad.net/~zeitgeist/zeitgeist/bluebird

Your team Zeitgeist Framework Team is subscribed to branch lp:zeitgeist.
To unsubscribe from this branch go to https://code.launchpad.net/~zeitgeist/zeitgeist/bluebird/+edit-subscription
=== modified file 'doc/zeitgeist-daemon.1'
--- doc/zeitgeist-daemon.1	2012-01-02 19:31:15 +0000
+++ doc/zeitgeist-daemon.1	2012-01-25 13:24:34 +0000
@@ -87,6 +87,12 @@
 .TP
 .B 10
 There is already a running Zeitgeist instance.
+.TP
+.B 21
+Could not access the database file.
+.TP
+.B 22
+The database file is locked.
 
 .SH SEE ALSO
 \fBzeitgeist-datahub\fR, \fBgnome-activity-journal\fR

=== modified file 'src/errors.vala'
--- src/errors.vala	2011-10-20 13:32:51 +0000
+++ src/errors.vala	2012-01-25 10:36:27 +0000
@@ -23,10 +23,15 @@
     [DBus (name = "org.gnome.zeitgeist.EngineError")]
     public errordomain EngineError
     {
+        BACKUP_FAILED,
+        DATABASE_BUSY,
+        DATABASE_CANTOPEN,
+        DATABASE_CORRUPT,
         DATABASE_ERROR,
+        DATABASE_RETIRE_FAILED,
         INVALID_ARGUMENT,
         INVALID_KEY,
-        BACKUP_FAILED,
+        EXISTING_INSTANCE,
     }
 
     // vala doesn't include proper headers, this fixes it

=== modified file 'src/sql-schema.vala'
--- src/sql-schema.vala	2012-01-25 12:28:24 +0000
+++ src/sql-schema.vala	2012-01-25 13:24:34 +0000
@@ -417,8 +417,8 @@
         }
 
         /**
-         * Execute the given SQL. If the query doesn't succeed, log a
-         * critical warning (potentially aborting the program).
+         * Execute the given SQL. If the query doesn't succeed, throw
+         * an error.
          *
          * @param database the database on which to run the query
          * @param sql the SQL query to run
@@ -429,10 +429,17 @@
             int rc = database.exec (sql);
             if (rc != Sqlite.OK)
             {
-                const string fmt_str = "Can't create database: %d, %s\n\n" +
-                    "Unable to execute SQL:\n%s";
-                var err_msg = fmt_str.printf (rc, database.errmsg (), sql);
-                throw new EngineError.DATABASE_ERROR (err_msg);
+                if (rc == Sqlite.CORRUPT)
+                {
+                    throw new EngineError.DATABASE_CORRUPT (database.errmsg ());
+                }
+                else
+                {
+                    const string fmt_str = "Can't create database: %d, %s\n\n" +
+                        "Unable to execute SQL:\n%s";
+                    var err_msg = fmt_str.printf (rc, database.errmsg (), sql);
+                    throw new EngineError.DATABASE_ERROR (err_msg);
+                }
             }
         }
 

=== modified file 'src/sql.vala'
--- src/sql.vala	2012-01-02 19:30:51 +0000
+++ src/sql.vala	2012-01-25 13:24:34 +0000
@@ -67,13 +67,7 @@
 
         public ZeitgeistDatabase () throws EngineError
         {
-            message ("Opening DB from %s", Utils.get_database_file_path ());
-            int rc = Sqlite.Database.open_v2 (
-                Utils.get_database_file_path (),
-                out database);
-            assert_query_success (rc, "Can't open database");
-
-            DatabaseSchema.ensure_schema (database);
+            open_database (true);
 
             prepare_queries ();
 
@@ -82,6 +76,74 @@
             database.update_hook (update_callback);
         }
 
+        private void open_database (bool retry)
+            throws EngineError
+        {
+            int rc = Sqlite.Database.open_v2 (
+                Utils.get_database_file_path (),
+                out database);
+            
+            if (rc == Sqlite.OK)
+            {
+                try
+                {
+                    // Error (like a malformed database) may not be exposed
+                    // until we try to operate on the database.
+                    DatabaseSchema.ensure_schema (database);
+                }
+                catch (EngineError err)
+                {
+                    if (err is EngineError.DATABASE_CORRUPT && retry)
+                        rc = Sqlite.CORRUPT;
+                    else if (err is EngineError.DATABASE_CANTOPEN)
+                        rc = Sqlite.CANTOPEN;
+                    else if (err is EngineError.DATABASE_BUSY)
+                        rc = Sqlite.BUSY;
+                    else
+                        throw err;
+                }
+            }
+            
+            if (rc != Sqlite.OK)
+            {
+                if (rc == Sqlite.CORRUPT && retry)
+                {
+                    // The database disk image is malformed
+                    warning ("It looks like your database is corrupt. " +
+                        "It will be renamed and a new one will be created.");
+                    try
+                    {
+                        Utils.retire_database ();
+                    }
+                    catch (Error err)
+                    {
+                        string message =
+                            "Could not rename database: %s".printf (
+                                err.message);
+                        throw new EngineError.DATABASE_RETIRE_FAILED (message);
+                    }
+                    open_database (false);
+                }
+                else if (rc == Sqlite.PERM || rc == Sqlite.CANTOPEN)
+                {
+                    // Access permission denied / Unable to open database file
+                    throw new EngineError.DATABASE_CANTOPEN (
+                        database.errmsg ());
+                }
+                else if (rc == Sqlite.BUSY)
+                {
+                    // The database file is locked
+                    throw new EngineError.DATABASE_BUSY (database.errmsg ());
+                }
+                else
+                {
+                    string message = "Can't open database: %d, %s".printf(rc,
+                        database.errmsg ());
+                    throw new EngineError.DATABASE_ERROR (message);
+                }
+            }
+        }
+
         public uint32 get_last_id () throws EngineError
         {
             int last_id = -1;

=== modified file 'src/utils.vala'
--- src/utils.vala	2011-10-31 15:28:09 +0000
+++ src/utils.vala	2011-12-31 15:57:15 +0000
@@ -31,7 +31,8 @@
         private static string DATABASE_FILE_BACKUP_PATH;
         private static string LOCAL_EXTENSIONS_PATH;
 
-        public const string ZEITGEIST_DATA_FOLDER = "zeitgeist";
+        public const string DATA_FOLDER = "zeitgeist";
+        public const string DATABASE_BASENAME = "activity.sqlite";
         public const string USER_EXTENSION_PATH = "";
 
         // D-Bus
@@ -48,7 +49,7 @@
 
             DATA_PATH = Environment.get_variable ("ZEITGEIST_DATA_PATH") ??
                 Path.build_filename (Environment.get_user_data_dir (),
-                    ZEITGEIST_DATA_FOLDER);
+                    DATA_FOLDER);
 
             if (!FileUtils.test (DATA_PATH, FileTest.IS_DIR))
             {
@@ -66,7 +67,7 @@
 
             DATABASE_FILE_PATH =
                 Environment.get_variable ("ZEITGEIST_DATABASE_PATH") ??
-                Path.build_filename (get_data_path (), "activity.sqlite");
+                Path.build_filename (get_data_path (), DATABASE_BASENAME);
 
             debug ("DATABASE_FILE_PATH = %s", DATABASE_FILE_PATH);
 
@@ -80,13 +81,20 @@
 
             DATABASE_FILE_BACKUP_PATH =
                 Environment.get_variable ("ZEITGEIST_DATABASE_BACKUP_PATH") ??
-                Path.build_filename (get_data_path (), "activity.sqlite.bck");
+                Path.build_filename (get_data_path (),
+                    DATABASE_BASENAME + ".bck");
 
             debug ("DATABASE_FILE_BACKUP_PATH = %s", DATABASE_FILE_BACKUP_PATH);
 
             return DATABASE_FILE_BACKUP_PATH;
         }
 
+        public string get_database_file_retire_name ()
+        {
+            return DATABASE_BASENAME + ".%s.bck".printf (
+                new DateTime.now_local ().format ("%Y%m%d-%H%M%S"));
+        }
+
         public unowned string get_local_extensions_path ()
         {
             if (LOCAL_EXTENSIONS_PATH != null) return LOCAL_EXTENSIONS_PATH;
@@ -113,6 +121,12 @@
 
             original.copy (destination, FileCopyFlags.OVERWRITE, null, null);
         }
+
+        public void retire_database () throws Error
+        {
+            File dbfile = File.new_for_path (get_database_file_path ());
+            dbfile.set_display_name (get_database_file_retire_name ());
+        }
     }
 }
 

=== modified file 'src/zeitgeist-daemon.vala'
--- src/zeitgeist-daemon.vala	2012-01-02 19:30:51 +0000
+++ src/zeitgeist-daemon.vala	2012-01-25 13:24:34 +0000
@@ -329,6 +329,7 @@
         }
 
         static void run ()
+            throws Error
         {
             DBusConnection connection;
             bool name_owned;
@@ -342,8 +343,7 @@
             }
             catch (IOError err)
             {
-                critical ("%s", err.message);
-                return;
+                throw err;
             }
             if (name_owned)
             {
@@ -353,9 +353,10 @@
                 }
                 else
                 {
-                    critical ("An existing instance was found. Please use " +
+                    warning ("An existing instance was found. Please use " +
                         "--replace to stop it and start a new instance.");
-                    Posix.exit (10);
+                    throw new EngineError.EXISTING_INSTANCE (
+                        "Zeitgeist is running already.");
                 }
             }
 
@@ -370,8 +371,19 @@
             }
             catch (Error err)
             {
-                critical ("%s", err.message);
-                return;
+                if (err is EngineError.DATABASE_CANTOPEN)
+                {
+                    warning ("Could not access the database file.\n" +
+                        "Please check the permissions of file %s.",
+                        Utils.get_database_file_path ());
+                }
+                else if (err is EngineError.DATABASE_BUSY)
+                {
+                    warning ("It looks like another Zeitgeist instance " +
+                        "is already running (the database is locked). " +
+                        "If you want to start a new instance, use --replace.");
+                }
+                throw err;
             }
 
             uint owner_id = Bus.own_name_on_connection (connection,
@@ -474,7 +486,15 @@
             }
             catch (Error err)
             {
+                if (err is EngineError.EXISTING_INSTANCE)
+                    return 10;
+                if (err is EngineError.DATABASE_CANTOPEN)
+                    return 21;
+                if (err is EngineError.DATABASE_BUSY)
+                    return 22;
+
                 warning ("%s", err.message);
+                return 1;
             }
 
             return 0;