/*************************************************************************
* 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 .
**************************************************************************/
#include "clientdao.h"
#include "../stringtools.h"
#include "../Interface/Server.h"
#include
const int ClientDAO::c_is_group = 0;
const int ClientDAO::c_is_user = 1;
const int ClientDAO::c_is_system_user = 2;
ClientDAO::ClientDAO(IDatabase *pDB)
{
db=pDB;
prepareQueries();
}
ClientDAO::~ClientDAO()
{
destroyQueries();
}
void ClientDAO::prepareQueries()
{
q_get_files=db->Prepare("SELECT data, num, generation FROM files WHERE name=? AND tgroup=?", false);
q_add_files=db->Prepare("INSERT OR REPLACE INTO files (name, tgroup, num, data) VALUES (?,?,?,?)", false);
q_get_dirs=db->Prepare("SELECT name, path, id, optional, tgroup, symlinked, server_default, reset_keep FROM backupdirs", false);
q_remove_all=db->Prepare("DELETE FROM files", false);
q_get_changed_dirs=db->Prepare("SELECT id, name FROM mdirs WHERE name GLOB ? UNION SELECT id, name FROM mdirs_backup WHERE name GLOB ?", false);
q_modify_files=db->Prepare("UPDATE files SET data=?, num=?, generation=? WHERE name=? AND tgroup=? AND generation=?", false);
q_has_files=db->Prepare("SELECT count(*) AS num FROM files WHERE name=? AND tgroup=?", false);
q_insert_shadowcopy=db->Prepare("INSERT INTO shadowcopies (vssid, ssetid, target, path, tname, orig_target, filesrv, vol, starttime, refs, starttoken, clientsubname) VALUES (?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, ?, ?, ?)", false);
q_get_shadowcopies=db->Prepare("SELECT id, vssid, ssetid, target, path, tname, orig_target, filesrv, vol, (strftime('%s','now') - strftime('%s', starttime)) AS passedtime, refs, starttoken, clientsubname FROM shadowcopies", false);
q_remove_shadowcopies=db->Prepare("DELETE FROM shadowcopies WHERE id=?", false);
q_save_changed_dirs=db->Prepare("INSERT OR REPLACE INTO mdirs_backup SELECT id, name FROM mdirs WHERE name GLOB ?", false);
q_delete_saved_changed_dirs=db->Prepare("DELETE FROM mdirs_backup", false);
q_has_changed_gap=db->Prepare("SELECT name FROM mdirs WHERE name GLOB '##-GAP-##*'", false);
q_get_del_dirs=db->Prepare("SELECT name FROM del_dirs WHERE name GLOB ? UNION SELECT name FROM del_dirs_backup WHERE name GLOB ?", false);
q_del_del_dirs=db->Prepare("DELETE FROM del_dirs WHERE name GLOB ?", false);
q_copy_del_dirs=db->Prepare("INSERT INTO del_dirs_backup SELECT name FROM del_dirs WHERE name GLOB ?", false);
q_del_del_dirs_copy=db->Prepare("DELETE FROM del_dirs_backup", false);
q_remove_del_dir=db->Prepare("DELETE FROM files WHERE name GLOB ? AND tgroup=?", false);
q_get_shadowcopy_refcount=db->Prepare("SELECT refs FROM shadowcopies WHERE id=?", false);
q_set_shadowcopy_refcount=db->Prepare("UPDATE shadowcopies SET refs=? WHERE id=?", false);
q_get_pattern=db->Prepare("SELECT tvalue FROM misc WHERE tkey=?", false);
q_insert_pattern=db->Prepare("INSERT INTO misc (tkey, tvalue) VALUES (?, ?)", false);
q_update_pattern=db->Prepare("UPDATE misc SET tvalue=? WHERE tkey=?", false);
prepareQueriesGen();
}
void ClientDAO::destroyQueries(void)
{
db->destroyQuery(q_get_files);
db->destroyQuery(q_add_files);
db->destroyQuery(q_get_dirs);
db->destroyQuery(q_remove_all);
db->destroyQuery(q_get_changed_dirs);
db->destroyQuery(q_modify_files);
db->destroyQuery(q_has_files);
db->destroyQuery(q_insert_shadowcopy);
db->destroyQuery(q_get_shadowcopies);
db->destroyQuery(q_remove_shadowcopies);
db->destroyQuery(q_save_changed_dirs);
db->destroyQuery(q_delete_saved_changed_dirs);
db->destroyQuery(q_has_changed_gap);
db->destroyQuery(q_get_del_dirs);
db->destroyQuery(q_del_del_dirs);
db->destroyQuery(q_copy_del_dirs);
db->destroyQuery(q_del_del_dirs_copy);
db->destroyQuery(q_remove_del_dir);
db->destroyQuery(q_get_shadowcopy_refcount);
db->destroyQuery(q_set_shadowcopy_refcount);
db->destroyQuery(q_get_pattern);
db->destroyQuery(q_insert_pattern);
db->destroyQuery(q_update_pattern);
destroyQueriesGen();
}
//@-SQLGenSetup
void ClientDAO::prepareQueriesGen(void)
{
q_updateShadowCopyStarttime=NULL;
q_updateFileAccessToken=NULL;
q_getFileAccessTokens=NULL;
q_getFileAccessTokenId2Alts=NULL;
q_getFileAccessTokenId=NULL;
q_removeGroupMembership=NULL;
q_updateGroupMembership=NULL;
q_getGroupMembership=NULL;
q_addBackupDir=NULL;
q_delBackupDir=NULL;
q_setResetKeep=NULL;
q_resetHardlink=NULL;
q_hasHardLink=NULL;
q_addHardlink=NULL;
q_resetAllHardlinks=NULL;
}
//@-SQLGenDestruction
void ClientDAO::destroyQueriesGen(void)
{
db->destroyQuery(q_updateShadowCopyStarttime);
db->destroyQuery(q_updateFileAccessToken);
db->destroyQuery(q_getFileAccessTokens);
db->destroyQuery(q_getFileAccessTokenId2Alts);
db->destroyQuery(q_getFileAccessTokenId);
db->destroyQuery(q_removeGroupMembership);
db->destroyQuery(q_updateGroupMembership);
db->destroyQuery(q_getGroupMembership);
db->destroyQuery(q_addBackupDir);
db->destroyQuery(q_delBackupDir);
db->destroyQuery(q_setResetKeep);
db->destroyQuery(q_resetHardlink);
db->destroyQuery(q_hasHardLink);
db->destroyQuery(q_addHardlink);
db->destroyQuery(q_resetAllHardlinks);
}
void ClientDAO::restartQueries(void)
{
destroyQueries();
prepareQueries();
}
std::string ClientDAO::escapeGlob(const std::string& glob)
{
std::string ret;
ret.reserve(glob.size());
for (size_t i = 0; i &data, int64& generation)
{
q_get_files->Bind(path);
q_get_files->Bind(tgroup);
db_results res=q_get_files->Read();
q_get_files->Reset();
if(res.size()==0)
return false;
generation = watoi64(res[0]["generation"]);
std::string &qdata=res[0]["data"];
if(qdata.empty())
return true;
int num=watoi(res[0]["num"]);
char *ptr=(char*)&qdata[0];
while(ptr-(char*)&qdata[0]0)
{
memcpy(&f.hash[0], ptr, hashsize);
}
ptr+=hashsize;
char issym=*ptr;
++ptr;
f.issym=issym==0?false:true;
char isspecialf=*ptr;
++ptr;
f.isspecialf= isspecialf ==0?false:true;
if(f.issym)
{
memcpy(&ss, ptr, sizeof(unsigned short));
ptr+=sizeof(unsigned short);
if(ss>0)
{
tmp.resize(ss);
memcpy(&tmp[0], ptr, ss);
f.symlink_target=(tmp);
ptr+=ss;
}
}
data.push_back(f);
}
return true;
}
char * constructData(const std::vector &data, size_t &datasize)
{
datasize=0;
std::vector utf;
utf.resize(data.size());
for(size_t i=0;i(data[i].hash.size());
memcpy(ptr, &hashsize, sizeof(hashsize));
ptr+=sizeof(hashsize);
memcpy(ptr, data[i].hash.data(), hashsize);
ptr+=hashsize;
char issym = data[i].issym?1:0;
*ptr=issym;
++ptr;
char isspecialf = data[i].isspecialf?1:0;
*ptr= isspecialf ?1:0;
++ptr;
if(data[i].issym)
{
std::string symlink_target = (data[i].symlink_target);
ss=(unsigned short)symlink_target.size();
memcpy(ptr, (char*)&ss, sizeof(unsigned short));
ptr+=sizeof(unsigned short);
if(ss>0)
{
memcpy(ptr, &symlink_target[0], ss);
}
ptr+=ss;
}
}
return buffer;
}
std::string guidToString( GUID guid )
{
return bytesToHex(reinterpret_cast(&guid), sizeof(guid));
}
GUID randomGuid()
{
GUID ret;
Server->secureRandomFill(reinterpret_cast(&ret), sizeof(ret));
return ret;
}
void ClientDAO::addFiles(std::string path, int tgroup, const std::vector &data)
{
size_t ds;
char *buffer=constructData(data, ds);
q_add_files->Bind(path);
q_add_files->Bind(tgroup);
q_add_files->Bind(ds);
q_add_files->Bind(buffer, (_u32)ds);
q_add_files->Write();
q_add_files->Reset();
delete []buffer;
}
void ClientDAO::modifyFiles(std::string path, int tgroup, const std::vector &data, int64 target_generation)
{
size_t ds;
char *buffer=constructData(data, ds);
q_modify_files->Bind(buffer, (_u32)ds);
q_modify_files->Bind(ds);
q_modify_files->Bind(target_generation+1);
q_modify_files->Bind(path);
q_modify_files->Bind(tgroup);
q_modify_files->Bind(target_generation);
q_modify_files->Write();
q_modify_files->Reset();
delete []buffer;
}
bool ClientDAO::hasFiles(std::string path, int tgroup)
{
q_has_files->Bind(path);
q_has_files->Bind(tgroup);
db_results res=q_has_files->Read();
q_has_files->Reset();
if(res.size()>0)
return res[0]["num"]=="1";
else
return false;
}
std::vector ClientDAO::getBackupDirs(void)
{
db_results res=q_get_dirs->Read();
q_get_dirs->Reset();
std::vector ret;
std::vector sym_ret;
for(size_t i=0;iWrite();
}
std::vector ClientDAO::getChangedDirs(const std::string& path, bool backup)
{
std::vector ret;
std::string sep = os_file_sep();
if(path == "##-GAP-##")
{
sep = "";
}
q_get_changed_dirs->Bind(escapeGlob(path)+sep+"*");
q_get_changed_dirs->Bind(escapeGlob(path)+sep+"*");
db_results res=q_get_changed_dirs->Read();
q_get_changed_dirs->Reset();
ret.reserve(res.size());
for(size_t i=0;iBind(escapeGlob(path) + sep + "*");
q_save_changed_dirs->Write();
q_save_changed_dirs->Reset();
}
return ret;
}
std::vector ClientDAO::getShadowcopies(void)
{
db_results res=q_get_shadowcopies->Read();
q_get_shadowcopies->Reset();
std::vector ret;
for(size_t i=0;iBind((char*)&sc.vssid, sizeof(GUID) );
q_insert_shadowcopy->Bind((char*)&sc.ssetid, sizeof(GUID) );
q_insert_shadowcopy->Bind(sc.target);
q_insert_shadowcopy->Bind(sc.path);
q_insert_shadowcopy->Bind(sc.tname);
q_insert_shadowcopy->Bind(sc.orig_target);
q_insert_shadowcopy->Bind(sc.filesrv?1:0);
q_insert_shadowcopy->Bind(sc.vol);
q_insert_shadowcopy->Bind(sc.refs);
q_insert_shadowcopy->Bind(sc.starttoken);
q_insert_shadowcopy->Bind(sc.clientsubname);
q_insert_shadowcopy->Write();
q_insert_shadowcopy->Reset();
return (int)db->getLastInsertID();
}
int ClientDAO::modShadowcopyRefCount(int id, int m)
{
q_get_shadowcopy_refcount->Bind(id);
db_results res=q_get_shadowcopy_refcount->Read();
q_get_shadowcopy_refcount->Reset();
if(!res.empty())
{
int refs=watoi(res[0]["refs"]);
refs+=m;
q_set_shadowcopy_refcount->Bind(refs);
q_set_shadowcopy_refcount->Bind(id);
q_set_shadowcopy_refcount->Write();
q_set_shadowcopy_refcount->Reset();
return refs;
}
return -1;
}
void ClientDAO::deleteShadowcopy(int id)
{
q_remove_shadowcopies->Bind(id);
q_remove_shadowcopies->Write();
q_remove_shadowcopies->Reset();
}
void ClientDAO::deleteSavedChangedDirs(void)
{
q_delete_saved_changed_dirs->Write();
q_delete_saved_changed_dirs->Reset();
}
bool ClientDAO::hasChangedGap(void)
{
db_results res=q_has_changed_gap->Read();
q_has_changed_gap->Reset();
return !res.empty();
}
std::vector ClientDAO::getGapDirs(void)
{
db_results res=q_has_changed_gap->Read();
q_has_changed_gap->Reset();
std::vector ret;
for(size_t i=0;i ClientDAO::getDelDirs(const std::string& path, bool del)
{
std::vector ret;
if(del)
{
q_copy_del_dirs->Bind(escapeGlob(path)+os_file_sep()+"*");
q_copy_del_dirs->Write();
q_copy_del_dirs->Reset();
q_del_del_dirs->Bind(escapeGlob(path)+os_file_sep()+"*");
q_del_del_dirs->Write();
q_del_del_dirs->Reset();
}
q_get_del_dirs->Bind(escapeGlob(path)+os_file_sep()+"*");
q_get_del_dirs->Bind(escapeGlob(path)+os_file_sep()+"*");
db_results res=q_get_del_dirs->Read();
q_get_del_dirs->Reset();
for(size_t i=0;iWrite();
q_del_del_dirs_copy->Reset();
}
void ClientDAO::removeDeletedDir(const std::string &dir, int tgroup)
{
q_remove_del_dir->Bind(escapeGlob(dir)+"*");
q_remove_del_dir->Bind(tgroup);
q_remove_del_dir->Write();
q_remove_del_dir->Reset();
}
const std::string exclude_pattern_key="exclude_pattern";
std::string ClientDAO::getOldExcludePattern(void)
{
return getMiscValue(exclude_pattern_key);
}
void ClientDAO::updateOldExcludePattern(const std::string &pattern)
{
updateMiscValue(exclude_pattern_key, pattern);
}
const std::string include_pattern_key="include_pattern";
std::string ClientDAO::getOldIncludePattern(void)
{
return getMiscValue(include_pattern_key);
}
void ClientDAO::updateOldIncludePattern(const std::string &pattern)
{
updateMiscValue(include_pattern_key, pattern);
}
std::string ClientDAO::getMiscValue(const std::string& key)
{
q_get_pattern->Bind(key);
db_results res=q_get_pattern->Read();
q_get_pattern->Reset();
if(!res.empty())
{
return res[0]["tvalue"];
}
else
{
return "";
}
}
void ClientDAO::updateMiscValue(const std::string& key, const std::string& value)
{
q_get_pattern->Bind(key);
db_results res=q_get_pattern->Read();
q_get_pattern->Reset();
if(!res.empty())
{
q_update_pattern->Bind(value);
q_update_pattern->Bind(key);
q_update_pattern->Write();
q_update_pattern->Reset();
}
else
{
q_insert_pattern->Bind(key);
q_insert_pattern->Bind(value);
q_insert_pattern->Write();
q_insert_pattern->Reset();
}
}
/**
* @-SQLGenAccess
* @func void ClientDAO::updateShadowCopyStarttime
* @sql
* UPDATE shadowcopies SET starttime=CURRENT_TIMESTAMP
* WHERE id=:id(int)
*/
void ClientDAO::updateShadowCopyStarttime(int id)
{
if(q_updateShadowCopyStarttime==NULL)
{
q_updateShadowCopyStarttime=db->Prepare("UPDATE shadowcopies SET starttime=CURRENT_TIMESTAMP WHERE id=?", false);
}
q_updateShadowCopyStarttime->Bind(id);
q_updateShadowCopyStarttime->Write();
q_updateShadowCopyStarttime->Reset();
}
/**
* @-SQLGenAccess
* @func void ClientDAO::updateFileAccessToken
* @sql
* INSERT OR REPLACE INTO fileaccess_tokens
* (accountname, token, is_user)
* VALUES
* (:accountname(string), :token(string), :is_user(int))
*/
void ClientDAO::updateFileAccessToken(const std::string& accountname, const std::string& token, int is_user)
{
if(q_updateFileAccessToken==NULL)
{
q_updateFileAccessToken=db->Prepare("INSERT OR REPLACE INTO fileaccess_tokens (accountname, token, is_user) VALUES (?, ?, ?)", false);
}
q_updateFileAccessToken->Bind(accountname);
q_updateFileAccessToken->Bind(token);
q_updateFileAccessToken->Bind(is_user);
q_updateFileAccessToken->Write();
q_updateFileAccessToken->Reset();
}
/**
* @-SQLGenAccess
* @func vector ClientDAO::getFileAccessTokens
* @return int64 id, string accountname, string token, int is_user
* @sql
* SELECT id, accountname, token, is_user
* FROM fileaccess_tokens
*/
std::vector ClientDAO::getFileAccessTokens(void)
{
if(q_getFileAccessTokens==NULL)
{
q_getFileAccessTokens=db->Prepare("SELECT id, accountname, token, is_user FROM fileaccess_tokens", false);
}
db_results res=q_getFileAccessTokens->Read();
std::vector ret;
ret.resize(res.size());
for(size_t i=0;iPrepare("SELECT id FROM fileaccess_tokens WHERE accountname = ? AND (is_user = ? OR is_user = ?)", false);
}
q_getFileAccessTokenId2Alts->Bind(accountname);
q_getFileAccessTokenId2Alts->Bind(is_user_alt1);
q_getFileAccessTokenId2Alts->Bind(is_user_alt2);
db_results res=q_getFileAccessTokenId2Alts->Read();
q_getFileAccessTokenId2Alts->Reset();
CondInt64 ret = { false, 0 };
if(!res.empty())
{
ret.exists=true;
ret.value=watoi64(res[0]["id"]);
}
return ret;
}
/**
* @-SQLGenAccess
* @func int64 ClientDAO::getFileAccessTokenId
* @return int64 id
* @sql
* SELECT id
* FROM fileaccess_tokens WHERE accountname = :accountname(string) AND
* is_user = :is_user(int)
*/
ClientDAO::CondInt64 ClientDAO::getFileAccessTokenId(const std::string& accountname, int is_user)
{
if(q_getFileAccessTokenId==NULL)
{
q_getFileAccessTokenId=db->Prepare("SELECT id FROM fileaccess_tokens WHERE accountname = ? AND is_user = ?", false);
}
q_getFileAccessTokenId->Bind(accountname);
q_getFileAccessTokenId->Bind(is_user);
db_results res=q_getFileAccessTokenId->Read();
q_getFileAccessTokenId->Reset();
CondInt64 ret = { false, 0 };
if(!res.empty())
{
ret.exists=true;
ret.value=watoi64(res[0]["id"]);
}
return ret;
}
/**
* @-SQLGenAccess
* @func void ClientDAO::removeGroupMembership
* @sql
* DELETE FROM token_group_memberships WHERE uid=:uid(int64)
*/
void ClientDAO::removeGroupMembership(int64 uid)
{
if(q_removeGroupMembership==NULL)
{
q_removeGroupMembership=db->Prepare("DELETE FROM token_group_memberships WHERE uid=?", false);
}
q_removeGroupMembership->Bind(uid);
q_removeGroupMembership->Write();
q_removeGroupMembership->Reset();
}
/**
* @-SQLGenAccess
* @func void ClientDAO::updateGroupMembership
* @sql
* INSERT OR REPLACE INTO token_group_memberships
* (uid, gid)
* VALUES
* (:uid(int64), (SELECT id FROM fileaccess_tokens WHERE accountname = :accountname(string) AND is_user=0) )
*/
void ClientDAO::updateGroupMembership(int64 uid, const std::string& accountname)
{
if(q_updateGroupMembership==NULL)
{
q_updateGroupMembership=db->Prepare("INSERT OR REPLACE INTO token_group_memberships (uid, gid) VALUES (?, (SELECT id FROM fileaccess_tokens WHERE accountname = ? AND is_user=0) )", false);
}
q_updateGroupMembership->Bind(uid);
q_updateGroupMembership->Bind(accountname);
q_updateGroupMembership->Write();
q_updateGroupMembership->Reset();
}
/**
* @-SQLGenAccess
* @func vector ClientDAO::getGroupMembership
* @return int gid
* @sql
* SELECT gid
* FROM token_group_memberships
* WHERE uid = :uid(int)
*/
std::vector ClientDAO::getGroupMembership(int uid)
{
if(q_getGroupMembership==NULL)
{
q_getGroupMembership=db->Prepare("SELECT gid FROM token_group_memberships WHERE uid = ?", false);
}
q_getGroupMembership->Bind(uid);
db_results res=q_getGroupMembership->Read();
q_getGroupMembership->Reset();
std::vector ret;
ret.resize(res.size());
for(size_t i=0;iPrepare("INSERT INTO backupdirs (name, path, server_default, optional, tgroup, symlinked) VALUES (?, ?, ?, ?, ?, ? )", false);
}
q_addBackupDir->Bind(name);
q_addBackupDir->Bind(path);
q_addBackupDir->Bind(server_default);
q_addBackupDir->Bind(flags);
q_addBackupDir->Bind(tgroup);
q_addBackupDir->Bind(symlinked);
q_addBackupDir->Write();
q_addBackupDir->Reset();
}
/**
* @-SQLGenAccess
* @func void ClientDAO::delBackupDir
* @sql
* DELETE FROM backupdirs WHERE id=:id(int64)
**/
void ClientDAO::delBackupDir(int64 id)
{
if(q_delBackupDir==NULL)
{
q_delBackupDir=db->Prepare("DELETE FROM backupdirs WHERE id=?", false);
}
q_delBackupDir->Bind(id);
q_delBackupDir->Write();
q_delBackupDir->Reset();
}
/**
* @-SQLGenAccess
* @func void ClientDAO::setResetKeep
* @sql
* UPDATE backupdirs SET reset_keep=:val(int) WHERE id=:id(int64)
**/
void ClientDAO::setResetKeep(int val, int64 id)
{
if(q_setResetKeep==NULL)
{
q_setResetKeep=db->Prepare("UPDATE backupdirs SET reset_keep=? WHERE id=?", false);
}
q_setResetKeep->Bind(val);
q_setResetKeep->Bind(id);
q_setResetKeep->Write();
q_setResetKeep->Reset();
}
/**
* @-SQLGenAccess
* @func void ClientDAO::resetHardlink
* @sql
* DELETE FROM hardlinks WHERE vol=:vol(string) AND frn_high=:frn_high(int64) AND frn_low=:frn_low(int64)
**/
void ClientDAO::resetHardlink(const std::string& vol, int64 frn_high, int64 frn_low)
{
if(q_resetHardlink==NULL)
{
q_resetHardlink=db->Prepare("DELETE FROM hardlinks WHERE vol=? AND frn_high=? AND frn_low=?", false);
}
q_resetHardlink->Bind(vol);
q_resetHardlink->Bind(frn_high);
q_resetHardlink->Bind(frn_low);
q_resetHardlink->Write();
q_resetHardlink->Reset();
}
/**
* @-SQLGenAccess
* @func int64 ClientDAO::hasHardLink
* @return int64 frn_low
* @sql
* SELECT frn_low FROM hardlinks WHERE vol=:vol(string) AND frn_high=:frn_high(int64) AND frn_low=:frn_low(int64) LIMIT 1
**/
ClientDAO::CondInt64 ClientDAO::hasHardLink(const std::string& vol, int64 frn_high, int64 frn_low)
{
if(q_hasHardLink==NULL)
{
q_hasHardLink=db->Prepare("SELECT frn_low FROM hardlinks WHERE vol=? AND frn_high=? AND frn_low=? LIMIT 1", false);
}
q_hasHardLink->Bind(vol);
q_hasHardLink->Bind(frn_high);
q_hasHardLink->Bind(frn_low);
db_results res=q_hasHardLink->Read();
q_hasHardLink->Reset();
CondInt64 ret = { false, 0 };
if(!res.empty())
{
ret.exists=true;
ret.value=watoi64(res[0]["frn_low"]);
}
return ret;
}
/**
* @-SQLGenAccess
* @func void ClientDAO::addHardlink
* @sql
* INSERT OR IGNORE INTO hardlinks (vol, frn_high, frn_low, parent_frn_high, parent_frn_low)
* VALUES (:vol(string), :frn_high(int64), :frn_low(int64), :parent_frn_high(int64), :parent_frn_low(int64))
**/
void ClientDAO::addHardlink(const std::string& vol, int64 frn_high, int64 frn_low, int64 parent_frn_high, int64 parent_frn_low)
{
if(q_addHardlink==NULL)
{
q_addHardlink=db->Prepare("INSERT OR IGNORE INTO hardlinks (vol, frn_high, frn_low, parent_frn_high, parent_frn_low) VALUES (?, ?, ?, ?, ?)", false);
}
q_addHardlink->Bind(vol);
q_addHardlink->Bind(frn_high);
q_addHardlink->Bind(frn_low);
q_addHardlink->Bind(parent_frn_high);
q_addHardlink->Bind(parent_frn_low);
q_addHardlink->Write();
q_addHardlink->Reset();
}
/**
* @-SQLGenAccess
* @func void ClientDAO::resetAllHardlinks
* @sql
* DELETE FROM hardlinks
**/
void ClientDAO::resetAllHardlinks(void)
{
if(q_resetAllHardlinks==NULL)
{
q_resetAllHardlinks=db->Prepare("DELETE FROM hardlinks", false);
}
q_resetAllHardlinks->Write();
}