@ -8,6 +8,7 @@ using MediaBrowser.Server.Implementations.Reflection;
using System ;
using System ;
using System.Collections.Generic ;
using System.Collections.Generic ;
using System.Data ;
using System.Data ;
using System.Data.SQLite ;
using System.IO ;
using System.IO ;
using System.Linq ;
using System.Linq ;
using System.Threading ;
using System.Threading ;
@ -53,6 +54,19 @@ namespace MediaBrowser.Server.Implementations.Sqlite
/// </summary>
/// </summary>
private readonly IApplicationPaths _appPaths ;
private readonly IApplicationPaths _appPaths ;
/// <summary>
/// The _save item command
/// </summary>
private SQLiteCommand _saveItemCommand ;
/// <summary>
/// The _delete children command
/// </summary>
private SQLiteCommand _deleteChildrenCommand ;
/// <summary>
/// The _save children command
/// </summary>
private SQLiteCommand _saveChildrenCommand ;
/// <summary>
/// <summary>
/// Initializes a new instance of the <see cref="SQLiteUserDataRepository" /> class.
/// Initializes a new instance of the <see cref="SQLiteUserDataRepository" /> class.
/// </summary>
/// </summary>
@ -100,6 +114,8 @@ namespace MediaBrowser.Server.Implementations.Sqlite
} ;
} ;
RunQueries ( queries ) ;
RunQueries ( queries ) ;
PrepareStatements ( ) ;
}
}
//cascade delete triggers
//cascade delete triggers
@ -116,6 +132,39 @@ namespace MediaBrowser.Server.Implementations.Sqlite
DELETE FROM children WHERE children . child = old . child ;
DELETE FROM children WHERE children . child = old . child ;
END ";
END ";
/// <summary>
/// The _write lock
/// </summary>
private readonly SemaphoreSlim _writeLock = new SemaphoreSlim ( 1 , 1 ) ;
/// <summary>
/// Prepares the statements.
/// </summary>
private void PrepareStatements ( )
{
_saveItemCommand = new SQLiteCommand
{
CommandText = "replace into items (guid, obj_type, data) values (@1, @2, @3)"
} ;
_saveItemCommand . Parameters . Add ( new SQLiteParameter ( "@1" ) ) ;
_saveItemCommand . Parameters . Add ( new SQLiteParameter ( "@2" ) ) ;
_saveItemCommand . Parameters . Add ( new SQLiteParameter ( "@3" ) ) ;
_deleteChildrenCommand = new SQLiteCommand
{
CommandText = "delete from children where guid = @guid"
} ;
_deleteChildrenCommand . Parameters . Add ( new SQLiteParameter ( "@guid" ) ) ;
_saveChildrenCommand = new SQLiteCommand
{
CommandText = "replace into children (guid, child) values (@guid, @child)"
} ;
_saveChildrenCommand . Parameters . Add ( new SQLiteParameter ( "@guid" ) ) ;
_saveChildrenCommand . Parameters . Add ( new SQLiteParameter ( "@child" ) ) ;
}
/// <summary>
/// <summary>
/// Save a standard item in the repo
/// Save a standard item in the repo
/// </summary>
/// </summary>
@ -123,7 +172,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
/// <returns>Task.</returns>
/// <exception cref="System.ArgumentNullException">item</exception>
/// <exception cref="System.ArgumentNullException">item</exception>
public Task SaveItem ( BaseItem item , CancellationToken cancellationToken )
public async Task SaveItem ( BaseItem item , CancellationToken cancellationToken )
{
{
if ( item = = null )
if ( item = = null )
{
{
@ -137,19 +186,51 @@ namespace MediaBrowser.Server.Implementations.Sqlite
cancellationToken . ThrowIfCancellationRequested ( ) ;
cancellationToken . ThrowIfCancellationRequested ( ) ;
return Task . Run ( ( ) = >
{
var serialized = _jsonSerializer . SerializeToBytes ( item ) ;
var serialized = _jsonSerializer . SerializeToBytes ( item ) ;
cancellationToken . ThrowIfCancellationRequested ( ) ;
await _writeLock . WaitAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
var cmd = connection . CreateCommand ( ) ;
SQLiteTransaction transaction = null ;
cmd . CommandText = "replace into items (guid, obj_type, data) values (@1, @2, @3)" ;
cmd . AddParam ( "@1" , item . Id ) ;
try
cmd . AddParam ( "@2" , item . GetType ( ) . FullName ) ;
{
cmd . AddParam ( "@3" , serialized ) ;
transaction = Connection . BeginTransaction ( ) ;
QueueCommand ( cmd ) ;
} ) ;
_saveItemCommand . Parameters [ 0 ] . Value = item . Id ;
_saveItemCommand . Parameters [ 1 ] . Value = item . GetType ( ) . FullName ;
_saveItemCommand . Parameters [ 2 ] . Value = serialized ;
_saveItemCommand . Transaction = transaction ;
await _saveItemCommand . ExecuteNonQueryAsync ( cancellationToken ) ;
transaction . Commit ( ) ;
}
catch ( OperationCanceledException )
{
if ( transaction ! = null )
{
transaction . Rollback ( ) ;
}
}
catch ( Exception e )
{
Logger . ErrorException ( "Failed to save item:" , e ) ;
if ( transaction ! = null )
{
transaction . Rollback ( ) ;
}
}
finally
{
if ( transaction ! = null )
{
transaction . Dispose ( ) ;
}
_writeLock . Release ( ) ;
}
}
}
/// <summary>
/// <summary>
@ -157,6 +238,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite
/// </summary>
/// </summary>
/// <param name="id">The id.</param>
/// <param name="id">The id.</param>
/// <returns>BaseItem.</returns>
/// <returns>BaseItem.</returns>
/// <exception cref="System.ArgumentNullException">id</exception>
/// <exception cref="System.ArgumentException"></exception>
/// <exception cref="System.ArgumentException"></exception>
public BaseItem GetItem ( Guid id )
public BaseItem GetItem ( Guid id )
{
{
@ -189,6 +271,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite
/// </summary>
/// </summary>
/// <param name="id">The id.</param>
/// <param name="id">The id.</param>
/// <returns>BaseItem.</returns>
/// <returns>BaseItem.</returns>
/// <exception cref="System.ArgumentNullException">id</exception>
/// <exception cref="System.ArgumentException"></exception>
/// <exception cref="System.ArgumentException"></exception>
protected BaseItem RetrieveItemInternal ( Guid id )
protected BaseItem RetrieveItemInternal ( Guid id )
{
{
@ -197,7 +280,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite
throw new ArgumentNullException ( "id" ) ;
throw new ArgumentNullException ( "id" ) ;
}
}
using ( var cmd = c onnection. CreateCommand ( ) )
using ( var cmd = C onnection. CreateCommand ( ) )
{
{
cmd . CommandText = "select obj_type,data from items where guid = @guid" ;
cmd . CommandText = "select obj_type,data from items where guid = @guid" ;
var guidParam = cmd . Parameters . Add ( "@guid" , DbType . Guid ) ;
var guidParam = cmd . Parameters . Add ( "@guid" , DbType . Guid ) ;
@ -240,7 +323,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite
throw new ArgumentNullException ( ) ;
throw new ArgumentNullException ( ) ;
}
}
using ( var cmd = c onnection. CreateCommand ( ) )
using ( var cmd = C onnection. CreateCommand ( ) )
{
{
cmd . CommandText = "select obj_type,data from items where guid in (select child from children where guid = @guid)" ;
cmd . CommandText = "select obj_type,data from items where guid in (select child from children where guid = @guid)" ;
var guidParam = cmd . Parameters . Add ( "@guid" , DbType . Guid ) ;
var guidParam = cmd . Parameters . Add ( "@guid" , DbType . Guid ) ;
@ -281,7 +364,7 @@ namespace MediaBrowser.Server.Implementations.Sqlite
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
/// <returns>Task.</returns>
/// <exception cref="System.ArgumentNullException">id</exception>
/// <exception cref="System.ArgumentNullException">id</exception>
public Task SaveChildren ( Guid id , IEnumerable < BaseItem > children , CancellationToken cancellationToken )
public async Task SaveChildren ( Guid id , IEnumerable < BaseItem > children , CancellationToken cancellationToken )
{
{
if ( id = = Guid . Empty )
if ( id = = Guid . Empty )
{
{
@ -300,27 +383,57 @@ namespace MediaBrowser.Server.Implementations.Sqlite
cancellationToken . ThrowIfCancellationRequested ( ) ;
cancellationToken . ThrowIfCancellationRequested ( ) ;
return Task . Run ( ( ) = >
await _writeLock . WaitAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
{
var cmd = connection . CreateCommand ( ) ;
cmd . CommandText = "delete from children where guid = @guid" ;
SQLiteTransaction transaction = null ;
cmd . AddParam ( "@guid" , id ) ;
try
{
transaction = Connection . BeginTransaction ( ) ;
QueueCommand ( cmd ) ;
// Delete exising children
_deleteChildrenCommand . Parameters [ 0 ] . Value = id ;
_deleteChildrenCommand . Transaction = transaction ;
await _deleteChildrenCommand . ExecuteNonQueryAsync ( cancellationToken ) ;
// Save new children
foreach ( var child in children )
foreach ( var child in children )
{
{
var guid = child . Id ;
_saveChildrenCommand . Transaction = transaction ;
cmd = connection . CreateCommand ( ) ;
cmd . AddParam ( "@guid" , id ) ;
_saveChildrenCommand . Parameters [ 0 ] . Value = id ;
cmd . CommandText = "replace into children (guid, child) values (@guid, @child)" ;
_saveChildrenCommand . Parameters [ 1 ] . Value = child . Id ;
var childParam = cmd . Parameters . Add ( "@child" , DbType . Guid ) ;
childParam . Value = guid ;
await _saveChildrenCommand . ExecuteNonQueryAsync ( cancellationToken ) ;
QueueCommand ( cmd ) ;
}
transaction . Commit ( ) ;
}
catch ( OperationCanceledException )
{
if ( transaction ! = null )
{
transaction . Rollback ( ) ;
}
}
catch ( Exception e )
{
Logger . ErrorException ( "Failed to save item:" , e ) ;
if ( transaction ! = null )
{
transaction . Rollback ( ) ;
}
}
finally
{
if ( transaction ! = null )
{
transaction . Dispose ( ) ;
}
_writeLock . Release ( ) ;
}
}
} ) ;
}
}
/// <summary>
/// <summary>