/*************************************************************************
*    UrBackup - Client/Server backup system
*    Copyright (C) 2011-2016 Martin Raiber
*
*    This program is free software: you can redistribute it and/or modify
*    it under the terms of the GNU Affero General Public License as published by
*    the Free Software Foundation, either version 3 of the License, or
*    (at your option) any later version.
*
*    This program is distributed in the hope that it will be useful,
*    but WITHOUT ANY WARRANTY; without even the implied warranty of
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*    GNU Affero General Public License for more details.
*
*    You should have received a copy of the GNU Affero General Public License
*    along with this program.  If not, see <http://www.gnu.org/licenses/>.
**************************************************************************/

#include "ServerFilesDao.h"
#include "../../stringtools.h"
#include <assert.h>
#include <string.h>

/**
* @-SQLGenTempSetup
* @sql
*		CREATE TEMPORARY TABLE files_cont_path_lookup ( fullpath TEXT, entryid INTEGER);
*/

/**
* @-SQLGenTempSetup
* @sql
*		CREATE TEMPORARY TABLE files_last ( fullpath TEXT, hashpath TEXT, shahash BLOB, filesize INTEGER);
*/

/**
* @-SQLGenTempSetup
* @sql
*		CREATE TEMPORARY TABLE backups(id INTEGER PRIMARY KEY);
*/


const int ServerFilesDao::c_direction_incoming = 0;
const int ServerFilesDao::c_direction_outgoing = 1;
const int ServerFilesDao::c_direction_outgoing_nobackupstat = 2;

ServerFilesDao::ServerFilesDao(IDatabase * db)
	: db(db)
{
	prepareQueries();
}

ServerFilesDao::~ServerFilesDao()
{
	destroyQueries();
}

int64 ServerFilesDao::getLastId()
{
	return db->getLastInsertID();
}

int ServerFilesDao::getLastChanges()
{
	return db->getLastChanges();
}

void ServerFilesDao::BeginWriteTransaction()
{
	db->BeginWriteTransaction();
}

void ServerFilesDao::endTransaction()
{
	db->EndTransaction();
}

IDatabase * ServerFilesDao::getDatabase()
{
	return db;
}

/**
* @-SQLGenAccess
* @func void ServerFilesDao::setNextEntry
* @sql
*      UPDATE files SET next_entry=:next_entry(int64) WHERE id=:id(int64)
*/
void ServerFilesDao::setNextEntry(int64 next_entry, int64 id)
{
	if(q_setNextEntry==NULL)
	{
		q_setNextEntry=db->Prepare("UPDATE files SET next_entry=? WHERE id=?", false);
	}
	q_setNextEntry->Bind(next_entry);
	q_setNextEntry->Bind(id);
	q_setNextEntry->Write();
	q_setNextEntry->Reset();
}

/**
* @-SQLGenAccess
* @func void ServerFilesDao::setPrevEntry
* @sql
*      UPDATE files SET prev_entry=:prev_entry(int64) WHERE id=:id(int64)
*/
void ServerFilesDao::setPrevEntry(int64 prev_entry, int64 id)
{
	if(q_setPrevEntry==NULL)
	{
		q_setPrevEntry=db->Prepare("UPDATE files SET prev_entry=? WHERE id=?", false);
	}
	q_setPrevEntry->Bind(prev_entry);
	q_setPrevEntry->Bind(id);
	q_setPrevEntry->Write();
	q_setPrevEntry->Reset();
}

/**
* @-SQLGenAccess
* @func void ServerFilesDao::setPointedTo
* @sql
*      UPDATE files SET pointed_to=:pointed_to(int64) WHERE id=:id(int64)
*/
void ServerFilesDao::setPointedTo(int64 pointed_to, int64 id)
{
	if(q_setPointedTo==NULL)
	{
		q_setPointedTo=db->Prepare("UPDATE files SET pointed_to=? WHERE id=?", false);
	}
	q_setPointedTo->Bind(pointed_to);
	q_setPointedTo->Bind(id);
	q_setPointedTo->Write();
	q_setPointedTo->Reset();
}

/**
* @-SQLGenAccess
* @func int64 ServerFilesDao::getPointedTo
* @return int64 pointed_to
* @sql
*      SELECT pointed_to FROM files WHERE id=:id(int64)
*/
ServerFilesDao::CondInt64 ServerFilesDao::getPointedTo(int64 id)
{
	if(q_getPointedTo==NULL)
	{
		q_getPointedTo=db->Prepare("SELECT pointed_to FROM files WHERE id=?", false);
	}
	q_getPointedTo->Bind(id);
	db_results res=q_getPointedTo->Read();
	q_getPointedTo->Reset();
	CondInt64 ret = { false, 0 };
	if(!res.empty())
	{
		ret.exists=true;
		ret.value=watoi64(res[0]["pointed_to"]);
	}
	return ret;
}

/**
* @-SQLGenAccess
* @func void ServerFilesDao::delFileEntry
* @sql
*	   DELETE FROM files WHERE id=:id(int64)
*/
void ServerFilesDao::delFileEntry(int64 id)
{
	if(q_delFileEntry==NULL)
	{
		q_delFileEntry=db->Prepare("DELETE FROM files WHERE id=?", false);
	}
	q_delFileEntry->Bind(id);
	q_delFileEntry->Write();
	q_delFileEntry->Reset();
}

/**
* @-SQLGenAccess
* @func SFindFileEntry ServerFilesDao::getFileEntry
* @return int64 id, string shahash, int backupid, int clientid, string fullpath, string hashpath, int64 filesize, int64 next_entry, int64 prev_entry, int64 rsize, int incremental, int pointed_to
* @sql
*	   SELECT id, shahash, backupid, clientid, fullpath, hashpath, filesize, next_entry, prev_entry, rsize, incremental, pointed_to
*      FROM files WHERE id=:id(int64)
*/
ServerFilesDao::SFindFileEntry ServerFilesDao::getFileEntry(int64 id)
{
	if(q_getFileEntry==NULL)
	{
		q_getFileEntry=db->Prepare("SELECT id, shahash, backupid, clientid, fullpath, hashpath, filesize, next_entry, prev_entry, rsize, incremental, pointed_to FROM files WHERE id=?", false);
	}
	q_getFileEntry->Bind(id);
	db_results res=q_getFileEntry->Read();
	q_getFileEntry->Reset();
	SFindFileEntry ret = { false, 0, "", 0, 0, "", "", 0, 0, 0, 0, 0, 0 };
	if(!res.empty())
	{
		ret.exists=true;
		ret.id=watoi64(res[0]["id"]);
		ret.shahash=res[0]["shahash"];
		ret.backupid=watoi(res[0]["backupid"]);
		ret.clientid=watoi(res[0]["clientid"]);
		ret.fullpath=res[0]["fullpath"];
		ret.hashpath=res[0]["hashpath"];
		ret.filesize=watoi64(res[0]["filesize"]);
		ret.next_entry=watoi64(res[0]["next_entry"]);
		ret.prev_entry=watoi64(res[0]["prev_entry"]);
		ret.rsize=watoi64(res[0]["rsize"]);
		ret.incremental=watoi(res[0]["incremental"]);
		ret.pointed_to=watoi(res[0]["pointed_to"]);
	}
	return ret;
}

/**
* @-SQLGenAccess
* @func SStatFileEntry ServerFilesDao::getStatFileEntry
* @return int64 id, int backupid, int clientid, int64 filesize, int64 rsize, string shahash, int64 next_entry, int64 prev_entry
* @sql
*	   SELECT id, backupid, clientid, filesize, rsize, shahash, next_entry, prev_entry
*      FROM files WHERE id=:id(int64)
*/
ServerFilesDao::SStatFileEntry ServerFilesDao::getStatFileEntry(int64 id)
{
	if(q_getStatFileEntry==NULL)
	{
		q_getStatFileEntry=db->Prepare("SELECT id, backupid, clientid, filesize, rsize, shahash, next_entry, prev_entry FROM files WHERE id=?", false);
	}
	q_getStatFileEntry->Bind(id);
	db_results res=q_getStatFileEntry->Read();
	q_getStatFileEntry->Reset();
	SStatFileEntry ret = { false, 0, 0, 0, 0, 0, "", 0, 0 };
	if(!res.empty())
	{
		ret.exists=true;
		ret.id=watoi64(res[0]["id"]);
		ret.backupid=watoi(res[0]["backupid"]);
		ret.clientid=watoi(res[0]["clientid"]);
		ret.filesize=watoi64(res[0]["filesize"]);
		ret.rsize=watoi64(res[0]["rsize"]);
		ret.shahash=res[0]["shahash"];
		ret.next_entry=watoi64(res[0]["next_entry"]);
		ret.prev_entry=watoi64(res[0]["prev_entry"]);
	}
	return ret;
}


/**
* @-SQLGenAccess
* @func void ServerFilesDao::addFileEntry
* @sql
*	   INSERT INTO files (backupid, fullpath, hashpath, shahash, filesize, rsize, clientid, incremental, next_entry, prev_entry, pointed_to)
*      VALUES (:backupid(int), :fullpath(string), :hashpath(string), :shahash(blob),
*				:filesize(int64), :rsize(int64), :clientid(int), :incremental(int), :next_entry(int64), :prev_entry(int64), :pointed_to(int))
*/
void ServerFilesDao::addFileEntry(int backupid, const std::string& fullpath, const std::string& hashpath, const std::string& shahash, int64 filesize, int64 rsize, int clientid, int incremental, int64 next_entry, int64 prev_entry, int pointed_to)
{
	if(q_addFileEntry==NULL)
	{
		q_addFileEntry=db->Prepare("INSERT INTO files (backupid, fullpath, hashpath, shahash, filesize, rsize, clientid, incremental, next_entry, prev_entry, pointed_to) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", false);
	}
	q_addFileEntry->Bind(backupid);
	q_addFileEntry->Bind(fullpath);
	q_addFileEntry->Bind(hashpath);
	q_addFileEntry->Bind(shahash.c_str(), (_u32)shahash.size());
	q_addFileEntry->Bind(filesize);
	q_addFileEntry->Bind(rsize);
	q_addFileEntry->Bind(clientid);
	q_addFileEntry->Bind(incremental);
	q_addFileEntry->Bind(next_entry);
	q_addFileEntry->Bind(prev_entry);
	q_addFileEntry->Bind(pointed_to);
	q_addFileEntry->Write();
	q_addFileEntry->Reset();
}

/**
* @-SQLGenAccessNoCheck
* @func bool ServerFilesDao::createTemporaryPathLookupTable
* @sql
*      CREATE TEMPORARY TABLE files_cont_path_lookup ( fullpath TEXT, entryid INTEGER);
*/
bool ServerFilesDao::createTemporaryPathLookupTable(void)
{
	if(q_createTemporaryPathLookupTable==NULL)
	{
		q_createTemporaryPathLookupTable=db->Prepare("CREATE TEMPORARY TABLE files_cont_path_lookup ( fullpath TEXT, entryid INTEGER);", false);
	}
	bool ret = q_createTemporaryPathLookupTable->Write();
	return ret;
}

/**
* @-SQLGenAccessNoCheck
* @func void ServerFilesDao::dropTemporaryPathLookupTable
* @sql
*      DROP TABLE files_cont_path_lookup
*/
void ServerFilesDao::dropTemporaryPathLookupTable(void)
{
	if(q_dropTemporaryPathLookupTable==NULL)
	{
		q_dropTemporaryPathLookupTable=db->Prepare("DROP TABLE files_cont_path_lookup", false);
	}
	q_dropTemporaryPathLookupTable->Write();
}

/**
* @-SQLGenAccessNoCheck
* @func void ServerFilesDao::dropTemporaryPathLookupIndex
* @sql
*      DROP INDEX files_cont_path_lookup_idx
*/
void ServerFilesDao::dropTemporaryPathLookupIndex(void)
{
	if(q_dropTemporaryPathLookupIndex==NULL)
	{
		q_dropTemporaryPathLookupIndex=db->Prepare("DROP INDEX files_cont_path_lookup_idx", false);
	}
	q_dropTemporaryPathLookupIndex->Write();
}

/**
* @-SQLGenAccessNoCheck
* @func void ServerFilesDao::populateTemporaryPathLookupTable
* @sql
*      INSERT INTO files_cont_path_lookup (fullpath, entryid)
*		 SELECT fullpath, id AS entryid FROM files WHERE backupid=:backupid(int)
*/
void ServerFilesDao::populateTemporaryPathLookupTable(int backupid)
{
	if(q_populateTemporaryPathLookupTable==NULL)
	{
		q_populateTemporaryPathLookupTable=db->Prepare("INSERT INTO files_cont_path_lookup (fullpath, entryid) SELECT fullpath, id AS entryid FROM files WHERE backupid=?", false);
	}
	q_populateTemporaryPathLookupTable->Bind(backupid);
	q_populateTemporaryPathLookupTable->Write();
	q_populateTemporaryPathLookupTable->Reset();
}

/**
* @-SQLGenAccessNoCheck
* @func bool ServerFilesDao::createTemporaryPathLookupIndex
* @sql
*      CREATE INDEX files_cont_path_lookup_idx ON files_cont_path_lookup ( fullpath );
*/
bool ServerFilesDao::createTemporaryPathLookupIndex(void)
{
	if(q_createTemporaryPathLookupIndex==NULL)
	{
		q_createTemporaryPathLookupIndex=db->Prepare("CREATE INDEX files_cont_path_lookup_idx ON files_cont_path_lookup ( fullpath );", false);
	}
	bool ret = q_createTemporaryPathLookupIndex->Write();
	return ret;
}

/**
* @-SQLGenAccess
* @func int64 ServerFilesDao::lookupEntryIdByPath
* @return int64 entryid
* @sql
*       SELECT entryid FROM files_cont_path_lookup WHERE fullpath=:fullpath(string)
*/
ServerFilesDao::CondInt64 ServerFilesDao::lookupEntryIdByPath(const std::string& fullpath)
{
	if(q_lookupEntryIdByPath==NULL)
	{
		q_lookupEntryIdByPath=db->Prepare("SELECT entryid FROM files_cont_path_lookup WHERE fullpath=?", false);
	}
	q_lookupEntryIdByPath->Bind(fullpath);
	db_results res=q_lookupEntryIdByPath->Read();
	q_lookupEntryIdByPath->Reset();
	CondInt64 ret = { false, 0 };
	if(!res.empty())
	{
		ret.exists=true;
		ret.value=watoi64(res[0]["entryid"]);
	}
	return ret;
}

/**
* @-SQLGenAccess
* @func void ServerFilesDao::addIncomingFile
* @sql
*       INSERT INTO files_incoming_stat (filesize, clientid, backupid, existing_clients, direction, incremental)
*       VALUES (:filesize(int64), :clientid(int), :backupid(int), :existing_clients(string), :direction(int), :incremental(int))
*/
void ServerFilesDao::addIncomingFile(int64 filesize, int clientid, int backupid, const std::string& existing_clients, int direction, int incremental)
{
	if(q_addIncomingFile==NULL)
	{
		q_addIncomingFile=db->Prepare("INSERT INTO files_incoming_stat (filesize, clientid, backupid, existing_clients, direction, incremental) VALUES (?, ?, ?, ?, ?, ?)", false);
	}
	q_addIncomingFile->Bind(filesize);
	q_addIncomingFile->Bind(clientid);
	q_addIncomingFile->Bind(backupid);
	q_addIncomingFile->Bind(existing_clients);
	q_addIncomingFile->Bind(direction);
	q_addIncomingFile->Bind(incremental);
	q_addIncomingFile->Write();
	q_addIncomingFile->Reset();
}

/**
* @-SQLGenAccess
* @func int64 ServerFilesDao::getIncomingStatsCount
* @return int64 c
* @sql
*       SELECT COUNT(*) AS c
*       FROM files_incoming_stat
*/
ServerFilesDao::CondInt64 ServerFilesDao::getIncomingStatsCount(void)
{
	if(q_getIncomingStatsCount==NULL)
	{
		q_getIncomingStatsCount=db->Prepare("SELECT COUNT(*) AS c FROM files_incoming_stat", false);
	}
	db_results res=q_getIncomingStatsCount->Read();
	CondInt64 ret = { false, 0 };
	if(!res.empty())
	{
		ret.exists=true;
		ret.value=watoi64(res[0]["c"]);
	}
	return ret;
}

/**
* @-SQLGenAccess
* @func void ServerFilesDao::delIncomingStatEntry
* @sql
*       DELETE FROM files_incoming_stat WHERE id=:id(int64)
*/
void ServerFilesDao::delIncomingStatEntry(int64 id)
{
	if(q_delIncomingStatEntry==NULL)
	{
		q_delIncomingStatEntry=db->Prepare("DELETE FROM files_incoming_stat WHERE id=?", false);
	}
	q_delIncomingStatEntry->Bind(id);
	q_delIncomingStatEntry->Write();
	q_delIncomingStatEntry->Reset();
}

/**
* @-SQLGenAccess
* @func vector<SIncomingStat> ServerFilesDao::getIncomingStats
* @return int64 id, int64 filesize, int clientid, int backupid, string existing_clients, int direction, int incremental
* @sql
*       SELECT id, filesize, clientid, backupid, existing_clients, direction, incremental
*       FROM files_incoming_stat LIMIT 10000
*/
std::vector<ServerFilesDao::SIncomingStat> ServerFilesDao::getIncomingStats(void)
{
	if(q_getIncomingStats==NULL)
	{
		q_getIncomingStats=db->Prepare("SELECT id, filesize, clientid, backupid, existing_clients, direction, incremental FROM files_incoming_stat LIMIT 10000", false);
	}
	db_results res=q_getIncomingStats->Read();
	std::vector<ServerFilesDao::SIncomingStat> ret;
	ret.resize(res.size());
	for(size_t i=0;i<res.size();++i)
	{
		ret[i].id=watoi64(res[i]["id"]);
		ret[i].filesize=watoi64(res[i]["filesize"]);
		ret[i].clientid=watoi(res[i]["clientid"]);
		ret[i].backupid=watoi(res[i]["backupid"]);
		ret[i].existing_clients=res[i]["existing_clients"];
		ret[i].direction=watoi(res[i]["direction"]);
		ret[i].incremental=watoi(res[i]["incremental"]);
	}
	return ret;
}

/**
* @-SQLGenAccess
* @func void ServerFilesDao::deleteFiles
* @sql
*	DELETE FROM files WHERE backupid=:backupid(int)
*/
void ServerFilesDao::deleteFiles(int backupid)
{
	if(q_deleteFiles==NULL)
	{
		q_deleteFiles=db->Prepare("DELETE FROM files WHERE backupid=?", false);
	}
	q_deleteFiles->Bind(backupid);
	q_deleteFiles->Write();
	q_deleteFiles->Reset();
}

/**
* @-SQLGenAccess
* @func void ServerFilesDao::removeDanglingFiles
* @sql
*	DELETE FROM files WHERE backupid NOT IN (SELECT id FROM backups)
*/
void ServerFilesDao::removeDanglingFiles(void)
{
	if(q_removeDanglingFiles==NULL)
	{
		q_removeDanglingFiles=db->Prepare("DELETE FROM files WHERE backupid NOT IN (SELECT id FROM backups)", false);
	}
	q_removeDanglingFiles->Write();
}

/**
* @-SQLGenAccessNoCheck
* @func bool ServerFilesDao::createTemporaryLastFilesTable
* @sql
*      CREATE TEMPORARY TABLE files_last ( fullpath TEXT, hashpath TEXT, shahash BLOB, filesize INTEGER, rsize INTEGER );
*/
bool ServerFilesDao::createTemporaryLastFilesTable(void)
{
	if(q_createTemporaryLastFilesTable==NULL)
	{
		q_createTemporaryLastFilesTable=db->Prepare("CREATE TEMPORARY TABLE files_last ( fullpath TEXT, hashpath TEXT, shahash BLOB, filesize INTEGER, rsize INTEGER );", false);
	}
	bool ret = q_createTemporaryLastFilesTable->Write();
	return ret;
}

/**
* @-SQLGenAccessNoCheck
* @func void ServerFilesDao::dropTemporaryLastFilesTable
* @sql
*      DROP TABLE files_last
*/
void ServerFilesDao::dropTemporaryLastFilesTable(void)
{
	if(q_dropTemporaryLastFilesTable==NULL)
	{
		q_dropTemporaryLastFilesTable=db->Prepare("DROP TABLE files_last", false);
	}
	q_dropTemporaryLastFilesTable->Write();
}

/**
* @-SQLGenAccessNoCheck
* @func bool ServerFilesDao::createTemporaryLastFilesTableIndex
* @sql
*      CREATE INDEX files_last_idx ON files_last ( fullpath );
*/
bool ServerFilesDao::createTemporaryLastFilesTableIndex(void)
{
	if(q_createTemporaryLastFilesTableIndex==NULL)
	{
		q_createTemporaryLastFilesTableIndex=db->Prepare("CREATE INDEX files_last_idx ON files_last ( fullpath );", false);
	}
	bool ret = q_createTemporaryLastFilesTableIndex->Write();
	return ret;
}

/**
* @-SQLGenAccessNoCheck
* @func bool ServerFilesDao::dropTemporaryLastFilesTableIndex
* @sql
*      DROP INDEX files_last_idx;
*/
bool ServerFilesDao::dropTemporaryLastFilesTableIndex(void)
{
	if(q_dropTemporaryLastFilesTableIndex==NULL)
	{
		q_dropTemporaryLastFilesTableIndex=db->Prepare("DROP INDEX files_last_idx;", false);
	}
	bool ret = q_dropTemporaryLastFilesTableIndex->Write();
	return ret;
}

/**
* @-SQLGenAccess
* @func bool ServerFilesDao::copyToTemporaryLastFilesTable
* @sql
*      INSERT INTO files_last (fullpath, hashpath, shahash, filesize)
*			SELECT fullpath, hashpath, shahash, filesize FROM files
*				WHERE backupid = :backupid(int)
*/
bool ServerFilesDao::copyToTemporaryLastFilesTable(int backupid)
{
	if(q_copyToTemporaryLastFilesTable==NULL)
	{
		q_copyToTemporaryLastFilesTable=db->Prepare("INSERT INTO files_last (fullpath, hashpath, shahash, filesize) SELECT fullpath, hashpath, shahash, filesize FROM files WHERE backupid = ?", false);
	}
	q_copyToTemporaryLastFilesTable->Bind(backupid);
	bool ret = q_copyToTemporaryLastFilesTable->Write();
	q_copyToTemporaryLastFilesTable->Reset();
	return ret;
}

/**
* @-SQLGenAccess
* @func SFileEntry ServerFilesDao::getFileEntryFromTemporaryTable
* @return string fullpath, string hashpath, blob shahash, int64 filesize
* @sql
*      SELECT fullpath, hashpath, shahash, filesize
*       FROM files_last WHERE fullpath = :fullpath(string)
*/
ServerFilesDao::SFileEntry ServerFilesDao::getFileEntryFromTemporaryTable(const std::string& fullpath)
{
	if(q_getFileEntryFromTemporaryTable==NULL)
	{
		q_getFileEntryFromTemporaryTable=db->Prepare("SELECT fullpath, hashpath, shahash, filesize FROM files_last WHERE fullpath = ?", false);
	}
	q_getFileEntryFromTemporaryTable->Bind(fullpath);
	db_results res=q_getFileEntryFromTemporaryTable->Read();
	q_getFileEntryFromTemporaryTable->Reset();
	SFileEntry ret = { false, "", "", "", 0 };
	if(!res.empty())
	{
		ret.exists=true;
		ret.fullpath=res[0]["fullpath"];
		ret.hashpath=res[0]["hashpath"];
		ret.shahash=res[0]["shahash"];
		ret.filesize=watoi64(res[0]["filesize"]);
	}
	return ret;
}


/**
* @-SQLGenAccess
* @func vector<SFileEntry> ServerFilesDao::getFileEntriesFromTemporaryTableGlob
* @return string fullpath, string hashpath, blob shahash, int64 filesize
* @sql
*      SELECT fullpath, hashpath, shahash, filesize
*       FROM files_last WHERE fullpath GLOB :fullpath_glob(string)
*/
std::vector<ServerFilesDao::SFileEntry> ServerFilesDao::getFileEntriesFromTemporaryTableGlob(const std::string& fullpath_glob)
{
	if(q_getFileEntriesFromTemporaryTableGlob==NULL)
	{
		q_getFileEntriesFromTemporaryTableGlob=db->Prepare("SELECT fullpath, hashpath, shahash, filesize FROM files_last WHERE fullpath GLOB ?", false);
	}
	q_getFileEntriesFromTemporaryTableGlob->Bind(fullpath_glob);
	db_results res=q_getFileEntriesFromTemporaryTableGlob->Read();
	q_getFileEntriesFromTemporaryTableGlob->Reset();
	std::vector<ServerFilesDao::SFileEntry> ret;
	ret.resize(res.size());
	for(size_t i=0;i<res.size();++i)
	{
		ret[i].exists=true;
		ret[i].fullpath=res[i]["fullpath"];
		ret[i].hashpath=res[i]["hashpath"];
		ret[i].shahash=res[i]["shahash"];
		ret[i].filesize=watoi64(res[i]["filesize"]);
	}
	return ret;
}

/**
* @-SQLGenAccess
* @func SBackupIdMinMax ServerFilesDao::getBackupIdMinMax
* @return int64 tmin, int64 tmax
* @sql
*      SELECT MIN(id) AS tmin, MAX(id) AS tmax FROM files WHERE backupid=:backupid(int)
*/
ServerFilesDao::SBackupIdMinMax ServerFilesDao::getBackupIdMinMax(int backupid)
{
	if(q_getBackupIdMinMax==NULL)
	{
		q_getBackupIdMinMax=db->Prepare("SELECT MIN(id) AS tmin, MAX(id) AS tmax FROM files WHERE backupid=?", false);
	}
	q_getBackupIdMinMax->Bind(backupid);
	db_results res=q_getBackupIdMinMax->Read();
	q_getBackupIdMinMax->Reset();
	SBackupIdMinMax ret = { false, 0, 0 };
	if(!res.empty())
	{
		ret.exists=true;
		ret.tmin=watoi64(res[0]["tmin"]);
		ret.tmax=watoi64(res[0]["tmax"]);
	}
	return ret;
}

//@-SQLGenSetup
void ServerFilesDao::prepareQueries()
{
	q_setNextEntry=NULL;
	q_setPrevEntry=NULL;
	q_setPointedTo=NULL;
	q_getPointedTo=NULL;
	q_delFileEntry=NULL;
	q_getFileEntry=NULL;
	q_getStatFileEntry=NULL;
	q_addFileEntry=NULL;
	q_createTemporaryPathLookupTable=NULL;
	q_dropTemporaryPathLookupTable=NULL;
	q_dropTemporaryPathLookupIndex=NULL;
	q_populateTemporaryPathLookupTable=NULL;
	q_createTemporaryPathLookupIndex=NULL;
	q_lookupEntryIdByPath=NULL;
	q_addIncomingFile=NULL;
	q_getIncomingStatsCount=NULL;
	q_delIncomingStatEntry=NULL;
	q_getIncomingStats=NULL;
	q_deleteFiles=NULL;
	q_removeDanglingFiles=NULL;
	q_createTemporaryLastFilesTable=NULL;
	q_dropTemporaryLastFilesTable=NULL;
	q_createTemporaryLastFilesTableIndex=NULL;
	q_dropTemporaryLastFilesTableIndex=NULL;
	q_copyToTemporaryLastFilesTable=NULL;
	q_getFileEntryFromTemporaryTable=NULL;
	q_getFileEntriesFromTemporaryTableGlob=NULL;
	q_getBackupIdMinMax=NULL;
}

//@-SQLGenDestruction
void ServerFilesDao::destroyQueries()
{
	db->destroyQuery(q_setNextEntry);
	db->destroyQuery(q_setPrevEntry);
	db->destroyQuery(q_setPointedTo);
	db->destroyQuery(q_getPointedTo);
	db->destroyQuery(q_delFileEntry);
	db->destroyQuery(q_getFileEntry);
	db->destroyQuery(q_getStatFileEntry);
	db->destroyQuery(q_addFileEntry);
	db->destroyQuery(q_createTemporaryPathLookupTable);
	db->destroyQuery(q_dropTemporaryPathLookupTable);
	db->destroyQuery(q_dropTemporaryPathLookupIndex);
	db->destroyQuery(q_populateTemporaryPathLookupTable);
	db->destroyQuery(q_createTemporaryPathLookupIndex);
	db->destroyQuery(q_lookupEntryIdByPath);
	db->destroyQuery(q_addIncomingFile);
	db->destroyQuery(q_getIncomingStatsCount);
	db->destroyQuery(q_delIncomingStatEntry);
	db->destroyQuery(q_getIncomingStats);
	db->destroyQuery(q_deleteFiles);
	db->destroyQuery(q_removeDanglingFiles);
	db->destroyQuery(q_createTemporaryLastFilesTable);
	db->destroyQuery(q_dropTemporaryLastFilesTable);
	db->destroyQuery(q_createTemporaryLastFilesTableIndex);
	db->destroyQuery(q_dropTemporaryLastFilesTableIndex);
	db->destroyQuery(q_copyToTemporaryLastFilesTable);
	db->destroyQuery(q_getFileEntryFromTemporaryTable);
	db->destroyQuery(q_getFileEntriesFromTemporaryTableGlob);
	db->destroyQuery(q_getBackupIdMinMax);
}

int64 ServerFilesDao::addFileEntryExternal(int backupid, const std::string& fullpath, const std::string& hashpath, const std::string& shahash, int64 filesize, int64 rsize, int clientid, int incremental, int64 next_entry, int64 prev_entry, int pointed_to)
{
	addFileEntry(backupid, fullpath, hashpath, shahash, filesize, rsize, clientid, incremental, next_entry, prev_entry, pointed_to);

	int64 id = db->getLastInsertID();

	if (prev_entry != 0)
	{
		setNextEntry(id, prev_entry);
	}

	if (next_entry != 0)
	{
		setPrevEntry(id, next_entry);
	}

	return id;
}