2013-04-29 23:50:38 +02:00
using System ;
2013-03-08 22:24:54 +01:00
using System.Collections.Generic ;
using System.Linq ;
using System.Text ;
using System.IO ;
2013-03-15 21:14:00 +01:00
2013-05-08 20:17:07 +02:00
namespace Duplicati.Library.Main.Database
2013-03-08 22:24:54 +01:00
{
2013-08-22 20:52:54 +02:00
internal class LocalBackupDatabase : LocalDatabase
2013-03-08 22:24:54 +01:00
{
2013-09-07 21:33:59 +02:00
private class PathEntryKeeper
{
2015-01-24 21:59:53 +01:00
public DateTime Lastmodified ;
2013-11-20 19:18:12 +01:00
public long FileID ;
2015-01-24 21:59:53 +01:00
public long Filesize ;
2015-01-25 21:55:15 +01:00
public string Metahash ;
public long Metasize ;
2013-09-07 21:33:59 +02:00
private SortedList < KeyValuePair < long , long >, long > m_versions ;
2015-01-25 21:55:15 +01:00
public PathEntryKeeper ( long fileId , DateTime lastmodified , long filesize , string metahash , long metasize )
2013-09-07 21:33:59 +02:00
{
2013-11-20 19:18:12 +01:00
this . FileID = fileId ;
2015-01-24 21:59:53 +01:00
this . Lastmodified = lastmodified ;
this . Filesize = filesize ;
2015-01-25 21:55:15 +01:00
this . Metahash = metahash ;
this . Metasize = metasize ;
2013-09-07 21:33:59 +02:00
this . m_versions = null ;
}
public long GetFilesetID ( long blocksetId , long metadataId )
{
if ( m_versions == null )
return - 1 ;
long r ;
if (! m_versions . TryGetValue ( new KeyValuePair < long , long >( blocksetId , metadataId ), out r ))
return - 1 ;
else
return r ;
}
public void AddFilesetID ( long blocksetId , long metadataId , long filesetId )
{
if ( m_versions == null )
m_versions = new SortedList < KeyValuePair < long , long >, long >( 1 , new KeyValueComparer ());
m_versions . Add ( new KeyValuePair < long , long >( blocksetId , metadataId ), filesetId );
}
private struct KeyValueComparer : IComparer < KeyValuePair < long , long >>
{
public int Compare ( KeyValuePair < long , long > x , KeyValuePair < long , long > y )
{
return x . Key == y . Key ?
( x . Value == y . Value ?
0
: ( x . Value < y . Value ? - 1 : 1 ))
: ( x . Key < y . Key ? - 1 : 1 );
}
}
}
2013-03-08 22:24:54 +01:00
private readonly System . Data . IDbCommand m_findblockCommand ;
private readonly System . Data . IDbCommand m_findblocksetCommand ;
private readonly System . Data . IDbCommand m_findfilesetCommand ;
private readonly System . Data . IDbCommand m_findmetadatasetCommand ;
private readonly System . Data . IDbCommand m_insertblockCommand ;
private readonly System . Data . IDbCommand m_insertfileCommand ;
private readonly System . Data . IDbCommand m_insertblocksetCommand ;
2013-09-03 23:48:42 +02:00
private readonly System . Data . IDbCommand m_insertblocksetentryFastCommand ;
2013-03-08 22:24:54 +01:00
private readonly System . Data . IDbCommand m_insertblocksetentryCommand ;
private readonly System . Data . IDbCommand m_insertblocklistHashesCommand ;
private readonly System . Data . IDbCommand m_insertmetadatasetCommand ;
2017-01-05 10:03:32 +01:00
private readonly System . Data . IDbCommand m_findfileCommand ;
private readonly System . Data . IDbCommand m_selectfilelastmodifiedCommand ;
2013-03-08 22:24:54 +01:00
private readonly System . Data . IDbCommand m_selectfileHashCommand ;
private readonly System . Data . IDbCommand m_selectblocklistHashesCommand ;
private readonly System . Data . IDbCommand m_insertfileOperationCommand ;
2016-09-15 11:39:27 +02:00
2013-09-07 21:33:59 +02:00
private PathLookupHelper < PathEntryKeeper > m_pathLookup ;
2013-03-15 21:14:00 +01:00
2013-03-10 20:33:30 +01:00
private long m_missingBlockHashes ;
2013-04-06 13:46:58 +02:00
private long m_filesetId ;
2013-03-10 20:33:30 +01:00
2013-05-06 09:58:19 +02:00
public LocalBackupDatabase ( string path , Options options )
2016-04-06 20:40:34 +02:00
: this ( new LocalDatabase ( path , "Backup" , false ), options )
2013-03-08 22:24:54 +01:00
{
2016-04-06 20:40:34 +02:00
this . ShouldCloseConnection = true ;
2013-03-08 22:24:54 +01:00
}
2016-09-15 11:39:27 +02:00
2013-05-06 09:58:19 +02:00
public LocalBackupDatabase ( LocalDatabase db , Options options )
2016-09-15 11:39:27 +02:00
: base ( db )
2013-03-08 22:24:54 +01:00
{
m_findblockCommand = m_connection . CreateCommand ();
m_insertblockCommand = m_connection . CreateCommand ();
m_insertfileCommand = m_connection . CreateCommand ();
m_insertblocksetCommand = m_connection . CreateCommand ();
m_insertmetadatasetCommand = m_connection . CreateCommand ();
m_findblocksetCommand = m_connection . CreateCommand ();
m_findmetadatasetCommand = m_connection . CreateCommand ();
m_findfilesetCommand = m_connection . CreateCommand ();
m_insertblocksetentryCommand = m_connection . CreateCommand ();
m_insertblocklistHashesCommand = m_connection . CreateCommand ();
m_selectblocklistHashesCommand = m_connection . CreateCommand ();
m_insertfileOperationCommand = m_connection . CreateCommand ();
2017-01-05 10:03:32 +01:00
m_findfileCommand = m_connection . CreateCommand ();
m_selectfilelastmodifiedCommand = m_connection . CreateCommand ();
2013-03-08 22:24:54 +01:00
m_selectfileHashCommand = m_connection . CreateCommand ();
2013-09-03 23:48:42 +02:00
m_insertblocksetentryFastCommand = m_connection . CreateCommand ();
2016-09-15 11:39:27 +02:00
m_findblockCommand . CommandText = @"SELECT ""ID"" FROM ""Block"" WHERE ""Hash"" = ? AND ""Size"" = ?" ;
2013-03-08 22:24:54 +01:00
m_findblockCommand . AddParameters ( 2 );
m_findblocksetCommand . CommandText = @"SELECT ""ID"" FROM ""Blockset"" WHERE ""Fullhash"" = ? AND ""Length"" = ?" ;
m_findblocksetCommand . AddParameters ( 2 );
2014-10-30 21:36:27 +01:00
m_findmetadatasetCommand . CommandText = @"SELECT ""A"".""ID"" FROM ""Metadataset"" A, ""BlocksetEntry"" B, ""Block"" C WHERE ""A"".""BlocksetID"" = ""B"".""BlocksetID"" AND ""B"".""BlockID"" = ""C"".""ID"" AND ""C"".""Hash"" = ? AND ""C"".""Size"" = ?" ;
2013-03-08 22:24:54 +01:00
m_findmetadatasetCommand . AddParameters ( 2 );
2013-07-18 23:14:23 +02:00
m_findfilesetCommand . CommandText = @"SELECT ""ID"" FROM ""File"" WHERE ""BlocksetID"" = ? AND ""MetadataID"" = ? AND ""Path"" = ?" ;
2013-03-15 21:14:00 +01:00
m_findfilesetCommand . AddParameters ( 3 );
2013-03-08 22:24:54 +01:00
2013-09-03 23:48:42 +02:00
m_insertblockCommand . CommandText = @"INSERT INTO ""Block"" (""Hash"", ""VolumeID"", ""Size"") VALUES (?, ?, ?); SELECT last_insert_rowid();" ;
2013-03-08 22:24:54 +01:00
m_insertblockCommand . AddParameters ( 3 );
2015-01-24 21:59:53 +01:00
m_insertfileOperationCommand . CommandText = @"INSERT INTO ""FilesetEntry"" (""FilesetID"", ""FileID"", ""Lastmodified"") VALUES (?, ?, ?)" ;
2013-04-04 20:34:26 +02:00
m_insertfileOperationCommand . AddParameters ( 3 );
2013-03-08 22:24:54 +01:00
2013-04-06 13:46:58 +02:00
m_insertfileCommand . CommandText = @"INSERT INTO ""File"" (""Path"",""BlocksetID"", ""MetadataID"") VALUES (?, ? ,?); SELECT last_insert_rowid();" ;
2013-03-08 22:24:54 +01:00
m_insertfileCommand . AddParameters ( 3 );
m_insertblocksetCommand . CommandText = @"INSERT INTO ""Blockset"" (""Length"", ""FullHash"") VALUES (?, ?); SELECT last_insert_rowid();" ;
m_insertblocksetCommand . AddParameters ( 2 );
2013-09-03 23:48:42 +02:00
m_insertblocksetentryFastCommand . CommandText = @"INSERT INTO ""BlocksetEntry"" (""BlocksetID"", ""Index"", ""BlockID"") VALUES (?,?,?)" ;
m_insertblocksetentryFastCommand . AddParameters ( 3 );
2013-03-08 22:24:54 +01:00
m_insertblocksetentryCommand . CommandText = @"INSERT INTO ""BlocksetEntry"" (""BlocksetID"", ""Index"", ""BlockID"") SELECT ? AS A, ? AS B, ""ID"" FROM ""Block"" WHERE ""Hash"" = ? AND ""Size"" = ?" ;
m_insertblocksetentryCommand . AddParameters ( 4 );
m_insertblocklistHashesCommand . CommandText = @"INSERT INTO ""BlocklistHash"" (""BlocksetID"", ""Index"", ""Hash"") VALUES (?, ?, ?)" ;
m_insertblocklistHashesCommand . AddParameters ( 3 );
m_insertmetadatasetCommand . CommandText = @"INSERT INTO ""Metadataset"" (""BlocksetID"") VALUES (?); SELECT last_insert_rowid();" ;
m_insertmetadatasetCommand . AddParameter ();
2017-01-05 10:03:32 +01:00
m_selectfilelastmodifiedCommand . CommandText = @"SELECT ""File"".""ID"", ""FilesetEntry"".""LastModified"" FROM ""File"", ""FilesetEntry"" WHERE ""Path"" = ? AND ""File"".""ID"" = ""FilesetEntry"".""FileID"" AND ""FilesetEntry"".""FilesetID"" = ? LIMIT 1" ;
m_selectfilelastmodifiedCommand . AddParameters ( 2 );
2015-01-24 21:59:53 +01:00
//Need a temporary table with path/lastmodified lookups
2017-01-05 10:03:32 +01:00
m_findfileCommand . CommandText =
2016-05-06 00:14:03 +02:00
@" SELECT ""File"".""ID"" AS ""FileID"", ""FilesetEntry"".""Lastmodified"", ""FileBlockset"".""Length"", ""MetaBlockset"".""Fullhash"" AS ""Metahash"", ""MetaBlockset"".""Length"" AS ""Metasize"" " +
@" FROM ""File"", ""FilesetEntry"", ""Fileset"", ""Blockset"" ""FileBlockset"", ""Metadataset"", ""Blockset"" ""MetaBlockset"" " +
@" WHERE ""File"".""Path"" = ? " +
@" AND ""FilesetEntry"".""FileID"" = ""File"".""ID"" AND ""Fileset"".""ID"" = ""FilesetEntry"".""FilesetID"" " +
@" AND ""FileBlockset"".""ID"" = ""File"".""BlocksetID"" " +
@" AND ""Metadataset"".""ID"" = ""File"".""MetadataID"" AND ""MetaBlockset"".""ID"" = ""Metadataset"".""BlocksetID"" " +
2017-01-11 23:48:41 +01:00
@" ORDER BY ""Fileset"".""Timestamp"" DESC " +
2016-05-06 00:14:03 +02:00
@" LIMIT 1 " ;
2017-01-11 23:48:41 +01:00
m_findfileCommand . AddParameters ( 1 );
2013-03-08 22:24:54 +01:00
2013-06-28 14:56:26 +02:00
m_selectfileHashCommand . CommandText = @"SELECT ""Blockset"".""Fullhash"" FROM ""Blockset"", ""File"" WHERE ""Blockset"".""ID"" = ""File"".""BlocksetID"" AND ""File"".""ID"" = ? " ;
2013-03-08 22:24:54 +01:00
m_selectfileHashCommand . AddParameters ( 1 );
m_selectblocklistHashesCommand . CommandText = @"SELECT ""Hash"" FROM ""BlocklistHash"" WHERE ""BlocksetID"" = ? ORDER BY ""Index"" ASC " ;
m_selectblocklistHashesCommand . AddParameters ( 1 );
2013-08-30 22:19:50 +02:00
}
/// <summary>
/// Builds the lookup tables. Call this method after deleting items, and before processing items
/// </summary>
/// <param name="options">The option settings</param>
public void BuildLookupTable ( Options options )
{
2013-09-07 21:33:59 +02:00
if ( options . UseFilepathCache )
m_pathLookup = new PathLookupHelper < PathEntryKeeper >( true );
2013-05-08 19:57:13 +02:00
2013-03-10 14:27:13 +01:00
2013-03-15 21:14:00 +01:00
//Populate the lookup tables
2013-03-10 14:27:13 +01:00
using ( var cmd = m_connection . CreateCommand ())
2013-03-15 21:14:00 +01:00
{
2015-01-24 21:59:53 +01:00
//Need a temporary table with path/lastmodified lookups
2016-05-06 00:14:03 +02:00
var scantableDefinition =
@"SELECT ""A1"".""ID"" AS ""FileID"", ""A1"".""Lastmodified"" AS ""Lastmodified"", ""A1"".""Path"" AS ""Path"", ""C"".""Length"" AS ""Length"", ""F"".""Fullhash"" AS ""Metahash"", ""F"".""Length"" AS ""Metasize"", ""A1"".""BlocksetID"" " +
@" FROM (SELECT ""File"".""ID"", ""File"".""BlocksetID"", ""File"".""MetadataID"", ""FilesetEntry"".""Lastmodified"", ""File"".""Path"", ""Fileset"".""Timestamp"" " +
@" FROM ""FilesetEntry"", ""Fileset"", ""File"" WHERE ""Fileset"".""ID"" = ""FilesetEntry"".""FilesetID"" AND ""File"".""ID"" = ""FilesetEntry"".""FileID"" " +
@" ) ""A1"" LEFT JOIN " +
@" (SELECT ""File"".""Path"", ""Fileset"".""Timestamp"" " +
@" FROM ""FilesetEntry"", ""Fileset"", ""File"" WHERE ""Fileset"".""ID"" = ""FilesetEntry"".""FilesetID"" AND ""File"".""ID"" = ""FilesetEntry"".""FileID"" " +
@" ) ""A2"" ON ""A1"".""Path"" = ""A2"".""Path"" AND ""A1"".""Timestamp"" < ""A2"".""Timestamp"" " +
@" , ""Blockset"" ""C"", ""Metadataset"" ""E"", ""Blockset"" ""F"" " +
@" WHERE ""A2"".""Path"" IS NULL " +
@" AND ""C"".""ID"" = ""A1"".""BlocksetID"" " +
@" AND ""A1"".""MetadataID"" = ""E"".""ID"" " +
@" AND ""F"".""ID"" = ""E"".""BlocksetID"" " ;
2015-01-24 21:59:53 +01:00
2013-09-07 21:33:59 +02:00
if ( m_pathLookup != null )
2015-01-24 21:59:53 +01:00
using ( new Logging . Timer ( "Build path lastmodified lookup table" ))
2016-05-06 00:14:03 +02:00
using ( var rd = cmd . ExecuteReader ( string . Format ( @" SELECT ""FileID"", ""Lastmodified"", ""Length"", ""Path"", ""Metahash"", ""Metasize"" FROM ({0}) WHERE ""BlocksetID"" >= 0 " , scantableDefinition )))
2013-03-15 21:14:00 +01:00
while ( rd . Read ())
{
2015-01-24 21:59:53 +01:00
var id = rd . GetInt64 ( 0 );
var lastmodified = new DateTime ( rd . GetInt64 ( 1 ), DateTimeKind . Utc );
var filesize = rd . GetInt64 ( 2 );
2015-01-25 21:55:15 +01:00
var path = rd . GetString ( 3 );
var metahash = rd . GetString ( 4 );
var metasize = rd . GetInt64 ( 5 );
m_pathLookup . Insert ( path , new PathEntryKeeper ( id , lastmodified , filesize , metahash , metasize ));
2013-03-15 21:14:00 +01:00
}
2013-03-10 14:27:13 +01:00
2013-09-07 21:33:59 +02:00
if ( m_pathLookup != null )
2014-10-30 15:41:51 +01:00
try
{
using ( new Logging . Timer ( "Build path lookup table" ))
using ( var rd = cmd . ExecuteReader ( string . Format ( @" SELECT ""Path"", ""BlocksetID"", ""MetadataID"", ""ID"" FROM ""File"" " )))
while ( rd . Read ())
2013-09-07 21:33:59 +02:00
{
2014-10-30 15:41:51 +01:00
var path = rd . GetValue ( 0 ). ToString ();
2015-01-24 21:59:53 +01:00
var blocksetid = rd . GetInt64 ( 1 );
var metadataid = rd . GetInt64 ( 2 );
var filesetid = rd . GetInt64 ( 3 );
2014-10-30 15:41:51 +01:00
PathEntryKeeper r ;
if (! m_pathLookup . TryFind ( path , out r ))
{
2015-01-25 21:55:15 +01:00
r = new PathEntryKeeper (- 1 , new DateTime ( 0 , DateTimeKind . Utc ), - 1 , null , - 1 );
2014-10-30 15:41:51 +01:00
r . AddFilesetID ( blocksetid , metadataid , filesetid );
m_pathLookup . Insert ( path , r );
}
else
r . AddFilesetID ( blocksetid , metadataid , filesetid );
2013-09-07 21:33:59 +02:00
}
2014-10-30 15:41:51 +01:00
}
catch ( Exception ex )
{
throw new InvalidDataException ( "Duplicate file entries detected, run repair to fix it" , ex );
}
2013-03-15 21:14:00 +01:00
2015-01-24 21:59:53 +01:00
m_missingBlockHashes = cmd . ExecuteScalarInt64 ( @"SELECT COUNT (*) FROM (SELECT DISTINCT ""Block"".""Hash"", ""Block"".""Size"" FROM ""Block"", ""RemoteVolume"" WHERE ""RemoteVolume"".""ID"" = ""Block"".""VolumeID"" AND ""RemoteVolume"".""State"" NOT IN (?,?,?,?))" , 0 , RemoteVolumeState . Temporary . ToString (), RemoteVolumeState . Uploading . ToString (), RemoteVolumeState . Uploaded . ToString (), RemoteVolumeState . Verified . ToString ());
2014-12-30 16:15:21 +01:00
2015-01-24 21:59:53 +01:00
var tc = cmd . ExecuteScalarInt64 ( @"SELECT COUNT(*) FROM ""Remotevolume"" WHERE ""ID"" IN (SELECT DISTINCT ""VolumeID"" FROM ""Block"") AND ""State"" NOT IN (?, ?, ?, ?);" , 0 , RemoteVolumeState . Temporary . ToString (), RemoteVolumeState . Uploading . ToString (), RemoteVolumeState . Uploaded . ToString (), RemoteVolumeState . Verified . ToString ());
if ( tc > 0 )
2014-12-30 16:15:21 +01:00
throw new InvalidDataException ( "Detected blocks that are not reachable in the block table" );
2013-03-15 21:14:00 +01:00
}
2013-03-08 22:24:54 +01:00
}
/// <summary>
/// Adds a block to the local database, returning a value indicating if the value presents a new block
/// </summary>
/// <param name="key">The block key</param>
/// <param name="archivename">The name of the archive that holds the data</param>
/// <returns>True if the block should be added to the current output</returns>
2013-03-22 21:56:00 +01:00
public bool AddBlock ( string key , long size , long volumeid , System . Data . IDbTransaction transaction = null )
2013-03-08 22:24:54 +01:00
{
2016-12-16 17:30:39 +01:00
m_findblockCommand . Transaction = transaction ;
m_findblockCommand . SetParameterValue ( 0 , key );
m_findblockCommand . SetParameterValue ( 1 , size );
var r = m_findblockCommand . ExecuteScalarInt64 (- 1 );
2013-03-10 14:27:13 +01:00
2015-01-24 21:59:53 +01:00
if ( r == - 1L )
2013-03-08 22:24:54 +01:00
{
m_insertblockCommand . Transaction = transaction ;
m_insertblockCommand . SetParameterValue ( 0 , key );
2013-03-22 21:56:00 +01:00
m_insertblockCommand . SetParameterValue ( 1 , volumeid );
2013-03-08 22:24:54 +01:00
m_insertblockCommand . SetParameterValue ( 2 , size );
2015-01-24 21:59:53 +01:00
r = m_insertblockCommand . ExecuteScalarInt64 ();
2013-03-08 22:24:54 +01:00
return true ;
}
else
{
2014-10-31 11:01:23 +01:00
//Update lookup cache if required
return false ;
2013-03-08 22:24:54 +01:00
}
}
/// <summary>
/// Adds a blockset to the database, returns a value indicating if the blockset is new
/// </summary>
/// <param name="filehash">The hash of the blockset</param>
/// <param name="size">The size of the blockset</param>
/// <param name="fragmentoffset">The fragmentoffset for the last block</param>
/// <param name="fragmenthash">The hash of the fragment</param>
/// <param name="hashes">The list of hashes</param>
/// <param name="blocksetid">The id of the blockset, new or old</param>
/// <returns>True if the blockset was created, false otherwise</returns>
2013-09-03 23:48:42 +02:00
public bool AddBlockset ( string filehash , long size , int blocksize , IEnumerable < string > hashes , IEnumerable < string > blocklistHashes , out long blocksetid , System . Data . IDbTransaction transaction = null )
2013-03-08 22:24:54 +01:00
{
2016-12-16 17:30:39 +01:00
m_findblocksetCommand . Transaction = transaction ;
blocksetid = m_findblocksetCommand . ExecuteScalarInt64 ( null , - 1 , filehash , size );
if ( blocksetid != - 1 )
return false ; //Found it
2013-09-03 23:48:42 +02:00
using ( var tr = new TemporaryTransactionWrapper ( m_connection , transaction ))
2013-03-08 22:24:54 +01:00
{
m_insertblocksetCommand . Transaction = tr . Parent ;
2013-06-09 16:26:08 +02:00
m_insertblocksetCommand . SetParameterValue ( 0 , size );
m_insertblocksetCommand . SetParameterValue ( 1 , filehash );
2015-01-24 21:59:53 +01:00
blocksetid = m_insertblocksetCommand . ExecuteScalarInt64 ();
2013-03-08 22:24:54 +01:00
long ix = 0 ;
if ( blocklistHashes != null )
{
m_insertblocklistHashesCommand . SetParameterValue ( 0 , blocksetid );
m_insertblocklistHashesCommand . Transaction = tr . Parent ;
2013-09-03 23:48:42 +02:00
foreach ( var bh in blocklistHashes )
2013-03-08 22:24:54 +01:00
{
m_insertblocklistHashesCommand . SetParameterValue ( 1 , ix );
m_insertblocklistHashesCommand . SetParameterValue ( 2 , bh );
m_insertblocklistHashesCommand . ExecuteNonQuery ();
ix ++;
}
}
m_insertblocksetentryCommand . SetParameterValue ( 0 , blocksetid );
m_insertblocksetentryCommand . Transaction = tr . Parent ;
2013-09-03 23:48:42 +02:00
m_insertblocksetentryFastCommand . SetParameterValue ( 0 , blocksetid );
m_insertblocksetentryFastCommand . Transaction = tr . Parent ;
2013-03-08 22:24:54 +01:00
ix = 0 ;
long remainsize = size ;
2013-09-03 23:48:42 +02:00
foreach ( var h in hashes )
2013-03-08 22:24:54 +01:00
{
2013-09-03 23:48:42 +02:00
var exsize = remainsize < blocksize ? remainsize : blocksize ;
2016-12-23 17:03:02 +01:00
m_insertblocksetentryCommand . SetParameterValue ( 1 , ix );
m_insertblocksetentryCommand . SetParameterValue ( 2 , h );
m_insertblocksetentryCommand . SetParameterValue ( 3 , exsize );
var c = m_insertblocksetentryCommand . ExecuteNonQuery ();
if ( c != 1 )
2017-01-05 22:15:59 +01:00
{
m_result . AddError ( string . Format ( "Checking errors, related to #1400. Unexpected result count: {0}, expected {1}, hash: {2}, size: {3}, blocksetid: {4}, ix: {5}, fullhash: {6}, fullsize: {7}" , c , 1 , h , exsize , blocksetid , ix , filehash , size ), null );
using ( var cmd = m_connection . CreateCommand ( tr . Parent ))
{
var bid = cmd . ExecuteScalarInt64 ( @"SELECT ""ID"" FROM ""Block"" WHERE ""Hash"" = ?" , - 1 , h );
if ( bid == - 1 )
throw new Exception ( string . Format ( "Could not find any blocks with the given hash: {0}" , h ));
foreach ( var rd in cmd . ExecuteReaderEnumerable ( @"SELECT ""Size"" FROM ""Block"" WHERE ""Hash"" = ?" , h ))
m_result . AddError ( string . Format ( "Found block with ID {0} and hash {1} and size {2}" , bid , h , rd . ConvertValueToInt64 ( 0 , - 1 )), null );
}
throw new Exception ( string . Format ( "Unexpected result count: {0}, expected {1}, check log for more messages" , c , 1 ));
}
2013-09-03 23:48:42 +02:00
2013-03-08 22:24:54 +01:00
ix ++;
remainsize -= blocksize ;
}
2013-06-10 23:04:44 +02:00
tr . Commit ();
2013-03-08 22:24:54 +01:00
}
return true ;
}
/// <summary>
/// Adds a metadata set to the database, and returns a value indicating if the record was new
/// </summary>
/// <param name="hash">The metadata hash</param>
/// <param name="metadataid">The id of the metadata set</param>
/// <returns>True if the set was added to the database, false otherwise</returns>
2016-04-03 23:54:52 +02:00
public bool AddMetadataset ( string filehash , long size , int blocksize , IEnumerable < string > blockhashes , IEnumerable < string > blocklisthashes , out long metadataid , System . Data . IDbTransaction transaction = null )
2013-03-08 22:24:54 +01:00
{
if ( size > 0 )
{
2016-12-16 17:30:39 +01:00
m_findmetadatasetCommand . Transaction = transaction ;
metadataid = m_findmetadatasetCommand . ExecuteScalarInt64 ( null , - 1 , filehash , size );
if ( metadataid != - 1 )
return false ;
2013-03-15 21:14:00 +01:00
2013-03-08 22:24:54 +01:00
long blocksetid ;
2016-04-03 23:54:52 +02:00
AddBlockset ( filehash , size , blocksize , blockhashes , blocklisthashes , out blocksetid , transaction );
2013-03-08 22:24:54 +01:00
using ( var tr = new TemporaryTransactionWrapper ( m_connection , transaction ))
{
m_insertmetadatasetCommand . Transaction = tr . Parent ;
2013-06-09 16:26:08 +02:00
m_insertmetadatasetCommand . SetParameterValue ( 0 , blocksetid );
2015-01-24 21:59:53 +01:00
metadataid = m_insertmetadatasetCommand . ExecuteScalarInt64 ();
2013-06-10 23:04:44 +02:00
tr . Commit ();
2013-03-08 22:24:54 +01:00
}
return true ;
}
metadataid = - 2 ;
return false ;
}
/// <summary>
/// Adds a file record to the database
/// </summary>
/// <param name="filename">The path to the file</param>
2015-01-24 21:59:53 +01:00
/// <param name="lastmodified">The time the file was modified</param>
2013-03-08 22:24:54 +01:00
/// <param name="blocksetID">The ID of the hashkey for the file</param>
/// <param name="metadataID">The ID for the metadata</param>
2013-04-04 20:34:26 +02:00
/// <param name="transaction">The transaction to use for insertion, or null for no transaction</param>
/// <param name="operationId">The operationId to use, or -1 to use the current operation</param>
2015-01-24 21:59:53 +01:00
public void AddFile ( string filename , DateTime lastmodified , long blocksetID , long metadataID , System . Data . IDbTransaction transaction )
2013-09-07 21:33:59 +02:00
{
2015-01-24 21:59:53 +01:00
var fileidobj = - 1L ;
2013-09-07 21:33:59 +02:00
PathEntryKeeper entry = null ;
2015-01-24 21:59:53 +01:00
var entryFound = false ;
2013-09-07 21:33:59 +02:00
if ( m_pathLookup != null )
2013-03-15 21:14:00 +01:00
{
2013-11-20 19:18:12 +01:00
if ( entryFound = ( m_pathLookup . TryFind ( filename , out entry ) && entry != null ))
2013-03-15 21:14:00 +01:00
{
2013-09-07 21:33:59 +02:00
var fid = entry . GetFilesetID ( blocksetID , metadataID );
if ( fid >= 0 )
fileidobj = fid ;
2013-03-15 21:14:00 +01:00
}
}
else
{
m_findfilesetCommand . Transaction = transaction ;
2013-06-01 13:51:43 +02:00
m_findfilesetCommand . SetParameterValue ( 0 , blocksetID );
m_findfilesetCommand . SetParameterValue ( 1 , metadataID );
m_findfilesetCommand . SetParameterValue ( 2 , filename );
2015-01-24 21:59:53 +01:00
fileidobj = m_findfilesetCommand . ExecuteScalarInt64 ();
2013-03-15 21:14:00 +01:00
}
2015-01-24 21:59:53 +01:00
if ( fileidobj == - 1 )
2013-03-08 22:24:54 +01:00
{
2013-09-07 21:33:59 +02:00
using ( var tr = new TemporaryTransactionWrapper ( m_connection , transaction ))
2013-03-08 22:24:54 +01:00
{
m_insertfileCommand . Transaction = tr . Parent ;
m_insertfileCommand . SetParameterValue ( 0 , filename );
m_insertfileCommand . SetParameterValue ( 1 , blocksetID );
m_insertfileCommand . SetParameterValue ( 2 , metadataID );
2015-01-24 21:59:53 +01:00
fileidobj = m_insertfileCommand . ExecuteScalarInt64 ();
2013-06-10 23:04:44 +02:00
tr . Commit ();
2013-03-15 21:14:00 +01:00
// We do not need to update this, because we will not ask for the same file twice
2013-09-07 21:33:59 +02:00
if ( m_pathLookup != null )
{
if (! entryFound )
{
2015-01-25 21:55:15 +01:00
entry = new PathEntryKeeper (- 1 , new DateTime ( 0 , DateTimeKind . Utc ), - 1 , null , - 1 );
2015-01-24 21:59:53 +01:00
entry . AddFilesetID ( blocksetID , metadataID , fileidobj );
2013-09-07 21:33:59 +02:00
m_pathLookup . Insert ( filename , entry );
}
else
2015-01-24 21:59:53 +01:00
entry . AddFilesetID ( blocksetID , metadataID , fileidobj );
2013-09-07 21:33:59 +02:00
}
2013-03-08 22:24:54 +01:00
}
}
2013-07-09 13:51:08 +02:00
2013-03-08 22:24:54 +01:00
m_insertfileOperationCommand . Transaction = transaction ;
2013-04-06 13:46:58 +02:00
m_insertfileOperationCommand . SetParameterValue ( 0 , m_filesetId );
2013-03-15 21:14:00 +01:00
m_insertfileOperationCommand . SetParameterValue ( 1 , fileidobj );
2015-01-24 21:59:53 +01:00
m_insertfileOperationCommand . SetParameterValue ( 2 , lastmodified . ToUniversalTime (). Ticks );
2013-03-08 22:24:54 +01:00
m_insertfileOperationCommand . ExecuteNonQuery ();
}
2015-01-24 21:59:53 +01:00
public void AddUnmodifiedFile ( long fileid , DateTime lastmodified , System . Data . IDbTransaction transaction = null )
2013-03-08 22:24:54 +01:00
{
m_insertfileOperationCommand . Transaction = transaction ;
2013-04-06 13:46:58 +02:00
m_insertfileOperationCommand . SetParameterValue ( 0 , m_filesetId );
2013-03-08 22:24:54 +01:00
m_insertfileOperationCommand . SetParameterValue ( 1 , fileid );
2015-01-24 21:59:53 +01:00
m_insertfileOperationCommand . SetParameterValue ( 2 , lastmodified . ToUniversalTime (). Ticks );
2013-03-08 22:24:54 +01:00
m_insertfileOperationCommand . ExecuteNonQuery ();
}
2015-01-24 21:59:53 +01:00
public void AddDirectoryEntry ( string path , long metadataID , DateTime lastmodified , System . Data . IDbTransaction transaction = null )
2013-03-08 22:24:54 +01:00
{
2015-01-24 21:59:53 +01:00
AddFile ( path , lastmodified , FOLDER_BLOCKSET_ID , metadataID , transaction );
2013-03-08 22:24:54 +01:00
}
2013-03-22 21:56:00 +01:00
2015-01-24 21:59:53 +01:00
public void AddSymlinkEntry ( string path , long metadataID , DateTime lastmodified , System . Data . IDbTransaction transaction = null )
2013-03-08 22:24:54 +01:00
{
2015-01-24 21:59:53 +01:00
AddFile ( path , lastmodified , SYMLINK_BLOCKSET_ID , metadataID , transaction );
2013-03-08 22:24:54 +01:00
}
2017-01-05 10:03:32 +01:00
public long GetFileLastModified ( string path , long filesetid , out DateTime oldModified )
{
m_selectfilelastmodifiedCommand . SetParameterValue ( 0 , path );
m_selectfilelastmodifiedCommand . SetParameterValue ( 1 , filesetid );
using ( var rd = m_selectfilelastmodifiedCommand . ExecuteReader ())
if ( rd . Read ())
{
oldModified = new DateTime ( rd . ConvertValueToInt64 ( 1 ), DateTimeKind . Utc );
return rd . ConvertValueToInt64 ( 0 );
}
oldModified = new DateTime ( 0 , DateTimeKind . Utc );
return - 1 ;
}
public long GetFileEntry ( string path , long filesetid , out DateTime oldModified , out long lastFileSize , out string oldMetahash , out long oldMetasize )
2013-03-08 22:24:54 +01:00
{
2013-09-07 21:33:59 +02:00
if ( m_pathLookup != null )
{
PathEntryKeeper tmp ;
2013-11-20 19:18:12 +01:00
if ( m_pathLookup . TryFind ( path , out tmp ) && tmp != null && tmp . FileID >= 0 )
2013-03-15 21:14:00 +01:00
{
2015-01-24 21:59:53 +01:00
oldModified = tmp . Lastmodified ;
lastFileSize = tmp . Filesize ;
2015-01-25 21:55:15 +01:00
oldMetahash = tmp . Metahash ;
oldMetasize = tmp . Metasize ;
2013-11-20 19:18:12 +01:00
return tmp . FileID ;
2013-03-08 22:24:54 +01:00
}
else
{
2015-01-24 21:59:53 +01:00
oldModified = new DateTime ( 0 , DateTimeKind . Utc );
lastFileSize = - 1 ;
2015-01-25 21:55:15 +01:00
oldMetahash = null ;
oldMetasize = - 1 ;
2013-03-08 22:24:54 +01:00
return - 1 ;
}
2013-09-07 21:33:59 +02:00
}
else
{
2017-01-05 10:03:32 +01:00
m_findfileCommand . SetParameterValue ( 0 , path );
using ( var rd = m_findfileCommand . ExecuteReader ())
2013-09-07 21:33:59 +02:00
if ( rd . Read ())
{
2017-01-05 10:03:32 +01:00
oldModified = new DateTime ( rd . ConvertValueToInt64 ( 1 ), DateTimeKind . Utc );
2015-01-24 21:59:53 +01:00
lastFileSize = rd . GetInt64 ( 2 );
2015-01-25 21:55:15 +01:00
oldMetahash = rd . GetString ( 3 );
oldMetasize = rd . GetInt64 ( 4 );
2017-01-05 10:03:32 +01:00
return rd . ConvertValueToInt64 ( 0 );
2013-09-07 21:33:59 +02:00
}
else
{
2015-01-24 21:59:53 +01:00
oldModified = new DateTime ( 0 , DateTimeKind . Utc );
lastFileSize = - 1 ;
2015-01-25 21:55:15 +01:00
oldMetahash = null ;
oldMetasize = - 1 ;
2013-09-07 21:33:59 +02:00
return - 1 ;
}
}
2013-03-08 22:24:54 +01:00
}
public string GetFileHash ( long fileid )
{
2013-11-20 19:18:12 +01:00
m_selectfileHashCommand . SetParameterValue ( 0 , fileid );
var r = m_selectfileHashCommand . ExecuteScalar ();
if ( r == null || r == DBNull . Value )
return null ;
return r . ToString ();
2013-03-08 22:24:54 +01:00
}
2013-04-27 15:13:14 +02:00
public void WriteFileset ( Volumes . FilesetVolumeWriter filesetvolume , System . Data . IDbTransaction transaction )
2016-09-15 11:39:27 +02:00
{
WriteFileset ( filesetvolume , transaction , m_filesetId );
}
2013-03-08 22:24:54 +01:00
2013-03-11 20:13:40 +01:00
public override void Dispose ()
{
2013-09-07 21:33:59 +02:00
m_pathLookup = null ;
2013-03-15 21:14:00 +01:00
2013-03-11 20:13:40 +01:00
base . Dispose ();
}
2013-07-01 17:04:57 +02:00
2013-04-06 13:46:58 +02:00
private long GetPreviousFilesetID ( System . Data . IDbCommand cmd )
2013-07-01 17:04:57 +02:00
{
return GetPreviousFilesetID ( cmd , OperationTimestamp , m_filesetId );
}
private long GetPreviousFilesetID ( System . Data . IDbCommand cmd , DateTime timestamp , long filesetid )
2013-04-03 21:06:27 +02:00
{
2015-01-24 21:59:53 +01:00
var lastFilesetId = cmd . ExecuteScalarInt64 ( @"SELECT ""ID"" FROM ""Fileset"" WHERE ""Timestamp"" < ? AND ""ID"" != ? ORDER BY ""Timestamp"" DESC " , - 1 , NormalizeDateTimeToEpochSeconds ( timestamp ), filesetid );
2013-04-25 19:45:24 +02:00
return lastFilesetId ;
2013-04-03 21:06:27 +02:00
}
2013-03-11 20:13:40 +01:00
2013-05-25 16:40:15 +02:00
internal void UpdateChangeStatistics ( BackupResults results )
2013-03-08 22:24:54 +01:00
{
2013-09-10 20:42:54 +02:00
using ( var cmd = m_connection . CreateCommand ())
2013-03-08 22:24:54 +01:00
{
2013-09-10 20:42:54 +02:00
var lastFilesetId = GetPreviousFilesetID ( cmd );
2015-01-24 21:59:53 +01:00
results . AddedFolders = cmd . ExecuteScalarInt64 ( @"SELECT COUNT(*) FROM ""File"" INNER JOIN ""FilesetEntry"" ON ""File"".""ID"" = ""FilesetEntry"".""FileID"" WHERE ""FilesetEntry"".""FilesetID"" = ? AND ""File"".""BlocksetID"" = ? AND NOT ""File"".""Path"" IN (SELECT ""Path"" FROM ""File"" INNER JOIN ""FilesetEntry"" ON ""File"".""ID"" = ""FilesetEntry"".""FileID"" WHERE ""FilesetEntry"".""FilesetID"" = ?)" , 0 , m_filesetId , FOLDER_BLOCKSET_ID , lastFilesetId );
results . AddedSymlinks = cmd . ExecuteScalarInt64 ( @"SELECT COUNT(*) FROM ""File"" INNER JOIN ""FilesetEntry"" ON ""File"".""ID"" = ""FilesetEntry"".""FileID"" WHERE ""FilesetEntry"".""FilesetID"" = ? AND ""File"".""BlocksetID"" = ? AND NOT ""File"".""Path"" IN (SELECT ""Path"" FROM ""File"" INNER JOIN ""FilesetEntry"" ON ""File"".""ID"" = ""FilesetEntry"".""FileID"" WHERE ""FilesetEntry"".""FilesetID"" = ?)" , 0 , m_filesetId , SYMLINK_BLOCKSET_ID , lastFilesetId );
2013-04-06 13:46:58 +02:00
2015-01-24 21:59:53 +01:00
results . DeletedFolders = cmd . ExecuteScalarInt64 ( @"SELECT COUNT(*) FROM ""File"" INNER JOIN ""FilesetEntry"" ON ""File"".""ID"" = ""FilesetEntry"".""FileID"" WHERE ""FilesetEntry"".""FilesetID"" = ? AND ""File"".""BlocksetID"" = ? AND NOT ""File"".""Path"" IN (SELECT ""Path"" FROM ""File"" INNER JOIN ""FilesetEntry"" ON ""File"".""ID"" = ""FilesetEntry"".""FileID"" WHERE ""FilesetEntry"".""FilesetID"" = ?)" , 0 , lastFilesetId , FOLDER_BLOCKSET_ID , m_filesetId );
results . DeletedSymlinks = cmd . ExecuteScalarInt64 ( @"SELECT COUNT(*) FROM ""File"" INNER JOIN ""FilesetEntry"" ON ""File"".""ID"" = ""FilesetEntry"".""FileID"" WHERE ""FilesetEntry"".""FilesetID"" = ? AND ""File"".""BlocksetID"" = ? AND NOT ""File"".""Path"" IN (SELECT ""Path"" FROM ""File"" INNER JOIN ""FilesetEntry"" ON ""File"".""ID"" = ""FilesetEntry"".""FileID"" WHERE ""FilesetEntry"".""FilesetID"" = ?)" , 0 , lastFilesetId , SYMLINK_BLOCKSET_ID , m_filesetId );
2013-04-25 19:49:15 +02:00
2013-09-10 20:42:54 +02:00
var subqueryNonFiles = @"SELECT ""File"".""Path"", ""Blockset"".""Fullhash"" FROM ""File"", ""FilesetEntry"", ""Metadataset"", ""Blockset"" WHERE ""File"".""ID"" = ""FilesetEntry"".""FileID"" AND ""Metadataset"".""ID"" = ""File"".""MetadataID"" AND ""File"".""BlocksetID"" = ? AND ""Metadataset"".""BlocksetID"" = ""Blockset"".""ID"" AND ""FilesetEntry"".""FilesetID"" = ? " ;
2015-01-24 21:59:53 +01:00
results . ModifiedFolders = cmd . ExecuteScalarInt64 ( @"SELECT COUNT(*) FROM (" + subqueryNonFiles + @") A, (" + subqueryNonFiles + @") B WHERE ""A"".""Path"" = ""B"".""Path"" AND ""A"".""Fullhash"" != ""B"".""Fullhash"" " , 0 , lastFilesetId , FOLDER_BLOCKSET_ID , m_filesetId , FOLDER_BLOCKSET_ID );
results . ModifiedSymlinks = cmd . ExecuteScalarInt64 ( @"SELECT COUNT(*) FROM (" + subqueryNonFiles + @") A, (" + subqueryNonFiles + @") B WHERE ""A"".""Path"" = ""B"".""Path"" AND ""A"".""Fullhash"" != ""B"".""Fullhash"" " , 0 , lastFilesetId , SYMLINK_BLOCKSET_ID , m_filesetId , SYMLINK_BLOCKSET_ID );
2013-09-09 22:48:26 +02:00
2014-08-12 11:04:57 +02:00
var tmpName1 = "TmpFileList-" + Library . Utility . Utility . ByteArrayAsHexString ( Guid . NewGuid (). ToByteArray ());
var tmpName2 = "TmpFileList-" + Library . Utility . Utility . ByteArrayAsHexString ( Guid . NewGuid (). ToByteArray ());
2013-09-10 20:42:54 +02:00
try
{
var subqueryFiles = @"SELECT ""File"".""Path"", ""A"".""Fullhash"" AS ""Filehash"", ""B"".""Fullhash"" AS ""Metahash"" FROM ""File"", ""FilesetEntry"", ""Blockset"" A, ""Blockset"" B, ""Metadataset"" WHERE ""File"".""ID"" = ""FilesetEntry"".""FileID"" AND ""A"".""ID"" = ""File"".""BlocksetID"" AND ""FilesetEntry"".""FilesetID"" = ? AND ""File"".""MetadataID"" = ""Metadataset"".""ID"" AND ""Metadataset"".""BlocksetID"" = ""B"".""ID"" " ;
2014-08-12 11:04:57 +02:00
cmd . ExecuteNonQuery ( string . Format ( @"CREATE TEMPORARY TABLE ""{0}"" AS " + subqueryFiles , tmpName1 ), lastFilesetId );
cmd . ExecuteNonQuery ( string . Format ( @"CREATE TEMPORARY TABLE ""{0}"" AS " + subqueryFiles , tmpName2 ), m_filesetId );
2013-09-10 20:42:54 +02:00
2015-01-24 21:59:53 +01:00
results . AddedFiles = cmd . ExecuteScalarInt64 ( string . Format ( @"SELECT COUNT(*) FROM ""File"" INNER JOIN ""FilesetEntry"" ON ""File"".""ID"" = ""FilesetEntry"".""FileID"" WHERE ""FilesetEntry"".""FilesetID"" = ? AND ""File"".""BlocksetID"" != ? AND ""File"".""BlocksetID"" != ? AND NOT ""File"".""Path"" IN (SELECT ""Path"" FROM ""{0}"")" , tmpName1 ), 0 , m_filesetId , FOLDER_BLOCKSET_ID , SYMLINK_BLOCKSET_ID );
results . DeletedFiles = cmd . ExecuteScalarInt64 ( string . Format ( @"SELECT COUNT(*) FROM ""{0}"" WHERE ""{0}"".""Path"" NOT IN (SELECT ""Path"" FROM ""File"" INNER JOIN ""FilesetEntry"" ON ""File"".""ID"" = ""FilesetEntry"".""FileID"" WHERE ""FilesetEntry"".""FilesetID"" = ?)" , tmpName1 ), 0 , m_filesetId );
results . ModifiedFiles = cmd . ExecuteScalarInt64 ( string . Format ( @"SELECT COUNT(*) FROM ""{0}"" A, ""{1}"" B WHERE ""A"".""Path"" = ""B"".""Path"" AND (""A"".""Filehash"" != ""B"".""Filehash"" OR ""A"".""Metahash"" != ""B"".""Metahash"")" , tmpName1 , tmpName2 ), 0 );
2013-09-10 20:42:54 +02:00
}
finally
{
2014-08-12 11:04:57 +02:00
try { cmd . ExecuteNonQuery ( string . Format ( @"DROP TABLE IF EXISTS ""{0}"";" , tmpName1 )); }
catch ( Exception ex ) { m_result . AddWarning ( "Dispose temp table error" , ex ); }
try { cmd . ExecuteNonQuery ( string . Format ( @"DROP TABLE IF EXISTS ""{0}"";" , tmpName2 )); }
2013-09-10 20:42:54 +02:00
catch ( Exception ex ) { m_result . AddWarning ( "Dispose temp table error" , ex ); }
}
2013-03-10 20:33:30 +01:00
}
2013-03-08 22:24:54 +01:00
}
2013-03-15 22:04:07 +01:00
public void AppendFilesFromPreviousSet ( System . Data . IDbTransaction transaction , IEnumerable < string > deleted )
2013-07-01 17:04:57 +02:00
{
2013-08-31 15:30:34 +02:00
AppendFilesFromPreviousSet ( transaction , deleted , m_filesetId , - 1 , OperationTimestamp );
2013-07-01 17:04:57 +02:00
}
2013-08-31 15:30:34 +02:00
public void AppendFilesFromPreviousSet ( System . Data . IDbTransaction transaction , IEnumerable < string > deleted , long filesetid , long prevId , DateTime timestamp )
2013-03-15 22:04:07 +01:00
{
using ( var cmd = m_connection . CreateCommand ())
using ( var tr = new TemporaryTransactionWrapper ( m_connection , transaction ))
{
2013-08-31 15:30:34 +02:00
long lastFilesetId = prevId < 0 ? GetPreviousFilesetID ( cmd , timestamp , filesetid ) : prevId ;
2013-03-15 22:04:07 +01:00
cmd . Transaction = tr . Parent ;
2015-01-24 21:59:53 +01:00
cmd . ExecuteNonQuery ( @"INSERT INTO ""FilesetEntry"" (""FilesetID"", ""FileID"", ""Lastmodified"") SELECT ? AS ""FilesetID"", ""FileID"", ""Lastmodified"" FROM (SELECT DISTINCT ""FilesetID"", ""FileID"", ""Lastmodified"" FROM ""FilesetEntry"" WHERE ""FilesetID"" = ? AND ""FileID"" NOT IN (SELECT ""FileID"" FROM ""FilesetEntry"" WHERE ""FilesetID"" = ?)) " , filesetid , lastFilesetId , filesetid );
2013-03-15 22:04:07 +01:00
if ( deleted != null )
{
2013-04-06 13:46:58 +02:00
cmd . CommandText = @"DELETE FROM ""FilesetEntry"" WHERE ""FilesetID"" = ? AND ""FileID"" IN (SELECT ""ID"" FROM ""File"" WHERE ""Path"" = ?) " ;
2013-07-01 17:04:57 +02:00
cmd . AddParameter ( filesetid );
2013-03-15 22:04:07 +01:00
cmd . AddParameter ();
foreach ( string s in deleted )
{
cmd . SetParameterValue ( 1 , s );
cmd . ExecuteNonQuery ();
}
}
2013-06-10 23:04:44 +02:00
tr . Commit ();
2013-03-15 22:04:07 +01:00
}
}
2013-07-22 16:54:19 +02:00
/// <summary>
/// Creates a timestamped backup operation to correctly associate the fileset with the time it was created.
/// </summary>
/// <param name="volumeid">The ID of the fileset volume to update</param>
/// <param name="timestamp">The timestamp of the operation to create</param>
/// <param name="transaction">An optional external transaction</param>
public override long CreateFileset ( long volumeid , DateTime timestamp , System . Data . IDbTransaction transaction = null )
{
return m_filesetId = base . CreateFileset ( volumeid , timestamp , transaction );
}
2016-09-15 11:39:27 +02:00
2013-07-01 17:23:05 +02:00
public IEnumerable < KeyValuePair < long , DateTime >> GetIncompleteFilesets ( System . Data . IDbTransaction transaction )
{
using ( var cmd = m_connection . CreateCommand ())
{
cmd . Transaction = transaction ;
2013-08-31 15:29:52 +02:00
using ( var rd = cmd . ExecuteReader ( @"SELECT DISTINCT ""Fileset"".""ID"", ""Fileset"".""Timestamp"" FROM ""Fileset"", ""RemoteVolume"" WHERE ""RemoteVolume"".""ID"" = ""Fileset"".""VolumeID"" AND ""Fileset"".""ID"" IN (SELECT ""FilesetID"" FROM ""FilesetEntry"") AND (""RemoteVolume"".""State"" = ""Uploading"" OR ""RemoteVolume"".""State"" = ""Temporary"")" ))
2013-07-01 17:23:05 +02:00
while ( rd . Read ())
{
yield return new KeyValuePair < long , DateTime >(
2015-01-24 21:59:53 +01:00
rd . GetInt64 ( 0 ),
ParseFromEpochSeconds ( rd . GetInt64 ( 1 )). ToLocalTime ()
2013-07-01 17:23:05 +02:00
);
}
}
}
2014-12-30 18:29:07 +01:00
public IRemoteVolume GetRemoteVolumeFromName ( string name )
{
using ( var cmd = m_connection . CreateCommand ())
using ( var rd = cmd . ExecuteReader ( @"SELECT ""Name"", ""Hash"", ""Size"" FROM ""RemoteVolume"" WHERE ""Name"" = ?" , name ))
if ( rd . Read ())
2015-04-05 14:33:13 +02:00
return new RemoteVolume ( rd . GetValue ( 0 ). ToString (), rd . GetValue ( 1 ). ToString (), rd . ConvertValueToInt64 ( 2 ));
2014-12-30 18:29:07 +01:00
else
return null ;
}
2016-12-24 11:42:26 +01:00
public RemoteVolumeEntry GetRemoteVolumeFromID ( long id )
{
using ( var cmd = m_connection . CreateCommand ())
using ( var rd = cmd . ExecuteReader ( @"SELECT ""Name"", ""Type"", ""Size"", ""Hash"", ""State"", ""DeleteGraceTime"" FROM ""RemoteVolume"" WHERE ""ID"" = ?" , id ))
if ( rd . Read ())
return new RemoteVolumeEntry (
rd . GetValue ( 0 ). ToString (),
( rd . GetValue ( 3 ) == null || rd . GetValue ( 3 ) == DBNull . Value ) ? null : rd . GetValue ( 3 ). ToString (),
rd . ConvertValueToInt64 ( 2 , - 1 ),
( RemoteVolumeType ) Enum . Parse ( typeof ( RemoteVolumeType ), rd . GetValue ( 1 ). ToString ()),
( RemoteVolumeState ) Enum . Parse ( typeof ( RemoteVolumeState ), rd . GetValue ( 4 ). ToString ()),
new DateTime ( rd . ConvertValueToInt64 ( 5 , 0 ), DateTimeKind . Utc )
);
else
return default ( RemoteVolumeEntry );
}
2014-12-30 18:29:07 +01:00
public IEnumerable < string > GetMissingIndexFiles ()
{
using ( var cmd = m_connection . CreateCommand ())
2015-04-05 14:33:13 +02:00
using ( var rd = cmd . ExecuteReader ( @"SELECT ""Name"" FROM ""RemoteVolume"" WHERE ""Type"" = ? AND NOT ""ID"" IN (SELECT ""BlockVolumeID"" FROM ""IndexBlockLink"") AND ""State"" IN (?,?)" , RemoteVolumeType . Blocks . ToString (), RemoteVolumeState . Uploaded . ToString (), RemoteVolumeState . Verified . ToString ()))
2014-12-30 18:29:07 +01:00
while ( rd . Read ())
yield return rd . GetValue ( 0 ). ToString ();
}
2013-07-01 17:23:05 +02:00
public void LinkFilesetToVolume ( long filesetid , long volumeid , System . Data . IDbTransaction transaction )
{
using ( var cmd = m_connection . CreateCommand ())
{
cmd . Transaction = transaction ;
var c = cmd . ExecuteNonQuery ( @"UPDATE ""Fileset"" SET ""VolumeID"" = ? WHERE ""ID"" = ?" , volumeid , filesetid );
if ( c != 1 )
throw new Exception ( string . Format ( "Failed to link filesetid {0} to volumeid {1}" , filesetid , volumeid ));
}
}
2017-01-06 23:01:54 +01:00
public string GetFirstPath ()
{
using ( var cmd = m_connection . CreateCommand ())
{
cmd . CommandText = string . Format ( @"SELECT ""Path"" FROM ""File"" ORDER BY LENGTH(""Path"") DESC LIMIT 1" );
var v0 = cmd . ExecuteScalar ();
if ( v0 == null || v0 == DBNull . Value )
return null ;
return v0 . ToString ();
}
}
2013-03-08 22:24:54 +01:00
}
}