2013-07-22 16:54:19 +02:00
using System ;
using System.Collections.Generic ;
using System.IO ;
using System.Linq ;
using System.Text ;
namespace Duplicati.Library.Main.Database
{
internal partial class LocalRecreateDatabase : LocalRestoreDatabase
{
2013-09-07 21:33:59 +02:00
private class PathEntryKeeper
{
private SortedList < KeyValuePair < long , long >, long > m_versions ;
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-07-22 16:54:19 +02:00
private System . Data . IDbCommand m_insertFileCommand ;
private System . Data . IDbCommand m_insertFilesetEntryCommand ;
private System . Data . IDbCommand m_insertMetadatasetCommand ;
private System . Data . IDbCommand m_insertBlocksetCommand ;
private System . Data . IDbCommand m_insertBlocklistHashCommand ;
private System . Data . IDbCommand m_updateBlockVolumeCommand ;
private System . Data . IDbCommand m_insertBlockset ;
2016-04-03 23:10:47 +02:00
private System . Data . IDbCommand m_insertSmallBlockset ;
2013-07-22 16:54:19 +02:00
private System . Data . IDbCommand m_findBlocksetCommand ;
private System . Data . IDbCommand m_findMetadatasetCommand ;
private System . Data . IDbCommand m_findFilesetCommand ;
private System . Data . IDbCommand m_findblocklisthashCommand ;
private System . Data . IDbCommand m_findHashBlockCommand ;
private System . Data . IDbCommand m_insertBlockCommand ;
private System . Data . IDbCommand m_insertDuplicateBlockCommand ;
2013-09-07 21:33:59 +02:00
private PathLookupHelper < PathEntryKeeper > m_filesetLookup ;
2013-07-22 16:54:19 +02:00
private string m_tempblocklist ;
2016-04-03 23:10:47 +02:00
private string m_tempsmalllist ;
2013-07-22 16:54:19 +02:00
/// <summary>
/// A lookup table that prevents multiple downloads of the same volume
/// </summary>
private Dictionary < long , long > m_proccessedVolumes ;
// SQL that finds index and block size for all blocklist hashes, based on the temporary hash list
2016-03-26 13:14:05 +01:00
// with vars Used:
// {0} --> Blocksize
// {1} --> BlockHash-Size
// {2} --> Temp-Table
// {3} --> FullBlocklist-BlockCount [equals ({0} / {1}), if SQLite pays respect to ints]
private const string SELECT_BLOCKLIST_ENTRIES =
2016-03-15 21:02:13 +01:00
@"
2017-01-19 10:53:32 +01:00
SELECT
2016-03-17 21:17:22 +01:00
""E"".""BlocksetID"",
2016-03-26 13:14:05 +01:00
""F"".""Index"" + (""E"".""BlocklistIndex"" * {3}) AS ""FullIndex"",
2016-03-17 21:17:22 +01:00
""F"".""BlockHash"",
2016-03-27 12:50:40 +02:00
MIN({0}, ""E"".""Length"" - ((""F"".""Index"" + (""E"".""BlocklistIndex"" * {3})) * {0})) AS ""BlockSize"",
2016-03-17 21:17:22 +01:00
""E"".""Hash"",
""E"".""BlocklistSize"",
""E"".""BlocklistHash""
FROM
(
SELECT * FROM
(
SELECT
""A"".""BlocksetID"",
""A"".""Index"" AS ""BlocklistIndex"",
2016-03-26 13:14:05 +01:00
MIN({3} * {1}, (((""B"".""Length"" + {0} - 1) / {0}) - (""A"".""Index"" * ({3}))) * {1}) AS ""BlocklistSize"",
2016-03-17 21:17:22 +01:00
""A"".""Hash"" AS ""BlocklistHash"",
""B"".""Length""
FROM
""BlocklistHash"" A,
""Blockset"" B
WHERE
""B"".""ID"" = ""A"".""BlocksetID""
) C,
""Block"" D
WHERE
""C"".""BlocklistHash"" = ""D"".""Hash""
AND
""C"".""BlocklistSize"" = ""D"".""Size""
) E,
""{2}"" F
WHERE
""F"".""BlocklistHash"" = ""E"".""Hash""
ORDER BY
""E"".""BlocksetID"",
""FullIndex""
2016-03-15 21:02:13 +01:00
" ;
2013-07-22 16:54:19 +02:00
public LocalRecreateDatabase ( LocalDatabase parentdb , Options options )
2015-04-08 20:33:30 +02:00
: base ( parentdb )
2013-07-22 16:54:19 +02:00
{
m_tempblocklist = "TempBlocklist-" + Library . Utility . Utility . ByteArrayAsHexString ( Guid . NewGuid (). ToByteArray ());
2016-04-03 23:10:47 +02:00
m_tempsmalllist = "TempSmalllist-" + Library . Utility . Utility . ByteArrayAsHexString ( Guid . NewGuid (). ToByteArray ());
2013-07-22 16:54:19 +02:00
2016-02-11 13:29:12 +01:00
using ( var cmd = m_connection . CreateCommand ())
{
2013-07-22 16:54:19 +02:00
cmd . ExecuteNonQuery ( string . Format ( @"CREATE TEMPORARY TABLE ""{0}"" (""BlockListHash"" TEXT NOT NULL, ""BlockHash"" TEXT NOT NULL, ""Index"" INTEGER NOT NULL)" , m_tempblocklist ));
2016-02-13 22:13:40 +01:00
cmd . ExecuteNonQuery ( string . Format ( @"CREATE INDEX ""Index_{0}"" ON ""{0}"" (""BlockListHash"");" , m_tempblocklist ));
2016-04-03 23:10:47 +02:00
cmd . ExecuteNonQuery ( string . Format ( @"CREATE TEMPORARY TABLE ""{0}"" (""FileHash"" TEXT NOT NULL, ""BlockHash"" TEXT NOT NULL, ""BlockSize"" INTEGER NOT NULL)" , m_tempsmalllist ));
cmd . ExecuteNonQuery ( string . Format ( @"CREATE UNIQUE INDEX ""Index_File_{0}"" ON ""{0}"" (""FileHash"", ""BlockSize"");" , m_tempsmalllist ));
cmd . ExecuteNonQuery ( string . Format ( @"CREATE UNIQUE INDEX ""Index_Block_{0}"" ON ""{0}"" (""BlockHash"", ""BlockSize"");" , m_tempsmalllist ));
2016-02-11 13:29:12 +01:00
}
2013-07-22 16:54:19 +02:00
m_insertFileCommand = m_connection . CreateCommand ();
m_insertFilesetEntryCommand = m_connection . CreateCommand ();
m_insertMetadatasetCommand = m_connection . CreateCommand ();
m_insertBlocksetCommand = m_connection . CreateCommand ();
m_insertBlocklistHashCommand = m_connection . CreateCommand ();
m_updateBlockVolumeCommand = m_connection . CreateCommand ();
m_insertBlockset = m_connection . CreateCommand ();
2016-04-03 23:10:47 +02:00
m_insertSmallBlockset = m_connection . CreateCommand ();
2013-07-22 16:54:19 +02:00
m_findBlocksetCommand = m_connection . CreateCommand ();
m_findMetadatasetCommand = m_connection . CreateCommand ();
m_findFilesetCommand = m_connection . CreateCommand ();
m_findblocklisthashCommand = m_connection . CreateCommand ();
m_findHashBlockCommand = m_connection . CreateCommand ();
m_insertBlockCommand = m_connection . CreateCommand ();
m_insertDuplicateBlockCommand = m_connection . CreateCommand ();
m_insertFileCommand . CommandText = @"INSERT INTO ""File"" (""Path"", ""BlocksetID"", ""MetadataID"") VALUES (?,?,?); SELECT last_insert_rowid();" ;
m_insertFileCommand . AddParameters ( 3 );
2015-01-24 21:59:53 +01:00
m_insertFilesetEntryCommand . CommandText = @"INSERT INTO ""FilesetEntry"" (""FilesetID"", ""FileID"", ""Lastmodified"") VALUES (?,?,?)" ;
2013-07-22 16:54:19 +02:00
m_insertFilesetEntryCommand . AddParameters ( 3 );
m_insertMetadatasetCommand . CommandText = @"INSERT INTO ""Metadataset"" (""BlocksetID"") VALUES (?); SELECT last_insert_rowid();" ;
m_insertMetadatasetCommand . AddParameters ( 1 );
m_insertBlocksetCommand . CommandText = @"INSERT INTO ""Blockset"" (""Length"", ""FullHash"") VALUES (?,?); SELECT last_insert_rowid();" ;
m_insertBlocksetCommand . AddParameters ( 2 );
m_insertBlocklistHashCommand . CommandText = @"INSERT INTO ""BlocklistHash"" (""BlocksetID"", ""Index"", ""Hash"") VALUES (?,?,?)" ;
m_insertBlocklistHashCommand . AddParameters ( 3 );
m_updateBlockVolumeCommand . CommandText = @"UPDATE ""Block"" SET ""VolumeID"" = ? WHERE ""Hash"" = ? AND ""Size"" = ?" ;
m_updateBlockVolumeCommand . AddParameters ( 3 );
m_insertBlockset . CommandText = string . Format ( @"INSERT INTO ""{0}"" (""BlocklistHash"", ""BlockHash"", ""Index"") VALUES (?,?,?) " , m_tempblocklist );
m_insertBlockset . AddParameters ( 3 );
2016-04-03 23:10:47 +02:00
m_insertSmallBlockset . CommandText = string . Format ( @"INSERT OR IGNORE INTO ""{0}"" (""FileHash"", ""BlockHash"", ""BlockSize"") VALUES (?,?,?) " , m_tempsmalllist );
m_insertSmallBlockset . AddParameters ( 3 );
2014-11-10 10:31:26 +01:00
m_findBlocksetCommand . CommandText = @"SELECT ""ID"" FROM ""Blockset"" WHERE ""Length"" = ? AND ""FullHash"" = ? " ;
2013-07-22 16:54:19 +02:00
m_findBlocksetCommand . AddParameters ( 2 );
2015-07-12 21:36:48 +02:00
m_findMetadatasetCommand . CommandText = @"SELECT ""Metadataset"".""ID"" FROM ""Metadataset"",""Blockset"" WHERE ""Metadataset"".""BlocksetID"" = ""Blockset"".""ID"" AND ""Blockset"".""FullHash"" = ? AND ""Blockset"".""Length"" = ? " ;
2013-07-22 16:54:19 +02:00
m_findMetadatasetCommand . AddParameters ( 2 );
m_findFilesetCommand . CommandText = @"SELECT ""ID"" FROM ""File"" WHERE ""Path"" = ? AND ""BlocksetID"" = ? AND ""MetadataID"" = ? " ;
m_findFilesetCommand . AddParameters ( 3 );
m_findblocklisthashCommand . CommandText = string . Format ( @"SELECT DISTINCT ""BlockListHash"" FROM ""{0}"" WHERE ""BlockListHash"" = ? " , m_tempblocklist );
m_findblocklisthashCommand . AddParameters ( 1 );
m_findHashBlockCommand . CommandText = @"SELECT ""VolumeID"" FROM ""Block"" WHERE ""Hash"" = ? AND ""Size"" = ? " ;
m_findHashBlockCommand . AddParameters ( 2 );
m_insertBlockCommand . CommandText = @"INSERT INTO ""Block"" (""Hash"", ""Size"", ""VolumeID"") VALUES (?,?,?)" ;
m_insertBlockCommand . AddParameters ( 3 );
2014-11-11 22:29:38 +01:00
m_insertDuplicateBlockCommand . CommandText = @"INSERT INTO ""DuplicateBlock"" (""BlockID"", ""VolumeID"") VALUES ((SELECT ""ID"" FROM ""Block"" WHERE ""Hash"" = ? AND ""Size"" = ?), ?)" ;
2013-07-22 16:54:19 +02:00
m_insertDuplicateBlockCommand . AddParameters ( 3 );
2013-09-07 21:33:59 +02:00
if ( options . UseFilepathCache )
m_filesetLookup = new PathLookupHelper < PathEntryKeeper >();
2013-07-22 16:54:19 +02:00
}
2018-01-21 07:35:07 +01:00
public void FindMissingBlocklistHashes ( long hashsize , long blocksize )
2013-07-22 16:54:19 +02:00
{
2018-01-21 07:35:07 +01:00
using ( var cmd = m_connection . CreateCommand ( Transaction ))
{
2013-07-22 16:54:19 +02:00
//Update all small blocklists and matching blocks
2016-04-03 23:10:47 +02:00
var selectSmallBlocks = string . Format ( @"SELECT ""BlockHash"", ""BlockSize"" FROM ""{0}""" , m_tempsmalllist );
2013-07-22 16:54:19 +02:00
var selectBlockHashes = string . Format (
@"SELECT ""BlockHash"" AS ""FullHash"", ""BlockSize"" AS ""Length"" FROM ( " +
SELECT_BLOCKLIST_ENTRIES +
@" )" ,
2015-04-08 20:33:30 +02:00
blocksize ,
2013-07-22 16:54:19 +02:00
hashsize ,
2016-03-26 13:14:05 +01:00
m_tempblocklist ,
blocksize / hashsize
2013-07-22 16:54:19 +02:00
);
var selectAllBlocks = @"SELECT DISTINCT ""FullHash"", ""Length"" FROM (" + selectBlockHashes + " UNION " + selectSmallBlocks + " )" ;
var selectNewBlocks = string . Format (
@"SELECT ""FullHash"" AS ""Hash"", ""Length"" AS ""Size"", -1 AS ""VolumeID"" " +
@" FROM (SELECT ""A"".""FullHash"", ""A"".""Length"", CASE WHEN ""B"".""Hash"" IS NULL THEN '' ELSE ""B"".""Hash"" END AS ""Hash"", CASE WHEN ""B"".""Size"" is NULL THEN -1 ELSE ""B"".""Size"" END AS ""Size"" FROM ({0}) A" +
2013-07-24 21:25:02 +02:00
@" LEFT OUTER JOIN ""Block"" B ON ""B"".""Hash"" = ""A"".""FullHash"" AND ""B"".""Size"" = ""A"".""Length"" )" +
2013-07-22 16:54:19 +02:00
@" WHERE ""FullHash"" != ""Hash"" AND ""Length"" != ""Size"" " ,
selectAllBlocks
);
var insertBlocksCommand =
@"INSERT INTO ""Block"" (""Hash"", ""Size"", ""VolumeID"") " +
selectNewBlocks ;
// Insert all known blocks into block table with volumeid = -1
cmd . ExecuteNonQuery ( insertBlocksCommand );
var selectBlocklistBlocksetEntries = string . Format (
@"SELECT ""E"".""BlocksetID"" AS ""BlocksetID"", ""D"".""FullIndex"" AS ""Index"", ""F"".""ID"" AS ""BlockID"" FROM ( " +
SELECT_BLOCKLIST_ENTRIES +
2017-01-19 09:55:36 +01:00
@") D, ""BlocklistHash"" E, ""Block"" F, ""Block"" G WHERE ""D"".""BlocksetID"" = ""E"".""BlocksetID"" AND ""D"".""BlocklistHash"" = ""E"".""Hash"" AND ""D"".""BlocklistSize"" = ""G"".""Size"" AND ""D"".""BlocklistHash"" = ""G"".""Hash"" AND ""D"".""Blockhash"" = ""F"".""Hash"" AND ""D"".""BlockSize"" = ""F"".""Size"" " ,
2017-01-19 10:53:32 +01:00
// TODO: The BlocklistHash join seems to be unnecessary, but the join might be required to work around a from really old versions of Duplicati
// this could be used instead
//@") D, ""Block"" WHERE ""BlockQuery"".""BlockHash"" = ""Block"".""Hash"" AND ""BlockQuery"".""BlockSize"" = ""Block"".""Size"" ";
2015-04-08 20:33:30 +02:00
blocksize ,
2013-07-22 16:54:19 +02:00
hashsize ,
2016-03-26 13:14:05 +01:00
m_tempblocklist ,
blocksize / hashsize
2013-07-22 16:54:19 +02:00
);
2016-04-03 23:10:47 +02:00
2013-07-22 16:54:19 +02:00
var selectBlocksetEntries = string . Format (
2016-04-03 23:10:47 +02:00
@"SELECT ""Blockset"".""ID"" AS ""BlocksetID"", 0 AS ""Index"", ""Block"".""ID"" AS ""BlockID"" FROM ""Blockset"", ""Block"", ""{1}"" S WHERE ""Blockset"".""Fullhash"" = ""S"".""FileHash"" AND ""S"".""BlockHash"" = ""Block"".""Hash"" AND ""S"".""BlockSize"" = ""Block"".""Size"" AND ""Blockset"".""Length"" = ""S"".""BlockSize"" AND ""Blockset"".""Length"" <= {0} " ,
blocksize ,
m_tempsmalllist
2013-07-22 16:54:19 +02:00
);
var selectAllBlocksetEntries =
selectBlocklistBlocksetEntries +
@" UNION " +
selectBlocksetEntries ;
var selectFiltered =
2017-01-15 23:08:21 +01:00
@"SELECT DISTINCT ""H"".""BlocksetID"", ""H"".""Index"", ""H"".""BlockID"" FROM (" +
2013-07-22 16:54:19 +02:00
selectAllBlocksetEntries +
2017-01-15 23:08:21 +01:00
@") H WHERE (""H"".""BlocksetID"" || ':' || ""H"".""Index"") NOT IN (SELECT (""ExistingBlocksetEntries"".""BlocksetID"" || ':' || ""ExistingBlocksetEntries"".""Index"") FROM ""BlocksetEntry"" ""ExistingBlocksetEntries"" )" ;
2013-07-22 16:54:19 +02:00
var insertBlocksetEntriesCommand =
2017-01-05 21:41:21 +01:00
@"INSERT INTO ""BlocksetEntry"" (""BlocksetID"", ""Index"", ""BlockID"") " + selectFiltered ;
try
{
cmd . ExecuteNonQuery ( insertBlocksetEntriesCommand );
}
catch ( Exception ex )
{
2017-01-19 09:55:36 +01:00
m_result . AddError ( "Blockset insert failed, comitting temporary tables for trace purposes" , ex );
2017-01-05 21:41:21 +01:00
2017-01-15 23:08:21 +01:00
using ( var fixcmd = m_connection . CreateCommand ())
2017-01-05 21:41:21 +01:00
{
2017-01-15 23:08:21 +01:00
fixcmd . ExecuteNonQuery ( string . Format ( @"CREATE TABLE ""{0}-Failure"" AS SELECT * FROM ""{0}"" " , m_tempblocklist ));
fixcmd . ExecuteNonQuery ( string . Format ( @"CREATE TABLE ""{0}-Failure"" AS SELECT * FROM ""{0}"" " , m_tempsmalllist ));
2017-01-05 21:41:21 +01:00
}
2017-01-19 09:55:36 +01:00
throw new Exception ( "The recreate failed, please create a bug-report from this database and send it to the developers for further analysis" );
2017-01-05 21:41:21 +01:00
}
2013-07-22 16:54:19 +02:00
}
}
2018-01-21 07:35:07 +01:00
public void AddDirectoryEntry ( long filesetid , string path , DateTime time , long metadataid )
2013-07-22 16:54:19 +02:00
{
2018-01-21 07:35:07 +01:00
AddEntry ( FilelistEntryType . Folder , filesetid , path , time , FOLDER_BLOCKSET_ID , metadataid );
2013-07-22 16:54:19 +02:00
}
2018-01-21 07:35:07 +01:00
public void AddSymlinkEntry ( long filesetid , string path , DateTime time , long metadataid )
2013-07-22 16:54:19 +02:00
{
2018-01-21 07:35:07 +01:00
AddEntry ( FilelistEntryType . Symlink , filesetid , path , time , SYMLINK_BLOCKSET_ID , metadataid );
2013-07-22 16:54:19 +02:00
}
2018-01-21 07:35:07 +01:00
public void AddFileEntry ( long filesetid , string path , DateTime time , long blocksetid , long metadataid )
2013-07-22 16:54:19 +02:00
{
2018-01-21 07:35:07 +01:00
AddEntry ( FilelistEntryType . File , filesetid , path , time , blocksetid , metadataid );
2013-07-22 16:54:19 +02:00
}
2018-01-21 07:35:07 +01:00
private void AddEntry ( FilelistEntryType type , long filesetid , string path , DateTime time , long blocksetid , long metadataid )
2013-07-22 16:54:19 +02:00
{
var fileid = - 1L ;
2013-09-07 21:33:59 +02:00
2013-07-22 16:54:19 +02:00
if ( m_filesetLookup != null )
{
2013-09-07 21:33:59 +02:00
PathEntryKeeper e ;
if ( m_filesetLookup . TryFind ( path , out e ))
fileid = e . GetFilesetID ( blocksetid , metadataid );
2013-07-22 16:54:19 +02:00
}
else
{
2018-01-21 07:35:07 +01:00
m_findFilesetCommand . Transaction = Transaction ;
2013-07-22 16:54:19 +02:00
m_findFilesetCommand . SetParameterValue ( 0 , path );
m_findFilesetCommand . SetParameterValue ( 1 , blocksetid );
m_findFilesetCommand . SetParameterValue ( 2 , metadataid );
2015-01-24 21:59:53 +01:00
fileid = m_findFilesetCommand . ExecuteScalarInt64 (- 1 );
2013-07-22 16:54:19 +02:00
}
if ( fileid < 0 )
{
2018-01-21 07:35:07 +01:00
m_insertFileCommand . Transaction = Transaction ;
2013-07-22 16:54:19 +02:00
m_insertFileCommand . SetParameterValue ( 0 , path );
m_insertFileCommand . SetParameterValue ( 1 , blocksetid );
m_insertFileCommand . SetParameterValue ( 2 , metadataid );
2015-01-24 21:59:53 +01:00
fileid = m_insertFileCommand . ExecuteScalarInt64 (- 1 );
2013-07-22 16:54:19 +02:00
if ( m_filesetLookup != null )
2013-09-07 21:33:59 +02:00
{
PathEntryKeeper e ;
if ( m_filesetLookup . TryFind ( path , out e ))
e . AddFilesetID ( blocksetid , metadataid , fileid );
else
{
e = new PathEntryKeeper ();
e . AddFilesetID ( blocksetid , metadataid , fileid );
m_filesetLookup . Insert ( path , e );
}
}
2013-07-22 16:54:19 +02:00
}
2018-01-21 07:35:07 +01:00
m_insertFilesetEntryCommand . Transaction = Transaction ;
2013-07-22 16:54:19 +02:00
m_insertFilesetEntryCommand . SetParameterValue ( 0 , filesetid );
m_insertFilesetEntryCommand . SetParameterValue ( 1 , fileid );
2015-01-24 21:59:53 +01:00
m_insertFilesetEntryCommand . SetParameterValue ( 2 , time . ToUniversalTime (). Ticks );
2013-07-22 16:54:19 +02:00
m_insertFilesetEntryCommand . ExecuteNonQuery ();
}
2018-01-21 07:35:07 +01:00
public long AddMetadataset ( string metahash , long metahashsize , IEnumerable < string > metablocklisthashes , long expectedmetablocklisthashes )
2013-07-22 16:54:19 +02:00
{
var metadataid = - 1L ;
if ( metahash == null )
return metadataid ;
2018-01-21 07:35:07 +01:00
m_findMetadatasetCommand . Transaction = Transaction ;
2016-12-16 17:30:39 +01:00
m_findMetadatasetCommand . SetParameterValue ( 0 , metahash );
m_findMetadatasetCommand . SetParameterValue ( 1 , metahashsize );
metadataid = m_findMetadatasetCommand . ExecuteScalarInt64 (- 1 );
if ( metadataid != - 1 )
return metadataid ;
2016-04-03 23:10:47 +02:00
2018-01-21 07:35:07 +01:00
var blocksetid = AddBlockset ( metahash , metahashsize , metablocklisthashes , expectedmetablocklisthashes );
2013-07-22 16:54:19 +02:00
2018-01-21 07:35:07 +01:00
m_insertMetadatasetCommand . Transaction = Transaction ;
2013-07-22 16:54:19 +02:00
m_insertMetadatasetCommand . SetParameterValue ( 0 , blocksetid );
2015-01-24 21:59:53 +01:00
metadataid = m_insertMetadatasetCommand . ExecuteScalarInt64 (- 1 );
2016-12-16 17:30:39 +01:00
2013-07-22 16:54:19 +02:00
return metadataid ;
}
2018-01-21 07:35:07 +01:00
public long AddBlockset ( string fullhash , long size , IEnumerable < string > blocklisthashes , long expectedblocklisthashes )
2013-07-22 16:54:19 +02:00
{
2018-01-21 07:35:07 +01:00
m_findBlocksetCommand . Transaction = Transaction ;
2016-12-16 17:30:39 +01:00
m_findBlocksetCommand . SetParameterValue ( 0 , size );
m_findBlocksetCommand . SetParameterValue ( 1 , fullhash );
var blocksetid = m_findBlocksetCommand . ExecuteScalarInt64 (- 1 );
if ( blocksetid != - 1 )
return blocksetid ;
2013-07-22 16:54:19 +02:00
2018-01-21 07:35:07 +01:00
m_insertBlocksetCommand . Transaction = Transaction ;
2013-07-22 16:54:19 +02:00
m_insertBlocksetCommand . SetParameterValue ( 0 , size );
m_insertBlocksetCommand . SetParameterValue ( 1 , fullhash );
2015-01-24 21:59:53 +01:00
blocksetid = m_insertBlocksetCommand . ExecuteScalarInt64 (- 1 );
2013-07-22 16:54:19 +02:00
2016-04-04 18:11:48 +02:00
long c = 0 ;
2013-07-22 16:54:19 +02:00
if ( blocklisthashes != null )
{
var index = 0L ;
2018-01-21 07:35:07 +01:00
m_insertBlocklistHashCommand . Transaction = Transaction ;
2013-07-22 16:54:19 +02:00
m_insertBlocklistHashCommand . SetParameterValue ( 0 , blocksetid );
2016-03-15 21:02:13 +01:00
2013-07-22 16:54:19 +02:00
foreach ( var hash in blocklisthashes )
{
if (! string . IsNullOrEmpty ( hash ))
{
2016-03-15 21:02:13 +01:00
c ++;
if ( c <= expectedblocklisthashes )
{
m_insertBlocklistHashCommand . SetParameterValue ( 1 , index ++);
m_insertBlocklistHashCommand . SetParameterValue ( 2 , hash );
m_insertBlocklistHashCommand . ExecuteNonQuery ();
}
2013-07-22 16:54:19 +02:00
}
}
}
2016-04-04 18:11:48 +02:00
if ( c != expectedblocklisthashes )
m_result . AddWarning ( string . Format ( "Mismatching number of blocklist hashes detected on blockset {2}. Expected {0} blocklist hashes, but found {1}" , expectedblocklisthashes , c , blocksetid ), null );
2013-07-22 16:54:19 +02:00
return blocksetid ;
}
2018-01-21 07:35:07 +01:00
public bool UpdateBlock ( string hash , long size , long volumeID )
2013-07-22 16:54:19 +02:00
{
2018-01-21 07:35:07 +01:00
m_findHashBlockCommand . Transaction = Transaction ;
2016-12-16 17:30:39 +01:00
m_findHashBlockCommand . SetParameterValue ( 0 , hash );
m_findHashBlockCommand . SetParameterValue ( 1 , size );
var currentVolumeId = m_findHashBlockCommand . ExecuteScalarInt64 (- 2 );
2013-07-22 16:54:19 +02:00
if ( currentVolumeId == volumeID )
2016-03-17 21:43:09 +01:00
return false ;
2013-07-22 16:54:19 +02:00
if ( currentVolumeId == - 2 )
{
//Insert
2018-01-21 07:35:07 +01:00
m_insertBlockCommand . Transaction = Transaction ;
2013-07-22 16:54:19 +02:00
m_insertBlockCommand . SetParameterValue ( 0 , hash );
m_insertBlockCommand . SetParameterValue ( 1 , size );
m_insertBlockCommand . SetParameterValue ( 2 , volumeID );
m_insertBlockCommand . ExecuteNonQuery ();
2016-03-17 21:43:09 +01:00
return true ;
2013-07-22 16:54:19 +02:00
}
else if ( currentVolumeId == - 1 )
{
//Update
2018-01-21 07:35:07 +01:00
m_updateBlockVolumeCommand . Transaction = Transaction ;
2013-07-22 16:54:19 +02:00
m_updateBlockVolumeCommand . SetParameterValue ( 0 , volumeID );
m_updateBlockVolumeCommand . SetParameterValue ( 1 , hash );
m_updateBlockVolumeCommand . SetParameterValue ( 2 , size );
var c = m_updateBlockVolumeCommand . ExecuteNonQuery ();
if ( c != 1 )
throw new Exception ( string . Format ( "Failed to update table, found {0} entries for key {1} with size {2}" , c , hash , size ));
2016-03-17 21:43:09 +01:00
return true ;
2013-07-22 16:54:19 +02:00
}
else
{
2018-01-21 07:35:07 +01:00
m_insertDuplicateBlockCommand . Transaction = Transaction ;
2013-07-22 16:54:19 +02:00
m_insertDuplicateBlockCommand . SetParameterValue ( 0 , hash );
m_insertDuplicateBlockCommand . SetParameterValue ( 1 , size );
m_insertDuplicateBlockCommand . SetParameterValue ( 2 , volumeID );
m_insertDuplicateBlockCommand . ExecuteNonQuery ();
2016-03-17 21:43:09 +01:00
return false ;
}
2013-07-22 16:54:19 +02:00
}
2016-04-03 23:10:47 +02:00
2018-01-21 07:35:07 +01:00
public void AddSmallBlocksetLink ( string filehash , string blockhash , long blocksize )
2016-04-03 23:10:47 +02:00
{
2018-01-21 07:35:07 +01:00
m_insertSmallBlockset . Transaction = Transaction ;
2016-04-03 23:10:47 +02:00
m_insertSmallBlockset . SetParameterValue ( 0 , filehash );
m_insertSmallBlockset . SetParameterValue ( 1 , blockhash );
m_insertSmallBlockset . SetParameterValue ( 2 , blocksize );
m_insertSmallBlockset . ExecuteNonQuery ();
}
2013-07-22 16:54:19 +02:00
2018-01-21 07:35:07 +01:00
public bool UpdateBlockset ( string hash , IEnumerable < string > blocklisthashes )
2013-07-22 16:54:19 +02:00
{
2018-01-21 07:35:07 +01:00
m_findblocklisthashCommand . Transaction = Transaction ;
2016-12-16 17:30:39 +01:00
m_findblocklisthashCommand . SetParameterValue ( 0 , hash );
var r = m_findblocklisthashCommand . ExecuteScalar ();
if ( r != null && r != DBNull . Value )
return false ;
2013-07-22 16:54:19 +02:00
2018-01-21 07:35:07 +01:00
m_insertBlockset . Transaction = Transaction ;
2013-07-22 16:54:19 +02:00
m_insertBlockset . SetParameterValue ( 0 , hash );
var index = 0L ;
foreach ( var s in blocklisthashes )
{
m_insertBlockset . SetParameterValue ( 1 , s );
m_insertBlockset . SetParameterValue ( 2 , index ++);
m_insertBlockset . ExecuteNonQuery ();
}
2016-03-17 21:43:09 +01:00
return true ;
2013-07-22 16:54:19 +02:00
}
public IEnumerable < string > GetBlockLists ( long volumeid )
{
using ( var cmd = m_connection . CreateCommand ())
{
2016-09-15 11:39:27 +02:00
cmd . CommandText = string . Format ( @"SELECT DISTINCT ""BlocklistHash"".""Hash"" FROM ""BlocklistHash"", ""Block"" WHERE ""Block"".""Hash"" = ""BlocklistHash"".""Hash"" AND ""Block"".""VolumeID"" = ?" );
2013-07-22 16:54:19 +02:00
cmd . AddParameter ( volumeid );
using ( var rd = cmd . ExecuteReader ())
while ( rd . Read ())
yield return rd . GetValue ( 0 ). ToString ();
}
}
2016-09-15 11:39:27 +02:00
public IEnumerable < IRemoteVolume > GetMissingBlockListVolumes ( int passNo , long blocksize , long hashsize )
2013-07-22 16:54:19 +02:00
{
using ( var cmd = m_connection . CreateCommand ())
{
var selectCommand = @"SELECT DISTINCT ""RemoteVolume"".""Name"", ""RemoteVolume"".""Hash"", ""RemoteVolume"".""Size"", ""RemoteVolume"".""ID"" FROM ""RemoteVolume""" ;
var missingBlocklistEntries =
2016-09-15 11:39:27 +02:00
string . Format (
@"SELECT ""BlocklistHash"".""Hash"" FROM ""BlocklistHash"" LEFT OUTER JOIN ""BlocksetEntry"" ON ""BlocksetEntry"".""Index"" = (""BlocklistHash"".""Index"" * {0}) AND ""BlocksetEntry"".""BlocksetID"" = ""BlocklistHash"".""BlocksetID"" WHERE ""BlocksetEntry"".""BlocksetID"" IS NULL" ,
blocksize / hashsize
);
2016-05-02 20:30:38 +02:00
2013-07-22 16:54:19 +02:00
var missingBlockInfo =
@"SELECT ""VolumeID"" FROM ""Block"" WHERE ""VolumeID"" < 0 " ;
var missingBlocklistVolumes = string . Format (
@"SELECT ""VolumeID"" FROM ""Block"", (" +
missingBlocklistEntries +
@") A WHERE ""A"".""Hash"" = ""Block"".""Hash"" "
);
var countMissingInformation = string . Format (
@"SELECT COUNT(*) FROM (SELECT DISTINCT ""VolumeID"" FROM ({0} UNION {1}))" ,
missingBlockInfo ,
missingBlocklistEntries );
if ( passNo == 0 )
{
// On the first pass, we select all the volumes we know we need,
// which may be an empty list
cmd . CommandText = string . Format ( selectCommand + @" WHERE ""ID"" IN ({0})" , missingBlocklistVolumes );
// Reset the list
m_proccessedVolumes = new Dictionary < long , long >();
}
else
{
//On anything but the first pass, we check if we are done
2015-01-24 21:59:53 +01:00
var r = cmd . ExecuteScalarInt64 ( countMissingInformation , 0 );
if ( r == 0 )
2013-07-22 16:54:19 +02:00
yield break ;
if ( passNo == 1 )
{
// On the second pass, we select all volumes that are not mentioned in the db
var mentionedVolumes =
@"SELECT DISTINCT ""VolumeID"" FROM ""Block"" " ;
cmd . CommandText = string . Format ( selectCommand + @" WHERE ""ID"" NOT IN ({0}) AND ""Type"" = ? " , mentionedVolumes );
cmd . AddParameter ( RemoteVolumeType . Blocks . ToString ());
}
else
{
// On the final pass, we select all volumes
// the filter will ensure that we do not download anything twice
cmd . CommandText = selectCommand + @" WHERE ""Type"" = ?" ;
cmd . AddParameter ( RemoteVolumeType . Blocks . ToString ());
}
}
using ( var rd = cmd . ExecuteReader ())
{
while ( rd . Read ())
{
2015-01-24 21:59:53 +01:00
var volumeID = rd . GetInt64 ( 3 );
2013-07-22 16:54:19 +02:00
// Guard against multiple downloads of the same file
if (! m_proccessedVolumes . ContainsKey ( volumeID ))
{
m_proccessedVolumes . Add ( volumeID , volumeID );
yield return new RemoteVolume (
2015-01-24 21:59:53 +01:00
rd . GetString ( 0 ),
2015-05-17 13:38:25 +02:00
rd . ConvertValueToString ( 1 ),
2015-01-24 21:59:53 +01:00
rd . ConvertValueToInt64 ( 2 , - 1 )
2013-07-22 16:54:19 +02:00
);
}
}
}
}
}
public override void Dispose ()
{
using ( var cmd = m_connection . CreateCommand ())
{
if ( m_tempblocklist != null )
try
{
2013-08-23 23:36:25 +02:00
cmd . CommandText = string . Format ( @"DROP TABLE IF EXISTS ""{0}""" , m_tempblocklist );
2013-07-22 16:54:19 +02:00
cmd . ExecuteNonQuery ();
}
2013-08-23 23:36:25 +02:00
catch { }
2013-07-22 16:54:19 +02:00
finally { m_tempblocklist = null ; }
2016-04-03 23:10:47 +02:00
if ( m_tempsmalllist != null )
try
{
cmd . CommandText = string . Format ( @"DROP TABLE IF EXISTS ""{0}""" , m_tempsmalllist );
cmd . ExecuteNonQuery ();
}
catch { }
finally { m_tempsmalllist = null ; }
2013-07-22 16:54:19 +02:00
}
2016-10-17 15:32:30 +02:00
2013-07-22 16:54:19 +02:00
base . Dispose ();
}
}
}