Logo Search packages:      
Sourcecode: f-spot version File versions

Updater.cs

using Mono.Data.SqliteClient;
using Mono.Unix;
using Gtk;
using System;
using System.Collections;
using Banshee.Database;

namespace FSpot.Database {
      public static class Updater {
            private static ProgressDialog dialog;
            private static Hashtable updates = new Hashtable ();
            private static MetaItem db_version;
            private static Db db;


            public static Version LatestVersion {
                  get {
                        if (updates == null || updates.Count == 0)
                              return new Version (0, 0);
                        ArrayList keys = new ArrayList (updates.Keys);
                        keys.Sort ();
                        return keys[keys.Count - 1] as Version;
                  }
            }
            
            static Updater () {
                  // Update from version 0 to 1: Remove empty Other tags
                  AddUpdate (new Version ("1"), delegate () {
                        string other_id = SelectSingleString ("SELECT id FROM tags WHERE name = 'Other'");

                        if (other_id == null)
                              return;

                        // Don't do anything if there are subtags
                        string tag_count = SelectSingleString (
                              String.Format ("SELECT COUNT(*) FROM tags WHERE category_id = {0}", other_id));
                        
                        if (tag_count == null || System.Int32.Parse (tag_count) != 0)
                              return;
                        
                        // Don't do anything if there are photos tagged with this
                        string photo_count = SelectSingleString (
                              String.Format ("SELECT COUNT(*) FROM photo_tags WHERE tag_id = {0}", other_id));

                        if (photo_count == null || System.Int32.Parse (photo_count) != 0)
                              return;

                        // Finally, we know that the Other tag exists and has no children, so remove it
                        ExecuteNonQuery ("DELETE FROM tags WHERE name = 'Other'");
                  });

                  // Update from version 1 to 2: Restore Other tags that were removed leaving dangling child tags
                  AddUpdate (new Version ("2"), delegate () {
                        string tag_count = SelectSingleString ("SELECT COUNT(*) FROM tags WHERE category_id != 0 AND category_id NOT IN (SELECT id FROM tags)");

                        // If there are no dangling tags, then don't do anything
                        if (tag_count == null || System.Int32.Parse (tag_count) == 0)
                              return;

                        int id = ExecuteScalar ("INSERT INTO tags (name, category_id, is_category, icon) VALUES ('Other', 0, 1, 'stock_icon:f-spot-other.png')");

                        ExecuteNonQuery (String.Format (
                              "UPDATE tags SET category_id = {0} WHERE id IN "            +
                              "(SELECT id FROM tags WHERE category_id != 0 AND category_id "    +
                              "NOT IN (SELECT id FROM tags))",
                              id));

                        System.Console.WriteLine ("Other tag restored.  Sorry about that!");
                  });
                  
                  // Update from version 2 to 3: ensure that Hidden is the only tag left which is a real tag (not category)
                  AddUpdate (new Version ("3"), delegate () {
                        ExecuteNonQuery ("UPDATE tags SET is_category = 1 WHERE name != 'Hidden'");
                  });

                  //Version 3.1, clean old (and unused) items in Export
                  AddUpdate (new Version (3, 1), delegate () {
                        if (TableExists ("exports"))
                              ExecuteScalar ("DELETE FROM exports WHERE export_type='fspot:Folder'");
                  });

                  //Version 4.0, bump the version number to a integer, for backward compatibility
                  AddUpdate (new Version (4, 0), delegate () {});


                  //Version 5.0, add a roll_id field to photos, rename table 'imports' to 'rolls' 
                  //and fix bgo 324425.
                  AddUpdate (new Version (5, 0), delegate () {
                        System.Console.WriteLine ("Will add a roll_id field to photos!");
                        string tmp_photos = MoveTableToTemp ("photos");
                        ExecuteNonQuery (
                              "CREATE TABLE photos (                                     " +
                              "     id                 INTEGER PRIMARY KEY NOT NULL,   " +
                              "       time               INTEGER NOT NULL,             " +
                              "       directory_path     STRING NOT NULL,              " +
                              "       name               STRING NOT NULL,              " +
                              "       description        TEXT NOT NULL,            " +
                              "       roll_id            INTEGER NOT NULL,             " +
                              "       default_version_id INTEGER NOT NULL              " +
                              ")");
                        ExecuteScalar (String.Format("INSERT INTO photos SELECT id, time, directory_path, name, description, 0, default_version_id FROM {0}", tmp_photos));

                        System.Console.WriteLine ("Will rename imports to rolls!");
                        string tmp_rolls = MoveTableToTemp ("imports");
                        ExecuteNonQuery (
                              "CREATE TABLE rolls (                                     " +
                              "     id                 INTEGER PRIMARY KEY NOT NULL,   " +
                              "       time               INTEGER NOT NULL              " +
                              ")");
                        ExecuteScalar (String.Format("INSERT INTO rolls SELECT id, time FROM {0}", tmp_rolls));

                        System.Console.WriteLine ("Cleaning weird descriptions, fixes bug #324425.");
                        ExecuteNonQuery ("UPDATE photos SET description = \"\" WHERE description LIKE \"Invalid size of entry%\"");
                  });                     


                  //Version 6.0, change tag icon f-spot-tag-other to emblem-generic
                  AddUpdate (new Version (6,0), delegate () {
                        ExecuteScalar ("UPDATE tags SET icon = \"stock_icon:emblem-generic\" " +
                                    " WHERE icon LIKE \"stock_icon:f-spot-other.png\"");
                  });

                  //Update to version 7.0, keep photo uri instead of path
                  AddUpdate (new Version (7,0), delegate () {
                        string tmp_photos = MoveTableToTemp ("photos");
                        ExecuteNonQuery ( 
                              "CREATE TABLE photos (" +
                              "     id                 INTEGER PRIMARY KEY NOT NULL," +
                              "       time               INTEGER NOT NULL," +
                              "       uri                STRING NOT NULL," +
                              "       description        TEXT NOT NULL," +
                              "       roll_id            INTEGER NOT NULL," +
                              "       default_version_id INTEGER NOT NULL" +
                              ")");
                        ExecuteNonQuery (String.Format (
                              "INSERT INTO photos (id, time, uri, description, roll_id, default_version_id) " +
                              "SELECT id, time, 'file://' || directory_path || '/' || name,           " +
                              "description, roll_id, default_version_id FROM {0}", tmp_photos));
                  }, true);
                  
                  // Update to version 8.0, store full version uri
                  AddUpdate (new Version (8,0),delegate () {
                        string tmp_versions = MoveTableToTemp ("photo_versions");
                        ExecuteNonQuery (
                              "CREATE TABLE photo_versions (          " +
                              "       photo_id        INTEGER,        " +
                              "       version_id      INTEGER,        " +
                              "       name            STRING,         " +
                              "       uri             STRING NOT NULL " +
                              ")");

                        SqliteDataReader reader = ExecuteReader (String.Format (
                                    "SELECT photo_id, version_id, name, uri " +
                                    "FROM {0}, photos " +
                                    "WHERE photo_id = id ", tmp_versions));
            
                        while (reader.Read ()) {
                              System.Uri photo_uri = new System.Uri (reader [3] as string);
                              string name_without_extension = System.IO.Path.GetFileNameWithoutExtension (photo_uri.AbsolutePath);
                              string extension = System.IO.Path.GetExtension (photo_uri.AbsolutePath);

                              string uri = photo_uri.Scheme + "://" + 
                                    photo_uri.Host + 
                                    System.IO.Path.GetDirectoryName (photo_uri.AbsolutePath) + "/" +
                                    name_without_extension + " (" + (reader [2]).ToString () + ")" + extension;

                              ExecuteNonQuery (new DbCommand (
                                    "INSERT INTO photo_versions (photo_id, version_id, name, uri) " +
                                    "VALUES (:photo_id, :version_id, :name, :uri)",
                                    "photo_id", Convert.ToUInt32 (reader [0]),
                                    "version_id", Convert.ToUInt32 (reader [1]),
                                    "name", (reader [2]).ToString (),
                                    "uri", uri));
                        }

                  }, true);
                  
                  // Update to version 9.0
                  AddUpdate (new Version (9,0),delegate () {
                        string tmp_versions = MoveTableToTemp ("photo_versions");
                        ExecuteNonQuery (
                              "CREATE TABLE photo_versions (          " +
                              "       photo_id        INTEGER,        " +
                              "       version_id      INTEGER,        " +
                              "       name            STRING,         " +
                              "       uri             STRING NOT NULL," +
                              "     protected   BOOLEAN           " +
                              ")");
                        ExecuteNonQuery (String.Format (
                              "INSERT INTO photo_versions (photo_id, version_id, name, uri, protected) " +
                              "SELECT photo_id, version_id, name, uri, 0 " +
                              "FROM {0} ", tmp_versions));
                  });

                  // Update to version 10.0, make id autoincrement
                  AddUpdate (new Version (10,0),delegate () {
                        string tmp_photos = MoveTableToTemp ("photos");
                        ExecuteNonQuery (
                              "CREATE TABLE photos (                                     " +
                              "     id                 INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, " +
                              "     time               INTEGER NOT NULL,               " +
                              "     uri            STRING NOT NULL,              " +
                              "     description        TEXT NOT NULL,              " +
                              "     roll_id            INTEGER NOT NULL,               " +
                              "     default_version_id INTEGER NOT NULL          " +
                              ")");
 
                        ExecuteNonQuery (String.Format (
                              "INSERT INTO photos (id, time, uri, description, roll_id, default_version_id) " +
                              "SELECT id, time, uri, description, roll_id, default_version_id  " + 
                              "FROM  {0} ", tmp_photos));
                  }, false);
 
                  // Update to version 11.0, rating
                  AddUpdate (new Version (11,0),delegate () {
                        string tmp_photos = MoveTableToTemp ("photos");
                        Execute (
                              "CREATE TABLE photos (                                     " +
                              "     id                 INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, " +
                              "     time               INTEGER NOT NULL,               " +
                              "     uri            STRING NOT NULL,              " +
                              "     description        TEXT NOT NULL,              " +
                              "     roll_id            INTEGER NOT NULL,               " +
                              "     default_version_id INTEGER NOT NULL,               " +
                              "       rating             INTEGER NULL                  " +
                              ")");
 
                        Execute (String.Format (
                              "INSERT INTO photos (id, time, uri, description, roll_id, default_version_id, rating) " +
                              "SELECT id, time, uri, description, roll_id, default_version_id, null  " + 
                              "FROM  {0} ", tmp_photos));
                  });

                  //Update to version 12.0, remove dead associations, bgo #507950, #488545
                  AddUpdate (new Version (12, 0), delegate () {
                        Execute ("DELETE FROM photo_tags WHERE tag_id NOT IN (SELECT id FROM tags)");
                  });
                  
                  // Update to version 13.0
                  AddUpdate (new Version (13,0), delegate () {
                        Execute ("UPDATE photos SET rating = 0 WHERE rating IS NULL");
                  });

                  // Update to version 14.0
                  AddUpdate (new Version (14,0), delegate () {
                        Execute ("UPDATE photos SET rating = 0 WHERE rating IS NULL");
                  });

                  // Update to version 14.0
                  //AddUpdate (new Version (14,0),delegate () {
                  //    do update here
                  //});
            }

            public static void Run (Db database)
            {
                  db = database;
                  db_version = db.Meta.DatabaseVersion;

                  if (updates.Count == 0)
                        return;

                  Version current_version = new Version (db_version.Value);

                  if (current_version == LatestVersion)
                        return;
                  else if (current_version > LatestVersion) {
                        Console.WriteLine ("The existing database version is more recent than this version of F-Spot expects.");
                        return;
                  }

                  Console.WriteLine ("Updating F-Spot Database");

                  // Only create and show the dialog if one or more of the updates to be done is
                  // marked as being slow
                  bool slow = false;
                  foreach (Version version in updates.Keys) {
                        if (version > current_version && (updates[version] as Update).IsSlow)
                              slow = true;
                              break;
                  }

                  if (slow) {
                        dialog = new ProgressDialog (Catalog.GetString ("Updating F-Spot Database"), ProgressDialog.CancelButtonType.None, 0, null);
                        dialog.Message.Text = Catalog.GetString ("Please wait while your F-Spot gallery's database is updated. This may take some time.");
                        dialog.Bar.Fraction = 0.0;
                        dialog.Modal = false;
                        dialog.SkipTaskbarHint = true;
                        dialog.WindowPosition = WindowPosition.Center;
                        dialog.ShowAll ();
                        dialog.Present ();
                        dialog.QueueDraw ();
                  }

                  db.BeginTransaction ();
                  try {
                        ArrayList keys = new ArrayList (updates.Keys);
                        keys.Sort ();
                        foreach (Version version in keys) {
                              if (version <= current_version)
                                    continue;

                              Pulse ();
                              (updates[version] as Update).Execute (db, db_version);
                        }

                        db.CommitTransaction ();
                  } catch (Exception e) {
                        Console.WriteLine ("Rolling back database changes because of Exception");
                        // There was an error, roll back the database
                        db.RollbackTransaction ();

                        // Pass the exception on, this is fatal
                        throw e;
                  }
                  
                  if (dialog != null)
                        dialog.Destroy ();
                  
                  if (new Version(db_version.Value) == LatestVersion)
                        Console.WriteLine ("Database updates completed successfully.");
            }
            
            private static void AddUpdate (Version version, UpdateCode code)
            {
                  AddUpdate (version, code, false);
            }
            
            private static void AddUpdate (Version version, UpdateCode code, bool is_slow)
            {
                  updates[version] = new Update (version, code, is_slow);
            }

            public static void Pulse ()
            {
                  if (dialog != null) {
                        dialog.Bar.Pulse ();
                        dialog.ShowAll ();
                  }
            }

            private static int Execute (string statement)
            {
                  return db.Database.Execute (statement);
            }

            private static int Execute (DbCommand command)
            {
                  return db.Database.Execute (command);
            }

            private static void ExecuteNonQuery (string statement)
            {
                  db.Database.ExecuteNonQuery(statement);
            }

            private static void ExecuteNonQuery (DbCommand command)
            {
                  db.Database.ExecuteNonQuery(command);
            }
            
            private static int ExecuteScalar (string statement)
            {
                  return db.Database.Execute(statement);
            }

            private static SqliteDataReader ExecuteReader (string statement)
            {
                  return db.Database.Query (statement);
            }
            
            private static bool TableExists (string table)
            {
                  return db.Database.TableExists (table);
            }

            private static string SelectSingleString (string statement)
            {
                  string result = null;

                  try {
                        result = (string)db.Database.QuerySingle(statement);
                  } catch (Exception) {}

                  return result;
            }

            private static string MoveTableToTemp (string table_name)
            {
                  string temp_name = table_name + "_temp";
                  
                  // Get the table definition for the table we are copying
                  string sql = SelectSingleString (String.Format ("SELECT sql FROM sqlite_master WHERE tbl_name = '{0}' ORDER BY type DESC", table_name));
                  
                  // Drop temp table if already exists
                  ExecuteNonQuery ("DROP TABLE IF EXISTS " + temp_name);

                  // Change the SQL to create the temp table
                  ExecuteNonQuery (sql.Replace ("CREATE TABLE " + table_name, "CREATE TEMPORARY TABLE " + temp_name));

                  // Copy the data
                  ExecuteScalar (String.Format ("INSERT INTO {0} SELECT * FROM {1}", temp_name, table_name));
                        
                  // Delete the original table
                  ExecuteNonQuery ("DROP TABLE " + table_name);

                  return temp_name;
            }

            private delegate void UpdateCode ();

            private class Update {
                  public Version Version;
                  private UpdateCode code;
                  public bool IsSlow = false;
                  
                  public Update (Version to_version, UpdateCode code, bool slow)
                  {
                        this.Version = to_version;
                        this.code = code;
                        IsSlow = slow;
                  }

                  public Update (Version to_version, UpdateCode code)
                  {
                        this.Version = to_version;
                        this.code = code;
                  }

                  public void Execute (Db db, MetaItem db_version)
                  {
                        code ();
                        
                        Console.WriteLine ("Updated database from version {0} to {1}",
                                    db_version.Value,
                                    Version.ToString ());


                        db_version.Value = Version.ToString ();
                        db.Meta.Commit (db_version);
                  }
            }

            public class Version : IComparable {
                  int maj = 0;
                  int min = 0;

                  public Version (int maj, int min)
                  {
                        this.maj = maj;
                        this.min = min;
                  }

                  public Version (string version)
                  {
                        string [] parts = version.Split (new char [] {'.'}, 2);
                        try {
                              this.maj = Convert.ToInt32 (parts [0]);
                        }
                        catch (Exception) {
                              this.maj = 0;
                        }
                        try {
                              this.min = Convert.ToInt32 (parts [1]);
                        }
                        catch (Exception) {
                              this.min = 0;
                        }
                  }

                  //IComparable
                  public int CompareTo (object obj) {
                        if (this.GetType () == obj.GetType ())
                              return Compare (this, (Version)obj);
                        else
                              throw new Exception ("Object must be of type Version");
                  }

                  public int CompareTo (Version version)
                  {
                        return Compare (this, version);
                  }
      
                  public static int Compare (Version v1, Version v2)
                  {
                        if (v1.maj == v2.maj)
                              return v1.min.CompareTo (v2.min);
                        return v1.maj.CompareTo (v2.maj);
                  }

                  public override string ToString ()
                  {
                        if (min == 0)
                              return maj.ToString ();
                        return maj + "." + min;
                  }

                  public override bool Equals (object obj)
                  {
                        return obj is Version && this == (Version)obj;
                  }

                  public override int GetHashCode ()
                  {
                        return maj ^ min;
                  }

                  public static bool operator == (Version v1, Version v2)
                  {
                        return v1.maj == v2.maj && v1.min == v2.min;
                  }

                  public static bool operator != (Version v1, Version v2)
                  {
                        return !(v1 == v2);
                  }

                  public static bool operator < (Version v1, Version v2)
                  {
                        return Compare (v1,v2) < 0;
                  }

                  public static bool operator > (Version v1, Version v2)
                  {
                        return Compare (v1,v2) > 0;
                  }

                  public static bool operator <= (Version v1, Version v2)
                  {
                        return !(v1 > v2);
                  }

                  public static bool operator >= (Version v1, Version v2)
                  {
                        return !(v1 < v2);
                  }
            }
      } 
}

Generated by  Doxygen 1.6.0   Back to index