banshee r3332 - in trunk/banshee: . build src/Core/Banshee.Core/Banshee.IO src/Core/Banshee.Services/Banshee.Collection.Database src/Core/Banshee.Services/Banshee.Configuration src/Core/Banshee.Services/Banshee.Database src/Core/Banshee.Services/Banshee.Library src/Core/Banshee.Services/Banshee.MediaEngine src/Core/Banshee.Services/Banshee.PlayerMigration src/Core/Banshee.Services/Banshee.Playlist src/Core/Banshee.Services/Banshee.ServiceStack src/Core/Banshee.Services/Banshee.SmartPlaylist src/Core/Banshee.Services/Banshee.Sources src/Core/Banshee.ThickClient/Banshee.SmartPlaylist.Gui src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Radio src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue src/Libraries/Hyena/Hyena.Data.Sqlite



Author: gburt
Date: Wed Feb 27 05:44:07 2008
New Revision: 3332
URL: http://svn.gnome.org/viewvc/banshee?rev=3332&view=rev

Log:
2008-02-26  Gabriel Burt  <gabriel burt gmail com>

	This commit brings back a thread-safe database layer.  Unfortunately, that
	currently means switching back to the deprecated Mono.Data.SqliteClient
	library.  Mono.Data.Sqlite does not allow executing a command in one
	thread and reading it in another, where M.D.SqliteClient does.

	* Makefile.am: Add BANSHEE_DEV_MONO_OPTIONS env var to enable setting
	--profile=default:stat for example to run with make run.

	* build/build.environment.mk: Depend on Mono.Data.SqliteClient again.
	Mono.Data.Sqlite requires that even reading the results of a query must be
	done in the same thread that ran the query/owns the connection.

	* src/Core/Banshee.Core/Banshee.IO/Provider.cs: Clean up.

	* src/Libraries/Hyena/Hyena.Data.Sqlite/SqliteModelCache.cs:
	* src/Libraries/Hyena/Hyena.Data.Sqlite/SqliteModelProvider.cs:
	* src/Core/Banshee.Services/Banshee.SmartPlaylist/SmartPlaylistSource.cs:
	* src/Core/Banshee.Services/Banshee.SmartPlaylist/Migrator.cs:
	* src/Core/Banshee.Services/Banshee.Configuration/DatabaseConfigurationClient.cs:
	* src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueSource.cs:
	* src/Core/Banshee.Services/Banshee.Playlist/PlaylistSource.cs:
	* src/Core/Banshee.Services/Banshee.Collection.Database/TrackListDatabaseModel.cs:
	* src/Core/Banshee.Services/Banshee.Collection.Database/LibraryAlbumInfo.cs:
	* src/Core/Banshee.Services/Banshee.Collection.Database/LibraryArtistInfo.cs:
	* src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseTrackInfo.cs:
	New HyenaSqliteConnection API.

	* src/Core/Banshee.Services/Banshee.Database/BansheeDbConnection.cs:
	Get rid of unused ctor and execute useful performance-related PRAGMAs.

	* src/Core/Banshee.Services/Banshee.Database/BansheeDbFormatMigrator.cs:
	Use HyenaSqliteConnection's utility methods, new API.  Add UserJob to
	rescan a user's songs when migrating over in order to get fields we now
	store in the database - like Disc, etc.

	* src/Core/Banshee.Services/Banshee.Library/LibraryImportManager.cs: Get
	rid of ProxyToMain call.

	* src/Core/Banshee.Services/Banshee.MediaEngine/PlayerEngineService.cs:
	Add null check.

	* src/Core/Banshee.Services/Banshee.ServiceStack/Application.cs: Reorder
	loading of Library, Library playlists.

	* src/Core/Banshee.Services/Banshee.PlayerMigration/AmarokPlayerImportSource.cs:
	Use Mono.Data.SqliteClient.

	* src/Core/Banshee.Services/Banshee.ServiceStack/UserJob.cs: Avoid
	duplication in ctors.

	* src/Core/Banshee.Services/Banshee.Sources/PrimarySource.cs: Increase the
	delay betwen refreshes.

	* src/Core/Banshee.ThickClient/Banshee.SmartPlaylist.Gui/Editor.cs: Get
	rid of ProxyToMain calls.

	* src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Radio/LastfmSource.cs:
	* src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Radio/StationSource.cs: Do
	not login to Last.fm radio until a source is activated.

	* src/Libraries/Hyena/Hyena.Data.Sqlite/DatabaseColumn.cs: Convert longs
	in a different way that M.D.SqliteClient likes.

	* src/Libraries/Hyena/Hyena.Data.Sqlite/HyenaSqliteCommand.cs: Combined
	with QueuedSqliteCommand code, meant to be called by threaded/queued
	HyenaSqliteConnection.

	* src/Libraries/Hyena/Hyena.Data.Sqlite/HyenaSqliteConnection.cs: Combined
	with QueuedSqliteDatabase code (from F-Spot's copy).  Supports blocking
	other threads during transactions and has no busy-waits.


Modified:
   trunk/banshee/ChangeLog
   trunk/banshee/Makefile.am
   trunk/banshee/build/build.environment.mk
   trunk/banshee/src/Core/Banshee.Core/Banshee.IO/Provider.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseTrackInfo.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/LibraryAlbumInfo.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/LibraryArtistInfo.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/TrackListDatabaseModel.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.Configuration/DatabaseConfigurationClient.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.Database/BansheeDbConnection.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.Database/BansheeDbFormatMigrator.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.Library/LibraryImportManager.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.MediaEngine/PlayerEngineService.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.PlayerMigration/AmarokPlayerImportSource.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.Playlist/PlaylistSource.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.ServiceStack/Application.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.ServiceStack/UserJob.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.SmartPlaylist/Migrator.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.SmartPlaylist/SmartPlaylistSource.cs
   trunk/banshee/src/Core/Banshee.Services/Banshee.Sources/PrimarySource.cs
   trunk/banshee/src/Core/Banshee.ThickClient/Banshee.SmartPlaylist.Gui/Editor.cs
   trunk/banshee/src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Radio/LastfmSource.cs
   trunk/banshee/src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Radio/StationSource.cs
   trunk/banshee/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueSource.cs
   trunk/banshee/src/Libraries/Hyena/Hyena.Data.Sqlite/DatabaseColumn.cs
   trunk/banshee/src/Libraries/Hyena/Hyena.Data.Sqlite/HyenaSqliteCommand.cs
   trunk/banshee/src/Libraries/Hyena/Hyena.Data.Sqlite/HyenaSqliteConnection.cs
   trunk/banshee/src/Libraries/Hyena/Hyena.Data.Sqlite/SqliteModelCache.cs
   trunk/banshee/src/Libraries/Hyena/Hyena.Data.Sqlite/SqliteModelProvider.cs

Modified: trunk/banshee/Makefile.am
==============================================================================
--- trunk/banshee/Makefile.am	(original)
+++ trunk/banshee/Makefile.am	Wed Feb 27 05:44:07 2008
@@ -61,7 +61,7 @@
 
 run:
 	@pushd bin; \
-	$(MONO) --debug Nereid.exe --debug --uninstalled $(BANSHEE_DEV_OPTIONS); \
+	$(MONO) --debug $(BANSHEE_DEV_MONO_OPTIONS) Nereid.exe --debug --uninstalled $(BANSHEE_DEV_OPTIONS); \
 	popd;
 
 clean-local:

Modified: trunk/banshee/build/build.environment.mk
==============================================================================
--- trunk/banshee/build/build.environment.mk	(original)
+++ trunk/banshee/build/build.environment.mk	Wed Feb 27 05:44:07 2008
@@ -9,7 +9,7 @@
 # External libraries to link against, generated from configure
 LINK_SYSTEM = -r:System
 LINK_SYSTEM_WEB = -r:System.Web
-LINK_SQLITE = -r:System.Data -r:Mono.Data.Sqlite
+LINK_SQLITE = -r:System.Data -r:Mono.Data.SqliteClient
 LINK_CAIRO = -r:Mono.Cairo
 LINK_MONO_POSIX = -r:Mono.Posix
 LINK_ICSHARP_ZIP_LIB = -r:ICSharpCode.SharpZipLib

Modified: trunk/banshee/src/Core/Banshee.Core/Banshee.IO/Provider.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Core/Banshee.IO/Provider.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Core/Banshee.IO/Provider.cs	Wed Feb 27 05:44:07 2008
@@ -42,8 +42,7 @@
         private static IDirectory directory;
         private static IFile file;
         
-        private static void LoadProvider ()
-        {
+        static Provider () {
             lock (typeof (Provider)) {
                 if (provider != null) {
                     return;
@@ -71,11 +70,11 @@
         }
         
         public static IDirectory Directory {
-            get { LoadProvider (); return directory; }
+            get { return directory; }
         }
         
         public static IFile File {
-            get { LoadProvider (); return file; }
+            get { return file; }
         }
         
         public static IDemuxVfs CreateDemuxVfs (string file)

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseTrackInfo.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseTrackInfo.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/DatabaseTrackInfo.cs	Wed Feb 27 05:44:07 2008
@@ -284,8 +284,8 @@
         public static bool ContainsUri (SafeUri uri)
         {
             string relative_path = Paths.MakePathRelativeToLibrary (uri.AbsolutePath) ?? uri.AbsoluteUri;
-            return Convert.ToInt32 (ServiceManager.DbConnection.ExecuteScalar (
-                check_command.ApplyValues (relative_path, uri.AbsoluteUri))) > 0;
+            return ServiceManager.DbConnection.Query<int> (
+                check_command.ApplyValues (relative_path, uri.AbsoluteUri)) > 0;
         }
         
         public SafeUri CopyToLibrary ()

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/LibraryAlbumInfo.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/LibraryAlbumInfo.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/LibraryAlbumInfo.cs	Wed Feb 27 05:44:07 2008
@@ -67,7 +67,7 @@
             if (title == null || title.Trim () == String.Empty)
                 title = Catalog.GetString ("Unknown Album");
 
-            using (IDataReader reader = ServiceManager.DbConnection.ExecuteReader (select_command.ApplyValues (artist.DbId, title))) {
+            using (IDataReader reader = ServiceManager.DbConnection.Query (select_command.ApplyValues (artist.DbId, title))) {
                 if (reader.Read ()) {
                     dbid = Convert.ToInt32 (reader[(int) Column.AlbumID]);
                     Title = reader[(int) Column.Title] as string;

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/LibraryArtistInfo.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/LibraryArtistInfo.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/LibraryArtistInfo.cs	Wed Feb 27 05:44:07 2008
@@ -66,7 +66,7 @@
             if (artistName == null || artistName.Trim () == String.Empty)
                 artistName = Catalog.GetString ("Unknown Artist");
 
-            using (IDataReader reader = ServiceManager.DbConnection.ExecuteReader (select_command.ApplyValues (artistName))) {
+            using (IDataReader reader = ServiceManager.DbConnection.Query (select_command.ApplyValues (artistName))) {
                 if (reader.Read ()) {
                     LoadFromReader (reader);
                 } else {

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/TrackListDatabaseModel.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/TrackListDatabaseModel.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Collection.Database/TrackListDatabaseModel.cs	Wed Feb 27 05:44:07 2008
@@ -169,12 +169,14 @@
                 provider.From, JoinFragment, provider.Where, ConditionFragment
             );
 
-            using (IDataReader reader = connection.ExecuteReader (String.Format (
+            using (IDataReader reader = connection.Query (String.Format (
                 "SELECT COUNT(*), {0} {1}", SelectAggregates, unfiltered_query)))
             {
-                count = Convert.ToInt32 (reader[0]);
-                duration = TimeSpan.FromMilliseconds (reader.IsDBNull (1) ? 0 : Convert.ToInt64 (reader[1]));
-                filesize = reader.IsDBNull (2) ? 0 : Convert.ToInt64 (reader[2]);
+                if (reader.Read ()) {
+                    count = Convert.ToInt32 (reader[0]);
+                    duration = TimeSpan.FromMilliseconds (reader.IsDBNull (1) ? 0 : Convert.ToInt64 (reader[1]));
+                    filesize = reader.IsDBNull (2) ? 0 : Convert.ToInt64 (reader[2]);
+                }
             }
 
             StringBuilder qb = new StringBuilder ();

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Configuration/DatabaseConfigurationClient.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Configuration/DatabaseConfigurationClient.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Configuration/DatabaseConfigurationClient.cs	Wed Feb 27 05:44:07 2008
@@ -95,7 +95,7 @@
         
         private IDataReader Get (string namespce, string key)
         {
-            return connection.ExecuteReader (
+            return connection.Query (
                 select_value_command.ApplyValues (MakeKey (namespce, key)));
         }
 

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Database/BansheeDbConnection.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Database/BansheeDbConnection.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Database/BansheeDbConnection.cs	Wed Feb 27 05:44:07 2008
@@ -29,8 +29,9 @@
 using System;
 using System.IO;
 using System.Data;
-using Mono.Data.Sqlite;
+using System.Threading;
 
+using Hyena;
 using Hyena.Data;
 using Hyena.Data.Sqlite;
 
@@ -41,22 +42,18 @@
 {
     public sealed class BansheeDbConnection : HyenaSqliteConnection, IService
     {
-        public BansheeDbConnection () : this (true)
+        public BansheeDbConnection () : base (DatabaseFile)
         {
-        }
+            Execute ("PRAGMA synchronous = OFF;");
+            Execute ("PRAGMA cache_size = 32768;");
 
-        public BansheeDbConnection (bool connect)
-            : base(connect)
-        {
-            if (connect) {
-                BansheeDbFormatMigrator migrator = new BansheeDbFormatMigrator (Connection);
-                migrator.SlowStarted += OnMigrationSlowStarted;
-                migrator.SlowPulse += OnMigrationSlowPulse;
-                migrator.SlowFinished += OnMigrationSlowFinished;
-                migrator.Migrate ();
-            }
+            BansheeDbFormatMigrator migrator = new BansheeDbFormatMigrator (this);
+            migrator.SlowStarted += OnMigrationSlowStarted;
+            migrator.SlowPulse += OnMigrationSlowPulse;
+            migrator.SlowFinished += OnMigrationSlowFinished;
+            migrator.Migrate ();
         }
-        
+
         //private Gtk.Window slow_window;
         //private Gtk.ProgressBar slow_progress;
         
@@ -125,7 +122,7 @@
             }*/
         }
 
-        public override string DatabaseFile {
+        public static string DatabaseFile {
             get {
                 if (ApplicationContext.CommandLine.Contains ("db"))
                     return ApplicationContext.CommandLine["db"];
@@ -150,7 +147,7 @@
                 return dbfile;
             }
         }
-        
+
         string IService.ServiceName {
             get { return "DbConnection"; }
         }

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Database/BansheeDbFormatMigrator.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Database/BansheeDbFormatMigrator.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Database/BansheeDbFormatMigrator.cs	Wed Feb 27 05:44:07 2008
@@ -31,8 +31,15 @@
 using System.Reflection;
 using System.Threading;
 
+using Hyena;
+using Hyena.Data.Sqlite;
 using Timer=Hyena.Timer;
 
+using Banshee.ServiceStack;
+using Banshee.Sources;
+using Banshee.Collection.Database;
+using Banshee.Streaming;
+
 namespace Banshee.Database
 {
     public class BansheeDbFormatMigrator
@@ -65,9 +72,9 @@
             }
         }
         
-        private IDbConnection connection;
+        private HyenaSqliteConnection connection;
         
-        public BansheeDbFormatMigrator(IDbConnection connection)
+        public BansheeDbFormatMigrator (HyenaSqliteConnection connection)
         {
             this.connection = connection;
         }
@@ -134,26 +141,12 @@
         
         protected bool TableExists(string tableName)
         {
-            IDbCommand command = connection.CreateCommand();
-            command.CommandText = @"
-                SELECT COUNT(*)
-                    FROM sqlite_master
-                    WHERE Type='table' AND Name=:table_name";
-            
-            IDbDataParameter table_param = command.CreateParameter();
-            table_param.ParameterName = "table_name";
-            table_param.Value = tableName;
-            
-            command.Parameters.Add(table_param);
-            
-            return Convert.ToInt32(command.ExecuteScalar()) > 0;
+            return connection.TableExists (tableName);
         }
         
         protected void Execute(string query)
         {
-            IDbCommand command = connection.CreateCommand();
-            command.CommandText = query;
-            command.ExecuteNonQuery();
+            connection.Execute (query);
         }
             
         protected int DatabaseVersion {
@@ -162,14 +155,13 @@
                     return 0;
                 }
                 
-                IDbCommand command = connection.CreateCommand();
-                command.CommandText = @"
+                string select_query = @"
                     SELECT Value 
                         FROM CoreConfiguration
                         WHERE Key = 'DatabaseVersion'
                 ";
                 
-                return Convert.ToInt32(command.ExecuteScalar());
+                return Convert.ToInt32 (connection.Query<int> (select_query));
             }
         }
         
@@ -449,9 +441,64 @@
                 INSERT INTO CoreSmartPlaylists (SmartPlaylistID, Name, Condition, OrderBy, LimitNumber, LimitCriterion)
                     SELECT * FROM SmartPlaylists
             ");
-            
+
             // TODO: Kick off some kind of scanner to find the file size of all the files
             //       since that information was never in the old Banshee
+            ServiceManager.ServiceStarted += OnServiceStarted;
+        }
+
+        private void OnServiceStarted (ServiceStartedArgs args)
+        {
+            if (args.Service is UserJobManager) {
+                ServiceManager.ServiceStarted -= OnServiceStarted;
+                if (ServiceManager.SourceManager.Library != null) {
+                    new RefreshMetadataJob ();
+                } else {
+                    ServiceManager.SourceManager.SourceAdded += OnSourceAdded;
+                }
+            }
+        }
+
+        private void OnSourceAdded (SourceAddedArgs args)
+        {
+            if (args.Source is Banshee.Library.LibrarySource) {
+                ServiceManager.SourceManager.SourceAdded -= OnSourceAdded;
+                new RefreshMetadataJob ();
+            }
+        }
+
+        private class RefreshMetadataJob : UserJob
+        {
+            public RefreshMetadataJob () : base ("Refreshing Metadata", "Refreshing Metadata", "Preparing...")
+            {
+                Register ();
+                CanCancel = false;
+
+                Banshee.Library.LibrarySource library = ServiceManager.SourceManager.Library;
+                int total = ServiceManager.DbConnection.Query<int> ("SELECT count(*) FROM CoreTracks WHERE SourceID = 1");
+                long now = DateTimeUtil.FromDateTime (DateTime.Now);
+
+                HyenaSqliteCommand select_command = new HyenaSqliteCommand (
+                    String.Format (
+                        "SELECT {0} FROM {1} WHERE {2} AND CoreTracks.SourceID = 1",
+                        DatabaseTrackInfo.Provider.Select,
+                        DatabaseTrackInfo.Provider.From,
+                        DatabaseTrackInfo.Provider.Where
+                    )
+                );
+
+                int count = 0;
+                using (System.Data.IDataReader reader = ServiceManager.DbConnection.Query (select_command)) {
+                    while (reader.Read ()) {
+                        Progress = ++count / total;
+                        DatabaseTrackInfo track = DatabaseTrackInfo.Provider.Load (reader, 0);
+                        Status = String.Format ("Updating {0} - {1}", track.ArtistName, track.TrackTitle);
+                        TagLib.File file = StreamTagger.ProcessUri (track.Uri);
+                        StreamTagger.TrackInfoMerge (track, file);
+                        track.Save ();
+                    }
+                }
+            }
         }
         
 #endregion

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Library/LibraryImportManager.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Library/LibraryImportManager.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Library/LibraryImportManager.cs	Wed Feb 27 05:44:07 2008
@@ -32,6 +32,8 @@
 using Mono.Unix;
 
 using Hyena;
+using Hyena.Data.Sqlite;
+
 using Banshee.Base;
 using Banshee.Sources;
 using Banshee.ServiceStack;
@@ -116,19 +118,20 @@
             
             /*if (DatabaseTrackInfo.ContainsUri (uri)) {
                 IncrementProcessedCount (null);
-                return;
+                return null;
             }*/
 
-            TagLib.File file = StreamTagger.ProcessUri (uri);
-            track = new DatabaseTrackInfo ();
-            StreamTagger.TrackInfoMerge (track, file);
-            
-            SafeUri newpath = track.CopyToLibrary ();
-            if (newpath != null) {
-                track.Uri = newpath;
-            }
+            //ServiceManager.DbConnection.BeginTransaction ();
+            //try {
+                TagLib.File file = StreamTagger.ProcessUri (uri);
+                track = new DatabaseTrackInfo ();
+                StreamTagger.TrackInfoMerge (track, file);
+                
+                SafeUri newpath = track.CopyToLibrary ();
+                if (newpath != null) {
+                    track.Uri = newpath;
+                }
 
-            ThreadAssist.ProxyToMain (delegate {
                 track.DateAdded = DateTime.Now;
                 LibraryArtistInfo artist = new LibraryArtistInfo (track.ArtistName);
                 track.ArtistId = artist.DbId;
@@ -137,8 +140,13 @@
                 artist.Save ();
 
                 track.Source = ServiceManager.SourceManager.Library;
+
                 track.Save ();
-            });
+                //ServiceManager.DbConnection.CommitTransaction ();
+            /*} catch (Exception) {
+                ServiceManager.DbConnection.RollbackTransaction ();
+                throw;
+            }*/
             
             return track;
         }
@@ -146,6 +154,10 @@
         private void LogError (string path, Exception e)
         {
             LogError (path, e.Message);
+
+            if (!(e is TagLib.CorruptFileException) && !(e is TagLib.UnsupportedFormatException)) {
+                Log.DebugFormat ("Full import exception: {0}", e.ToString ());
+            }
         }
 
         private void LogError (string path, string msg)

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.MediaEngine/PlayerEngineService.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.MediaEngine/PlayerEngineService.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.MediaEngine/PlayerEngineService.cs	Wed Feb 27 05:44:07 2008
@@ -357,7 +357,7 @@
         
         public bool IsPlaying (TrackInfo track)
         {
-            return CurrentState != PlayerEngineState.Idle && track.AudiblyEqual (CurrentTrack);
+            return CurrentState != PlayerEngineState.Idle && track != null && track.AudiblyEqual (CurrentTrack);
         }
 
         private void CheckPending ()

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.PlayerMigration/AmarokPlayerImportSource.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.PlayerMigration/AmarokPlayerImportSource.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.PlayerMigration/AmarokPlayerImportSource.cs	Wed Feb 27 05:44:07 2008
@@ -31,7 +31,7 @@
 using System.Data;
 using System.IO;
 
-using Mono.Data.Sqlite;
+using Mono.Data.SqliteClient;
 using Mono.Unix;
 
 using Banshee.Base;
@@ -222,4 +222,4 @@
             get { return new string [] { "system-search" }; }
         }
     }
-}
\ No newline at end of file
+}

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Playlist/PlaylistSource.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Playlist/PlaylistSource.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Playlist/PlaylistSource.cs	Wed Feb 27 05:44:07 2008
@@ -257,7 +257,7 @@
 
         public static IEnumerable<PlaylistSource> LoadAll ()
         {
-            using (IDataReader reader = ServiceManager.DbConnection.ExecuteReader (
+            using (IDataReader reader = ServiceManager.DbConnection.Query (
                 "SELECT PlaylistID, Name, SortColumn, SortType FROM CorePlaylists WHERE Special = 0")) {
                 while (reader.Read ()) {
                     yield return new PlaylistSource (
@@ -270,14 +270,9 @@
         
         public static int GetPlaylistId (string name)
         {
-            object result = ServiceManager.DbConnection.ExecuteScalar (new HyenaSqliteCommand (
-                "SELECT PlaylistID FROM Playlists WHERE Name = ? LIMIT 1", name));
-            
-            if (result != null) {
-                return Convert.ToInt32 (result);
-            }
-            
-            return 0;
+            return ServiceManager.DbConnection.Query<int> (
+                "SELECT PlaylistID FROM Playlists WHERE Name = ? LIMIT 1", name
+            );
         }
         
         public static bool PlaylistExists (string name)

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.ServiceStack/Application.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.ServiceStack/Application.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.ServiceStack/Application.cs	Wed Feb 27 05:44:07 2008
@@ -57,12 +57,13 @@
             ServiceManager.Run ();
             
             if (ServiceManager.SourceManager != null) {
-                ServiceManager.SourceManager.LoadExtensionSources ();
                 ServiceManager.SourceManager.AddSource (new LibrarySource (), true);
 
                 foreach (PlaylistSource pl in PlaylistSource.LoadAll ()) {
-                    ServiceManager.SourceManager.DefaultSource.AddChildSource (pl);
+                    ServiceManager.SourceManager.Library.AddChildSource (pl);
                 }
+
+                ServiceManager.SourceManager.LoadExtensionSources ();
             }
             
             Banshee.Base.PlatformHacks.RestoreMonoJitSegv ();

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.ServiceStack/UserJob.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.ServiceStack/UserJob.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.ServiceStack/UserJob.cs	Wed Feb 27 05:44:07 2008
@@ -52,26 +52,16 @@
         public event EventHandler Updated;
         public event EventHandler CancelRequested;
         
-        public UserJob (string name, string title, string status)
+        public UserJob (string name, string title, string status) : this (name, title, status, null)
         {
-            FreezeUpdate ();
-            Name = name;
-            Title = title;
-            Status = status;
-            ThawUpdate (true);
         }
         
-        public UserJob (string name, string title, string status, string iconName)
+        /*public UserJob (string name, string title, string status, string iconName)
+            : this (name, title, status, new string [] { iconName })
         {
-            FreezeUpdate ();
-            Name = name;
-            Title = title;
-            Status = status;
-            IconNames = new string [] { iconName };
-            ThawUpdate (true);
-        }
+        }*/
         
-        public UserJob (string name, string title, string status, string [] iconNames)
+        public UserJob (string name, string title, string status, params string [] iconNames)
         {
             FreezeUpdate ();
             Name = name;
@@ -173,9 +163,11 @@
         
         public virtual string [] IconNames {
             get { return icon_names; }
-            set { 
-                icon_names = value; 
-                OnUpdated (); 
+            set {
+                if (value != null) {
+                    icon_names = value; 
+                    OnUpdated (); 
+                }
             }
         }
         

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.SmartPlaylist/Migrator.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.SmartPlaylist/Migrator.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.SmartPlaylist/Migrator.cs	Wed Feb 27 05:44:07 2008
@@ -59,7 +59,7 @@
             try {
                 ServiceManager.DbConnection.Execute ("BEGIN");
                 Migrator m = new Migrator ();
-                using (IDataReader reader = ServiceManager.DbConnection.ExecuteReader (
+                using (IDataReader reader = ServiceManager.DbConnection.Query (
                     "SELECT SmartPlaylistID, Name, Condition, OrderBy, LimitNumber, LimitCriterion FROM CoreSmartPlaylists")) {
                     while (reader.Read ()) {
                         m.Migrate (

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.SmartPlaylist/SmartPlaylistSource.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.SmartPlaylist/SmartPlaylistSource.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.SmartPlaylist/SmartPlaylistSource.cs	Wed Feb 27 05:44:07 2008
@@ -338,7 +338,7 @@
 
         public static IEnumerable<SmartPlaylistSource> LoadAll ()
         {
-            using (IDataReader reader = ServiceManager.DbConnection.ExecuteReader (
+            using (IDataReader reader = ServiceManager.DbConnection.Query (
                 "SELECT SmartPlaylistID, Name, Condition, OrderBy, LimitNumber, LimitCriterion FROM CoreSmartPlaylists")) {
                 while (reader.Read ()) {
                     SmartPlaylistSource playlist = null;

Modified: trunk/banshee/src/Core/Banshee.Services/Banshee.Sources/PrimarySource.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.Services/Banshee.Sources/PrimarySource.cs	(original)
+++ trunk/banshee/src/Core/Banshee.Services/Banshee.Sources/PrimarySource.cs	Wed Feb 27 05:44:07 2008
@@ -90,11 +90,11 @@
                 source_id = ServiceManager.DbConnection.Execute ("INSERT INTO CorePrimarySources (StringID) VALUES (?)", id);
             }
 
-            track_model.Condition = String.Format ("CoreTracks.SourceID = {0}", source_id);;
+            track_model.Condition = String.Format ("CoreTracks.SourceID = {0}", source_id);
             error_source.Updated += OnErrorSourceUpdated;
             OnErrorSourceUpdated (null, null);
 
-            tracks_updated_limiter = new RateLimiter (10.0, 50.0, RateLimitedOnTracksUpdated);
+            tracks_updated_limiter = new RateLimiter (50.0, 500.0, RateLimitedOnTracksUpdated);
 
             primary_sources[source_id] = this;
         }

Modified: trunk/banshee/src/Core/Banshee.ThickClient/Banshee.SmartPlaylist.Gui/Editor.cs
==============================================================================
--- trunk/banshee/src/Core/Banshee.ThickClient/Banshee.SmartPlaylist.Gui/Editor.cs	(original)
+++ trunk/banshee/src/Core/Banshee.ThickClient/Banshee.SmartPlaylist.Gui/Editor.cs	Wed Feb 27 05:44:07 2008
@@ -232,8 +232,8 @@
                         playlist.Limit = limit;
                         playlist.LimitValue = limit_value;
 
+                        playlist.Save ();
                         ThreadAssist.ProxyToMain (delegate {
-                            playlist.Save ();
                             ServiceManager.SourceManager.DefaultSource.AddChildSource (playlist);
                         });
                         //SmartPlaylistCore.Instance.StartTimer (playlist);
@@ -243,11 +243,9 @@
                         playlist.LimitValue = limit_value;
                         playlist.Limit = limit;
 
-                        ThreadAssist.ProxyToMain (delegate {
-                            playlist.Rename (name);
-                            playlist.Save ();
-                            playlist.Reload ();
-                        });
+                        playlist.Rename (name);
+                        playlist.Save ();
+                        playlist.Reload ();
 
                         /*if (playlist.TimeDependent)
                             SmartPlaylistCore.Instance.StartTimer (playlist);

Modified: trunk/banshee/src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Radio/LastfmSource.cs
==============================================================================
--- trunk/banshee/src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Radio/LastfmSource.cs	(original)
+++ trunk/banshee/src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Radio/LastfmSource.cs	Wed Feb 27 05:44:07 2008
@@ -107,9 +107,9 @@
         {
             Connection.StateChanged += HandleConnectionStateChanged;
             
-            if (Account.UserName != null && Account.CryptedPassword != null) {
+            /*if (Account.UserName != null && Account.CryptedPassword != null) {
                 Connection.Connect ();
-            }
+            }*/
             
             UpdateUI ();
         }
@@ -234,10 +234,13 @@
             }
         }
 
-        /*public override void Activate ()
+        public override void Activate ()
         {
-            InterfaceElements.ActionButtonBox.PackStart (add_button, false, false, 0);
-        }*/
+            //InterfaceElements.ActionButtonBox.PackStart (add_button, false, false, 0);
+            if (Connection.State == ConnectionState.Disconnected) {
+                Connection.Connect ();
+            }
+        }
 
         public override bool CanSearch {
             get { return false; }

Modified: trunk/banshee/src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Radio/StationSource.cs
==============================================================================
--- trunk/banshee/src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Radio/StationSource.cs	(original)
+++ trunk/banshee/src/Extensions/Banshee.Lastfm/Banshee.Lastfm.Radio/StationSource.cs	Wed Feb 27 05:44:07 2008
@@ -521,7 +521,7 @@
                 creator
             );
 
-            using (IDataReader reader = ServiceManager.DbConnection.ExecuteReader (command)) {
+            using (IDataReader reader = ServiceManager.DbConnection.Query (command)) {
                 while (reader.Read ()) {
                     try {
                         stations.Add (new StationSource (lastfm,

Modified: trunk/banshee/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueSource.cs
==============================================================================
--- trunk/banshee/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueSource.cs	(original)
+++ trunk/banshee/src/Extensions/Banshee.PlayQueue/Banshee.PlayQueue/PlayQueueSource.cs	Wed Feb 27 05:44:07 2008
@@ -114,18 +114,17 @@
         
         private void BindToDatabase ()
         {
-            object result = ServiceManager.DbConnection.ExecuteScalar (new HyenaSqliteCommand (@"
-                SELECT PlaylistID FROM CorePlaylists 
-                    WHERE Special = 1 AND Name = ?
-                    LIMIT 1", special_playlist_name));
+            int result = ServiceManager.DbConnection.Query<int> (
+                "SELECT PlaylistID FROM CorePlaylists WHERE Special = 1 AND Name = ? LIMIT 1",
+                special_playlist_name
+            );
             
-            if (result != null) {
-                DbId = Convert.ToInt32 (result);
+            if (result != 0) {
+                DbId = result;
             } else {
-                ServiceManager.DbConnection.Execute (new HyenaSqliteCommand (@"
-                    INSERT INTO CorePlaylists VALUES (0, ?, -1, 0, 1)
+                DbId = ServiceManager.DbConnection.Execute (new HyenaSqliteCommand (@"
+                    INSERT INTO CorePlaylists (PlaylistID, Name, SortColumn, SortType, Special) VALUES (NULL, ?, -1, 0, 1)
                 ", special_playlist_name));
-                DbId = ServiceManager.DbConnection.LastInsertRowId;
             }
         }
         

Modified: trunk/banshee/src/Libraries/Hyena/Hyena.Data.Sqlite/DatabaseColumn.cs
==============================================================================
--- trunk/banshee/src/Libraries/Hyena/Hyena.Data.Sqlite/DatabaseColumn.cs	(original)
+++ trunk/banshee/src/Libraries/Hyena/Hyena.Data.Sqlite/DatabaseColumn.cs	Wed Feb 27 05:44:07 2008
@@ -95,7 +95,7 @@
                     : 0;
             } else {
                 result = !reader.IsDBNull (column)
-                    ? (long) reader.GetValue (column)
+                    ? Convert.ToInt64 (reader.GetValue (column))
                     : 0;
             }
             result = SqliteUtils.FromDbFormat (type, result);

Modified: trunk/banshee/src/Libraries/Hyena/Hyena.Data.Sqlite/HyenaSqliteCommand.cs
==============================================================================
--- trunk/banshee/src/Libraries/Hyena/Hyena.Data.Sqlite/HyenaSqliteCommand.cs	(original)
+++ trunk/banshee/src/Libraries/Hyena/Hyena.Data.Sqlite/HyenaSqliteCommand.cs	Wed Feb 27 05:44:07 2008
@@ -1,11 +1,11 @@
 //
 // HyenaSqliteCommand.cs
 //
-// Author:
+// Authors:
 //   Aaron Bockover <abockover novell com>
 //   Gabriel Burt <gburt novell com>
 //
-// Copyright (C) 2007 Novell, Inc.
+// Copyright (C) 2007-2008 Novell, Inc.
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
@@ -30,120 +30,154 @@
 using System;
 using System.IO;
 using System.Data;
-using Mono.Data.Sqlite;
+using System.Text;
+using System.Threading;
+
+// NOTE: Mono.Data.Sqlite has serious threading issues.  You cannot access
+//       its results from any thread but the one the SqliteConnection belongs to.
+//       That is why we still use Mono.Data.SqliteClient.
+using Mono.Data.SqliteClient;
 
 namespace Hyena.Data.Sqlite
 {
     public class HyenaSqliteCommand
     {
-        private SqliteCommand command;
+        protected object result = null;
+        private Exception execution_exception = null;
+        private bool finished = false;
+
+        private string command;
+        private string command_format = null;
+        private string command_formatted = null;
+        private int parameter_count = 0;
+        private object [] current_values;
 
 #region Properties
 
-        public SqliteCommand Command {
+        public string Text {
             get { return command; }
         }
 
-        public SqliteParameterCollection Parameters {
-            get { return command.Parameters; }
-        }
-
-        public string CommandText {
-            get { return command.CommandText; }
+        private HyenaCommandType command_type;
+        public HyenaCommandType CommandType {
+            get { return command_type; }
+            set { command_type = value; }
         }
 
 #endregion
 
-        public HyenaSqliteCommand (string command_str)
+        public HyenaSqliteCommand (string command)
+        {
+            this.command = command;
+        }
+
+        public HyenaSqliteCommand (string command, params object [] param_values)
         {
-            this.command = new SqliteCommand (command_str);
+            this.command = command;
+            ApplyValues (param_values);
+        }
 
-            int num_params = 0;
-            for (int i = 0; i < command_str.Length; i++) {
-                if (command_str [i] == '?') {
-                    num_params++;
+        public void Execute (SqliteConnection connection)
+        {
+            finished = false;
+            execution_exception = null;
+            result = null;
+
+            SqliteCommand sql_command = new SqliteCommand (CurrentSqlText ());
+            sql_command.Connection = connection;
+            //Log.Debug ("Executing {0}", sql_command.CommandText);
+
+            try {
+                switch (command_type) {
+                    case HyenaCommandType.Reader:
+                        result = sql_command.ExecuteReader ();
+                        break;
+
+                    case HyenaCommandType.Scalar:
+                        result = sql_command.ExecuteScalar ();
+                        break;
+
+                    case HyenaCommandType.Execute:
+                    default:
+                        sql_command.ExecuteNonQuery ();
+                        result = sql_command.LastInsertRowID ();
+                        break;
                 }
+            } catch (Exception e) {
+                Log.DebugFormat (String.Format ("Exception executing command: {0}", Text), e.ToString ()); 
+                execution_exception = e;
             }
 
-            CreateParameters (num_params);
+            finished = true;
         }
 
-        public HyenaSqliteCommand (string command_str, params object [] param_values)
+        internal object WaitForResult (HyenaSqliteConnection conn)
         {
-            this.command = new SqliteCommand (command_str);
-            CreateParameters (param_values.Length);
-            ApplyValues (param_values);
-        }
+            while (!finished) {
+                conn.ResultReadySignal.WaitOne ();
+            }
 
-        protected void CreateParameters (int num_params)
-        {
-            for (int i = 0; i < num_params; i++) {
-                Parameters.Add (new SqliteParameter ());
+            conn.ClaimResult ();
+
+            if (execution_exception != null) {
+                throw execution_exception;
             }
+            
+            return result;
         }
 
         public HyenaSqliteCommand ApplyValues (params object [] param_values)
         {
-            if (param_values.Length != Parameters.Count) {
+            finished = false;
+            if (command_format == null) {
+                CreateParameters ();
+            }
+
+            if (param_values.Length != parameter_count) {
                 throw new ArgumentException (String.Format (
-                    "Command has {0} parameters, but {1} values given.", Parameters.Count, param_values.Length
+                    "Command has {0} parameters, but {1} values given.", parameter_count, param_values.Length
                 ));
             }
 
-            for (int i = 0; i < param_values.Length; i++) {
-                Parameters[i].Value = param_values[i];
+            for (int i = 0; i < parameter_count; i++) {
+                if (param_values[i] is string) {
+                    param_values[i] = String.Format ("'{0}'", (param_values[i] as string).Replace ("'", "''"));
+                } else if (param_values[i] == null) {
+                    param_values[i] = "NULL";
+                }
             }
 
+            current_values = param_values;
+            command_formatted = null;
             return this;
         }
-        
-        public void AddNamedParameter (string name, object value)
+
+        private string CurrentSqlText ()
         {
-            SqliteParameter param = new SqliteParameter (name, DbType.String);
-            param.Value = value;
-            Parameters.Add (param);
-        }
-                
-        /*public DbCommand(string command, params object [] parameters) : this(command)
-        {
-            for(int i = 0; i < parameters.Length;) {
-                SqliteParameter param;
-                
-                if(parameters[i] is SqliteParameter) {
-                    param = (SqliteParameter)parameters[i];
-                    if(i < parameters.Length - 1 && !(parameters[i + 1] is SqliteParameter)) {
-                        param.Value = parameters[i + 1];
-                        i += 2;
-                    } else {
-                        i++;
-                    }
-                } else {
-                    param = new SqliteParameter();
-                    param.ParameterName = (string)parameters[i];
-                    param.Value = parameters[i + 1];
-                    i += 2;
-                }
-                
-                Parameters.Add(param);
+            if (command_format == null) {
+                return command;
             }
+
+            if (command_formatted == null) {
+                command_formatted = String.Format (command_format, current_values);
+            }
+
+            return command_formatted;
         }
-        
-        public void AddParameter (object value)
-        {
-            SqliteParameter param = new SqliteParameter ();
-            param.Value = value;
-            Parameters.Add (param);
-        }
-        
-        public void AddParameter<T>(string name, T value)
+
+        private void CreateParameters ()
         {
-            AddParameter<T>(new DbParameter<T>(name), value);
+            StringBuilder sb = new StringBuilder ();
+            foreach (char c in command) {
+                if (c == '?') {
+                    sb.Append ('{');
+                    sb.Append (parameter_count++);
+                    sb.Append ('}');
+                } else {
+                    sb.Append (c);
+                }
+            }
+            command_format = sb.ToString ();
         }
-        
-        public void AddParameter<T>(DbParameter<T> param, T value)
-        {
-            param.Value = value;
-            Parameters.Add(param);
-        }*/
     }
 }

Modified: trunk/banshee/src/Libraries/Hyena/Hyena.Data.Sqlite/HyenaSqliteConnection.cs
==============================================================================
--- trunk/banshee/src/Libraries/Hyena/Hyena.Data.Sqlite/HyenaSqliteConnection.cs	(original)
+++ trunk/banshee/src/Libraries/Hyena/Hyena.Data.Sqlite/HyenaSqliteConnection.cs	Wed Feb 27 05:44:07 2008
@@ -1,10 +1,11 @@
 //
 // HyenaSqliteConnection.cs
 //
-// Author:
+// Authors:
 //   Aaron Bockover <abockover novell com>
+//   Gabriel Burt <gburt novell com>
 //
-// Copyright (C) 2007 Novell, Inc.
+// Copyright (C) 2005-2008 Novell, Inc.
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
@@ -25,74 +26,170 @@
 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
-
+ 
 using System;
-using System.IO;
-using System.Collections.Generic;
 using System.Data;
 using System.Threading;
-using Mono.Data.Sqlite;
+using System.Collections.Generic;
+
+// NOTE: Mono.Data.Sqlite has serious threading issues.  You cannot access
+//       its results from any thread but the one the SqliteConnection belongs to.
+//       That is why we still use Mono.Data.SqliteClient.
+using Mono.Data.SqliteClient;
 
 namespace Hyena.Data.Sqlite
 {
-    public abstract class HyenaSqliteConnection : IDisposable
+    public enum HyenaCommandType {
+        Reader,
+        Scalar,
+        Execute,
+    }
+
+    public class HyenaSqliteConnection : IDisposable
     {
         private SqliteConnection connection;
+        private string dbpath;
+
+        private Queue<HyenaSqliteCommand> command_queue = new Queue<HyenaSqliteCommand>();
+        private Thread queue_thread;
+        private bool processing_queue = false;
+        private volatile bool dispose_requested = false;
+        private volatile int results_ready = 0;
+        private AutoResetEvent queue_signal = new AutoResetEvent (false);
+        private ManualResetEvent result_ready_signal = new ManualResetEvent (false);
+
+        private volatile Thread transaction_thread = null;
+        private ManualResetEvent transaction_signal = new ManualResetEvent (true);
 
-        private static Thread main_thread;
-        static HyenaSqliteConnection () {
-            main_thread = Thread.CurrentThread;
+        internal ManualResetEvent ResultReadySignal {
+            get { return result_ready_signal; }
+        }
+        
+        public HyenaSqliteConnection(string dbpath)
+        {
+            this.dbpath = dbpath;
+            queue_thread = new Thread(ProcessQueue);
+            queue_thread.IsBackground = true;
+            queue_thread.Start();
         }
 
-        private static bool InMainThread {
-            get { return main_thread.Equals (Thread.CurrentThread); }
+#region Public Query Methods
+        
+        // SELECT multiple column queries
+        public IDataReader Query (HyenaSqliteCommand command)
+        {
+            command.CommandType = HyenaCommandType.Reader;
+            QueueCommand(command);
+            return command.WaitForResult (this) as SqliteDataReader;
         }
 
-        public HyenaSqliteConnection () : this (true)
+        public IDataReader Query (string command_str, params object [] param_values)
+        {
+            return Query (new HyenaSqliteCommand (command_str, param_values));
+        }
+        
+        public IDataReader Query (object command)
         {
+            return Query (new HyenaSqliteCommand (command.ToString ()));
+        }
+        
+        // SELECT single column queries
+        public T Query<T> (HyenaSqliteCommand command)
+        {
+            command.CommandType = HyenaCommandType.Scalar;
+            QueueCommand(command);
+            object result = command.WaitForResult (this);
+
+            return result == null 
+                ? default (T)
+                : (T) SqliteUtils.FromDbFormat (typeof (T), Convert.ChangeType (result, typeof (T)));
         }
 
-        public HyenaSqliteConnection (bool connect)
+        public T Query<T> (string command_str, params object [] param_values)
         {
-            if (connect) {
-                Open ();
-            }
+            return Query<T> (new HyenaSqliteCommand (command_str, param_values));
         }
 
-        public void Dispose ()
+        public T Query<T> (object command)
         {
-            Close ();
+            return Query<T> (new HyenaSqliteCommand (command.ToString ()));
         }
 
-        public void Open ()
+        // INSERT, UPDATE, DELETE queries
+        public int Execute (HyenaSqliteCommand command)
         {
-            lock (this) {
-                if (connection != null) {
-                    return;
-                }
+            command.CommandType = HyenaCommandType.Execute;;
+            QueueCommand(command);
+            return (int) command.WaitForResult (this);
+        }
 
-                string dbfile = DatabaseFile;
-                connection = new SqliteConnection (String.Format ("Version=3,URI=file:{0}", dbfile));
-                connection.Open ();
+        public int Execute (string command_str, params object [] param_values)
+        {
+            return Execute (new HyenaSqliteCommand (command_str, param_values));
+        }
 
-                Execute (@"
-                    PRAGMA synchronous = OFF;
-                    PRAGMA cache_size = 32768;
-                ");
-            }
+        public int Execute (object command)
+        {
+            return Execute (new HyenaSqliteCommand (command.ToString ()));
         }
 
-        public void Close ()
+#endregion
+
+#region Public Utility Methods
+        
+        public void BeginTransaction ()
         {
-            lock (this) {
-                if (connection != null) {
-                    connection.Close ();
-                    connection = null;
+            if (transaction_thread == Thread.CurrentThread) {
+                throw new Exception ("Can't start a recursive transaction");
+            }
+
+            while (transaction_thread != Thread.CurrentThread) {
+                if (transaction_thread != null) {
+                    // Wait for the existing transaction to finish before this thread proceeds
+                    transaction_signal.WaitOne ();
+                }
+
+                lock (command_queue) {
+                    if (transaction_thread == null) {
+                        transaction_thread = Thread.CurrentThread;
+                        transaction_signal.Reset ();
+                    }
                 }
             }
+
+            Execute ("BEGIN TRANSACTION");
         }
 
-#region Convenience methods 
+        public void CommitTransaction ()
+        {
+            if (transaction_thread != Thread.CurrentThread) {
+                throw new Exception ("Can't commit from outside a transaction");
+            }
+
+            Execute ("COMMIT TRANSACTION");
+
+            lock (command_queue) {
+                transaction_thread = null;
+                // Let any other threads continue
+                transaction_signal.Set (); 
+            }
+        }
+
+        public void RollbackTransaction ()
+        {
+            if (transaction_thread != Thread.CurrentThread) {
+                throw new Exception ("Can't rollback from outside a transaction");
+            }
+
+            Execute ("ROLLBACK");
+
+            lock (command_queue) {
+                transaction_thread = null;
+            
+                // Let any other threads continue
+                transaction_signal.Set (); 
+            }
+        }
 
         public bool TableExists (string tableName)
         {
@@ -115,7 +212,7 @@
                         type, name)
                 ) > 0;
         }
-        
+
         private delegate void SchemaHandler (string column);
         
         private void SchemaClosure (string table_name, SchemaHandler code)
@@ -156,136 +253,73 @@
             return schema;
         }
 
-        public IDataReader ExecuteReader (SqliteCommand command)
-        {
-            if (command.Connection == null)
-                command.Connection = connection;
-
-            if (!InMainThread) {
-                Console.WriteLine ("About to execute command not in main thread: {0}", command.CommandText);
-            }
-
-            try {
-                return command.ExecuteReader ();
-            } catch (Exception e) {
-                Console.WriteLine ("Caught exception trying to execute {0}", command.CommandText);
-                throw e;
-            }
-        }
-
-        public IDataReader ExecuteReader (HyenaSqliteCommand command)
-        {
-            return ExecuteReader (command.Command);
-        }
-
-        public IDataReader ExecuteReader (string command_str, params object [] param_values)
-        {
-            return ExecuteReader (new HyenaSqliteCommand (command_str, param_values));
-        }
+#endregion
 
-        public IDataReader ExecuteReader (object command)
-        {
-            return ExecuteReader (new SqliteCommand (command.ToString ()));
-        }
+#region Private Queue Methods
 
-        public object ExecuteScalar (SqliteCommand command)
+        private void QueueCommand(HyenaSqliteCommand command)
         {
-            if (command.Connection == null)
-                command.Connection = connection;
-
-            if (!InMainThread) {
-                Console.WriteLine ("About to execute command not in main thread: {0}", command.CommandText);
-            }
+            bool queued = false;
+            while (true) {
+                lock (command_queue) {
+                    if (transaction_thread == null || Thread.CurrentThread == transaction_thread) {
+                        command_queue.Enqueue (command);
+                        break;
+                    }
+                }
 
-            try {
-                return command.ExecuteScalar ();
-            } catch (Exception e) {
-                Console.WriteLine ("Caught exception trying to execute {0}", command.CommandText);
-                throw e;
+                transaction_signal.WaitOne ();
             }
+            queue_signal.Set ();
         }
 
-        public object ExecuteScalar (HyenaSqliteCommand command)
-        {
-            return ExecuteScalar (command.Command);
-        }
-
-        public object ExecuteScalar (string command_str, params object [] param_values)
-        {
-            return ExecuteScalar (new HyenaSqliteCommand (command_str, param_values));
-        }
-
-        public object ExecuteScalar (object command)
-        {
-            return ExecuteScalar (new SqliteCommand (command.ToString ()));
-        }
-        
-        public T Query<T> (SqliteCommand command)
-        {
-            object result = ExecuteScalar (command);
-            return result == null 
-                ? default (T)
-                : (T)SqliteUtils.FromDbFormat (typeof (T), Convert.ChangeType (result, typeof (T)));
-        }
-        
-        public T Query<T> (HyenaSqliteCommand command)
+        internal void ClaimResult ()
         {
-            return Query<T> (command.Command);
-        }
-
-        public T Query<T> (string command_str, params object [] param_values)
-        {
-            return Query<T> (new HyenaSqliteCommand (command_str, param_values));
-        }
-
-        public T Query<T> (object command)
-        {
-            return Query<T> (new SqliteCommand (command.ToString ()));
+            lock (command_queue) {
+                results_ready++;
+                if (results_ready == 0) {
+                    result_ready_signal.Reset ();
+                }
+            }
         }
 
-        public int Execute (SqliteCommand command)
-        {
-            if (command.Connection == null)
-                command.Connection = connection;
-
-            if (!InMainThread) {
-                Console.WriteLine ("About to execute command not in main thread: {0}", command.CommandText);
+        private void ProcessQueue()
+        {         
+            if (connection == null) {
+                connection = new SqliteConnection (String.Format ("Version=3,URI=file:{0}", dbpath));
+                connection.Open ();
             }
+            
+            // Keep handling queries
+            while (!dispose_requested) {
+                while (command_queue.Count > 0) {
+                    HyenaSqliteCommand command;
+                    lock (command_queue) {
+                        command = command_queue.Dequeue ();
+                    }
+
+                    command.Execute (connection);
+
+                    lock (command_queue) {
+                        results_ready++;
+                        result_ready_signal.Set ();
+                    }
+                }
 
-            try {
-                command.ExecuteNonQuery ();
-            } catch (Exception e) {
-                Console.WriteLine ("Caught exception trying to execute {0}", command.CommandText);
-                throw e;
+                queue_signal.WaitOne ();
             }
-            return command.LastInsertRowID ();
-        }
 
-        public int Execute (HyenaSqliteCommand command)
-        {
-            return Execute (command.Command);
-        }
-
-        public int Execute (string command_str, params object [] param_values)
-        {
-            return Execute (new HyenaSqliteCommand (command_str, param_values));
-        }
-
-        public int Execute (object command)
-        {
-            return Execute (new SqliteCommand (command.ToString ()));
-        }
-        
-        public int LastInsertRowId {
-            get { return connection.LastInsertRowId; }
+            // Finish
+            connection.Close ();
         }
 
 #endregion
 
-        public abstract string DatabaseFile { get; }
-
-        public IDbConnection Connection {
-            get { return connection; }
+        public void Dispose()
+        {
+            dispose_requested = true;
+            queue_signal.Set ();
+            queue_thread.Join ();
         }
     }
 }

Modified: trunk/banshee/src/Libraries/Hyena/Hyena.Data.Sqlite/SqliteModelCache.cs
==============================================================================
--- trunk/banshee/src/Libraries/Hyena/Hyena.Data.Sqlite/SqliteModelCache.cs	(original)
+++ trunk/banshee/src/Libraries/Hyena/Hyena.Data.Sqlite/SqliteModelCache.cs	Wed Feb 27 05:44:07 2008
@@ -169,7 +169,7 @@
         {
             //using (new Timer (String.Format ("Fetching set for {0}", model))) {
                 select_range_command.ApplyValues (offset, limit);
-                using (IDataReader reader = connection.ExecuteReader (select_range_command)) {
+                using (IDataReader reader = connection.Query (select_range_command)) {
                     while (reader.Read ()) {
                         if (!ContainsKey (offset)) {
                             Add (offset, provider.Load (reader, offset));
@@ -182,12 +182,14 @@
         
         protected void UpdateAggregates ()
         {
-            using (IDataReader reader = connection.ExecuteReader (count_command.ApplyValues (uid))) {
-                rows = Convert.ToInt32 (reader[0]);
+            using (IDataReader reader = connection.Query (count_command.ApplyValues (uid))) {
+                if (reader.Read ()) {
+                    rows = Convert.ToInt32 (reader[0]);
 
-                AggregatesUpdatedEventHandler handler = AggregatesUpdated;
-                if (handler != null) {
-                    handler (reader);
+                    AggregatesUpdatedEventHandler handler = AggregatesUpdated;
+                    if (handler != null) {
+                        handler (reader);
+                    }
                 }
             }
         }

Modified: trunk/banshee/src/Libraries/Hyena/Hyena.Data.Sqlite/SqliteModelProvider.cs
==============================================================================
--- trunk/banshee/src/Libraries/Hyena/Hyena.Data.Sqlite/SqliteModelProvider.cs	(original)
+++ trunk/banshee/src/Libraries/Hyena/Hyena.Data.Sqlite/SqliteModelProvider.cs	Wed Feb 27 05:44:07 2008
@@ -48,7 +48,6 @@
         private HyenaSqliteCommand select_command;
         private HyenaSqliteCommand select_range_command;
         private HyenaSqliteCommand select_single_command;
-
         
         private string primary_key;
         private string select;
@@ -102,7 +101,7 @@
         protected virtual void CheckVersion ()
         {
             if (connection.TableExists (HyenaTableName)) {
-                using (IDataReader reader = connection.ExecuteReader (SelectVersionSql (TableName))) {
+                using (IDataReader reader = connection.Query (SelectVersionSql (TableName))) {
                     if (reader.Read ()) {
                         int table_version = reader.GetInt32 (0);
                         if (table_version < ModelVersion) {
@@ -243,14 +242,16 @@
         
         protected virtual void PrepareInsertCommand (T target)
         {
+            object [] values = new object [columns.Count];
             for (int i = 0; i < columns.Count; i++) {
                 if (columns[i] != key) {
-                    InsertCommand.Parameters[i].Value = columns[i].GetValue (target);
+                    values[i] = columns[i].GetValue (target);
                 } else {
                     // On insert, the key needs to be NULL to be automatically set by Sqlite
-                    InsertCommand.Parameters[i].Value = null;
+                    values[i] = null;
                 }
             }
+            InsertCommand.ApplyValues (values);
         }
         
         protected int Insert (T target)
@@ -261,10 +262,12 @@
 
         protected virtual void PrepareUpdateCommand (T target)
         {
+            object [] values = new object [columns.Count + 1];
             for (int i = 0; i < columns.Count; i++) {
-                UpdateCommand.Parameters[i].Value = columns[i].GetValue (target);
+                values [i] = columns[i].GetValue (target);
             }
-            UpdateCommand.Parameters[columns.Count].Value = key.GetValue (target);
+            values[columns.Count] = key.GetValue (target);
+            UpdateCommand.ApplyValues (values);
         }
         
         protected void Update (T target)
@@ -311,7 +314,7 @@
         {
             PrepareSelectCommand ();
             int i = 1;
-            using (IDataReader reader = connection.ExecuteReader (SelectCommand)) {
+            using (IDataReader reader = connection.Query (SelectCommand)) {
                 while (reader.Read ()) {
                     yield return Load (reader, i++);
                 }
@@ -326,7 +329,7 @@
         public IEnumerable<T> FetchRange (int offset, int limit)
         {
             PrepareSelectRangeCommand (offset, limit);
-            using (IDataReader reader = connection.ExecuteReader (SelectRangeCommand)) {
+            using (IDataReader reader = connection.Query (SelectRangeCommand)) {
                 while (reader.Read ()) {
                     yield return Load (reader, offset++);
                 }
@@ -341,7 +344,7 @@
         public T FetchSingle (int id)
         {
             PrepareSelectSingleCommand (id);
-            using (IDataReader reader = connection.ExecuteReader (SelectSingleCommand)) {
+            using (IDataReader reader = connection.Query (SelectSingleCommand)) {
                 if (reader.Read ()) {
                     return Load (reader, id);
                 }



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]