mirror of
https://github.com/nextcloud/desktop.git
synced 2024-11-29 04:08:54 +03:00
Merge pull request #2319 from owncloud/sqlite_capi
Introduce a common sqlite layer across csync and mirall. This avoids conflicts that both each load different and/or updated sqlite versions.
This commit is contained in:
commit
52a5729298
31 changed files with 157569 additions and 489 deletions
|
@ -123,6 +123,12 @@ find_package(Sphinx)
|
|||
find_package(PdfLatex)
|
||||
|
||||
|
||||
find_package(SQLite3 3.8.0 REQUIRED)
|
||||
# On some OS, we want to use our own, not the system sqlite
|
||||
if (USE_OUR_OWN_SQLITE3)
|
||||
include_directories(BEFORE ${SQLITE3_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
|
||||
configure_file(config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h)
|
||||
|
||||
|
|
|
@ -34,7 +34,6 @@ LIBRARY_SEARCH_PATH=['/usr/local/lib', '.']
|
|||
|
||||
QT_PLUGINS = [
|
||||
'accessible/libqtaccessiblewidgets.dylib',
|
||||
'sqldrivers/libqsqlite.dylib',
|
||||
'platforms/libqcocoa.dylib',
|
||||
'imageformats/libqgif.dylib',
|
||||
'imageformats/libqico.dylib',
|
||||
|
|
|
@ -14,7 +14,8 @@ if (${CMAKE_C_COMPILER_ID} MATCHES "(GNU|Clang)")
|
|||
|
||||
# add -Wconversion ?
|
||||
# cannot be pedantic with sqlite3 directly linked
|
||||
if (NOT CSYNC_STATIC_COMPILE_DIR)
|
||||
# FIXME Can we somehow not use those flags for sqlite3.* but use them for the rest of csync?
|
||||
if (NOT USE_OUR_OWN_SQLITE3)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -pedantic -pedantic-errors")
|
||||
endif()
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wshadow -Wmissing-prototypes")
|
||||
|
|
|
@ -50,8 +50,18 @@ if (SQLite3_FIND_VERSION AND _SQLITE3_VERSION)
|
|||
set(SQLite3_VERSION _SQLITE3_VERSION)
|
||||
endif (SQLite3_FIND_VERSION AND _SQLITE3_VERSION)
|
||||
|
||||
if (APPLE OR WINDOWS)
|
||||
set(USE_OUR_OWN_SQLITE3 TRUE)
|
||||
set(SQLITE3_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/src/3rdparty/sqlite3)
|
||||
set(SQLITE3_LIBRARIES "")
|
||||
set(SQLITE3_SOURCE ${SQLITE3_INCLUDE_DIR}/sqlite3.c)
|
||||
MESSAGE(STATUS "Using own sqlite3 from " ${SQLITE3_INCLUDE_DIR})
|
||||
else()
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(SQLite3 DEFAULT_MSG SQLITE3_LIBRARIES SQLITE3_INCLUDE_DIRS)
|
||||
endif()
|
||||
|
||||
|
||||
|
||||
# show the SQLITE3_INCLUDE_DIRS and SQLITE3_LIBRARIES variables only in the advanced view
|
||||
mark_as_advanced(SQLITE3_INCLUDE_DIRS SQLITE3_LIBRARIES)
|
||||
|
|
|
@ -37,7 +37,6 @@
|
|||
!define SOURCE_PATH "@CMAKE_SOURCE_DIR@"
|
||||
!define QT_DLL_PATH "${MING_BIN}"
|
||||
!define ACCESSIBLE_DLL_PATH "${MING_LIB}/qt5/plugins/accessible"
|
||||
!define SQLITE_DLL_PATH "${MING_LIB}/qt5/plugins/sqldrivers"
|
||||
!define IMAGEFORMATS_DLL_PATH "${MING_LIB}/qt5/plugins/imageformats"
|
||||
!define PLATFORMS_DLL_PATH "${MING_LIB}/qt5/plugins/platforms"
|
||||
|
||||
|
@ -404,9 +403,6 @@ Section "${APPLICATION_NAME}" SEC_APPLICATION
|
|||
File "${IMAGEFORMATS_DLL_PATH}\qjpeg.dll"
|
||||
File "${IMAGEFORMATS_DLL_PATH}\qico.dll"
|
||||
|
||||
SetOutPath "$INSTDIR\sqldrivers"
|
||||
File "${SQLITE_DLL_PATH}\qsqlite.dll"
|
||||
|
||||
SetOutPath "$INSTDIR"
|
||||
;License & release notes.
|
||||
File "@CPACK_RESOURCE_FILE_LICENSE@"
|
||||
|
@ -421,7 +417,6 @@ Section "${APPLICATION_NAME}" SEC_APPLICATION
|
|||
File "${QT_DLL_PATH}\Qt5Qml.dll"
|
||||
File "${QT_DLL_PATH}\Qt5Quick.dll"
|
||||
File "${QT_DLL_PATH}\Qt5Sensors.dll"
|
||||
File "${QT_DLL_PATH}\Qt5Sql.dll"
|
||||
File "${QT_DLL_PATH}\Qt5WebKit.dll"
|
||||
File "${QT_DLL_PATH}\Qt5WebKitWidgets.dll"
|
||||
File "${QT_DLL_PATH}\Qt5Widgets.dll"
|
||||
|
@ -437,7 +432,6 @@ Section "${APPLICATION_NAME}" SEC_APPLICATION
|
|||
File "${MING_BIN}\libjpeg-8.dll"
|
||||
File "${MING_BIN}\libpcre16-0.dll"
|
||||
File "${MING_BIN}\libproxy.dll"
|
||||
File "${MING_BIN}\libsqlite3-0.dll"
|
||||
File "${MING_BIN}\libcrypto-10.dll"
|
||||
File "${MING_BIN}\libssl-10.dll"
|
||||
File "${MING_BIN}\libstdc++-6.dll"
|
||||
|
@ -445,9 +439,6 @@ Section "${APPLICATION_NAME}" SEC_APPLICATION
|
|||
File "${MING_BIN}\libxslt-1.dll"
|
||||
File "${MING_BIN}\zlib1.dll"
|
||||
|
||||
;QtSql and csync dep
|
||||
File "${MING_BIN}\libsqlite3-0.dll"
|
||||
|
||||
;QtKeyChain stuff
|
||||
File "${MING_BIN}\libqt5keychain.dll"
|
||||
|
||||
|
|
|
@ -4,13 +4,6 @@ add_subdirectory(std)
|
|||
add_subdirectory(httpbf)
|
||||
|
||||
# Statically include sqlite
|
||||
if (CSYNC_STATIC_COMPILE_DIR)
|
||||
set(SQLITE3_INCLUDE_DIRS "")
|
||||
set(SQLITE3_LIBRARIES "")
|
||||
include_directories(${CSYNC_STATIC_COMPILE_DIR})
|
||||
else (CSYNC_STATIC_COMPILE_DIR)
|
||||
find_package(SQLite3 3.3.9 REQUIRED)
|
||||
endif()
|
||||
|
||||
set(CSYNC_PUBLIC_INCLUDE_DIRS
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
|
@ -86,8 +79,8 @@ set(csync_HDRS
|
|||
)
|
||||
|
||||
# Statically include sqlite
|
||||
if (CSYNC_STATIC_COMPILE_DIR)
|
||||
list(APPEND csync_SRCS ${CSYNC_STATIC_COMPILE_DIR}/dictionary.c ${CSYNC_STATIC_COMPILE_DIR}/sqlite3.c)
|
||||
if (USE_OUR_OWN_SQLITE3)
|
||||
list(APPEND csync_SRCS ${SQLITE3_SOURCE})
|
||||
endif()
|
||||
|
||||
include_directories(
|
||||
|
|
|
@ -722,16 +722,15 @@ sub createShare($$)
|
|||
my $re = $dd->mkcol( $url );
|
||||
if( $re == 0 ) {
|
||||
print "Failed to create test dir $url\n";
|
||||
|
||||
}
|
||||
|
||||
my $ua = LWP::UserAgent->new(ssl_opts => { verify_hostname => 0 } );
|
||||
$ua->agent( "ownCloudTest_sharing");
|
||||
# http://localhost/ocm/ocs/v1.php/apps/files_sharing/api/v1/shares
|
||||
my $puturl = $ocs_url . "apps/files_sharing/api/v1/shares";
|
||||
my $puturl = $ocs_url . "ocs/v1.php/apps/files_sharing/api/v1/shares";
|
||||
|
||||
my $string = "path=$dir&shareType=0&shareWith=$user&publicUpload=false&permissions=$readWrite";
|
||||
print ">>>>>>>>>> $string\n";
|
||||
print ">>>>>>>>>> $puturl $string\n";
|
||||
|
||||
my $req = POST $puturl, Content => $string;
|
||||
$req->authorization_basic($share_user, $share_passwd);
|
||||
|
@ -763,15 +762,14 @@ sub removeShare($$)
|
|||
|
||||
my $ua = LWP::UserAgent->new(ssl_opts => { verify_hostname => 0 });
|
||||
$ua->agent( "ownCloudTest_sharing");
|
||||
# http://localhost/ocm/ocs/v1.php/apps/files_sharing/api/v1/shares
|
||||
my $url = $ocs_url . "apps/files_sharing/api/v1/shares/" . $shareId;
|
||||
|
||||
my $url = $ocs_url . "ocs/v1.php/apps/files_sharing/api/v1/shares/" . $shareId;
|
||||
|
||||
my $req = DELETE $url;
|
||||
$req->authorization_basic($share_user, $share_passwd);
|
||||
my $response = $ua->request($req);
|
||||
|
||||
if ($response->is_success()) {
|
||||
# print "OK: ", $response->content;
|
||||
print $response->decoded_content;
|
||||
if( $response->decoded_content =~ /<status_code>(\d+)<\/status_code>/m) {
|
||||
my $code = $1;
|
||||
|
|
|
@ -33,11 +33,13 @@ print "Hello, this is t6, a tester for csync with ownCloud.\n";
|
|||
|
||||
initTesting();
|
||||
|
||||
sub createPostUpdateScript()
|
||||
sub createPostUpdateScript($)
|
||||
{
|
||||
my $srcFile = localDir()."BIG.file";
|
||||
my ($name) = @_;
|
||||
|
||||
my $srcFile = localDir().'BIG1.file';
|
||||
my $cred = configValue("user") . ":" . configValue("passwd");
|
||||
my $cmd = "curl -T $srcFile -u $cred " . testDirUrl();
|
||||
my $cmd = "curl -T $srcFile -u $cred " . testDirUrl().$name;
|
||||
my $script = "/tmp/post_update_script.sh";
|
||||
open SC, ">$script" || die("Can not create script file");
|
||||
print SC "#!/bin/bash\n";
|
||||
|
@ -48,11 +50,11 @@ sub createPostUpdateScript()
|
|||
return $script;
|
||||
}
|
||||
|
||||
sub getETagFromJournal($)
|
||||
sub getETagFromJournal($$)
|
||||
{
|
||||
my ($num) = @_;
|
||||
my ($name,$num) = @_;
|
||||
|
||||
my $sql = "sqlite3 " . localDir() . ".csync_journal.db \"SELECT md5 FROM metadata WHERE path='BIG.file';\"";
|
||||
my $sql = "sqlite3 " . localDir() . ".csync_journal.db \"SELECT md5 FROM metadata WHERE path='$name';\"";
|
||||
open(my $fh, '-|', $sql) or die $!;
|
||||
my $etag = <$fh>;
|
||||
close $fh;
|
||||
|
@ -89,26 +91,39 @@ sub chunkFileTest( $$ )
|
|||
}
|
||||
|
||||
printInfo("Big file that needs chunking with default chunk size");
|
||||
chunkFileTest( "BIG.file", 23251233 );
|
||||
chunkFileTest( "BIG1.file", 23251233 );
|
||||
|
||||
printInfo("Update the existing file and trigger reupload");
|
||||
# change the existing file again -> update
|
||||
chunkFileTest( "BIG.file", 21762122 );
|
||||
chunkFileTest( "BIG2.file", 21762122 );
|
||||
|
||||
printInfo("Cause a precondition failed error");
|
||||
# Now overwrite the existing file to change it
|
||||
createLocalFile( localDir()."BIG.file", 21832199 );
|
||||
createLocalFile( localDir()."BIG3.file", 21832 );
|
||||
sleep(2);
|
||||
csync();
|
||||
createLocalFile( localDir().'BIG3.file', 34323 );
|
||||
sleep(2);
|
||||
# and create a post update script
|
||||
my $script = createPostUpdateScript();
|
||||
my $script = createPostUpdateScript('BIG3.file');
|
||||
$ENV{'OWNCLOUD_POST_UPDATE_SCRIPT'} = $script;
|
||||
|
||||
# Save the etag before the sync
|
||||
my $firstETag = getETagFromJournal('First');
|
||||
my $firstETag = getETagFromJournal('BIG3.file', 'First');
|
||||
sleep(2);
|
||||
csync(); # Sync, which ends in a precondition failed error
|
||||
# get the etag again. It has to be unchanged because of the error.
|
||||
my $secondETag = getETagFromJournal('Second');
|
||||
assert( $firstETag eq $secondETag, "Different ETags, no precondition error." );
|
||||
my $secondETag = getETagFromJournal('BIG3.file', 'Second');
|
||||
|
||||
# Now the result is that there is a conflict file because since 1.7
|
||||
# the sync is stopped on preconditoin failed and done again.
|
||||
my $seen = 0;
|
||||
opendir(my $dh, localDir() );
|
||||
while(readdir $dh) {
|
||||
$seen = 1 if ( /BIG3_conflict.*\.file/ );
|
||||
}
|
||||
closedir $dh;
|
||||
assert( $seen == 1, "No conflict file created on precondition failed!" );
|
||||
unlink($script);
|
||||
|
||||
# Set a custom chunk size in environment.
|
||||
|
|
|
@ -73,13 +73,12 @@ To cross-compile:
|
|||
|
||||
``zypper install cmake make mingw32-cross-binutils mingw32-cross-cpp mingw32-cross-gcc \
|
||||
mingw32-cross-gcc-c++ mingw32-cross-pkg-config mingw32-filesystem \
|
||||
mingw32-headers mingw32-runtime site-config mingw32-libqt4-sql \
|
||||
mingw32-libqt4-sql-sqlite mingw32-sqlite mingw32-libsqlite-devel \
|
||||
mingw32-headers mingw32-runtime site-config \
|
||||
mingw32-dlfcn-devel mingw32-libssh2-devel kdewin-png2ico \
|
||||
mingw32-libqt4 mingw32-libqt4-devel mingw32-libgcrypt \
|
||||
mingw32-libgnutls mingw32-libneon-openssl mingw32-libneon-devel \
|
||||
mingw32-libbeecrypt mingw32-libopenssl mingw32-openssl \
|
||||
mingw32-libpng-devel mingw32-libsqlite mingw32-qtkeychain \
|
||||
mingw32-libpng-devel mingw32-qtkeychain \
|
||||
mingw32-qtkeychain-devel mingw32-dlfcn mingw32-libintl-devel \
|
||||
mingw32-libneon-devel mingw32-libopenssl-devel mingw32-libproxy-devel \
|
||||
mingw32-libxml2-devel mingw32-zlib-devel``
|
||||
|
|
|
@ -34,6 +34,7 @@ class syncStateExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.Info
|
|||
GObject.timeout_add(5000, self.connectToSocketServer)
|
||||
|
||||
def connectToSocketServer(self):
|
||||
do_reconnect = True
|
||||
try:
|
||||
self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
postfix = "/"+self.appname+"/socket"
|
||||
|
@ -44,15 +45,18 @@ class syncStateExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.Info
|
|||
print("Socket File: "+sock_file)
|
||||
self.sock.connect(sock_file)
|
||||
self.connected = True
|
||||
print("Setting connected to %r" % self.connected )
|
||||
self.watch_id = GObject.io_add_watch(self.sock, GObject.IO_IN, self.handle_notify)
|
||||
except:
|
||||
print("Could not connect to unix socket.")
|
||||
do_reconnect = False
|
||||
except Exception, e:
|
||||
print("Could not connect to unix socket." + str(e))
|
||||
else:
|
||||
print("Sock-File not valid: "+sock_file)
|
||||
except:
|
||||
print("Connect could not be established, try again later!")
|
||||
except Exception, e:
|
||||
print("Connect could not be established, try again later " + str(e))
|
||||
self.sock.close()
|
||||
return not self.connected
|
||||
# print("Returning %r" % do_reconnect)
|
||||
return do_reconnect
|
||||
|
||||
def sendCommand(self, cmd):
|
||||
if self.connected:
|
||||
|
@ -122,7 +126,7 @@ class syncStateExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.Info
|
|||
if( not itemStore['state'] or newState != itemStore['state'] ):
|
||||
item = itemStore['item']
|
||||
item.add_emblem(emblem)
|
||||
# print "Setting emblem on " + parts[2]
|
||||
# print "Setting emblem on " + parts[2]+ "<>"+emblem+"<>"
|
||||
self.nautilusVFSFile_table[parts[2]] = {'item': item, 'state':newState}
|
||||
|
||||
elif action == 'UPDATE_VIEW':
|
||||
|
|
148882
src/3rdparty/sqlite3/sqlite3.c
vendored
Normal file
148882
src/3rdparty/sqlite3/sqlite3.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
7494
src/3rdparty/sqlite3/sqlite3.h
vendored
Normal file
7494
src/3rdparty/sqlite3/sqlite3.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
|
@ -75,6 +75,7 @@ set(libsync_SRCS
|
|||
mirall/propagator_legacy.cpp
|
||||
mirall/syncjournalfilerecord.cpp
|
||||
mirall/syncjournaldb.cpp
|
||||
mirall/ownsql.cpp
|
||||
mirall/theme.cpp
|
||||
mirall/owncloudtheme.cpp
|
||||
mirall/logger.cpp
|
||||
|
@ -173,9 +174,9 @@ GENERATE_EXPORT_HEADER( ${synclib_NAME}
|
|||
|
||||
|
||||
if(TOKEN_AUTH_ONLY)
|
||||
qt5_use_modules(${synclib_NAME} Network Xml Sql)
|
||||
qt5_use_modules(${synclib_NAME} Network Xml)
|
||||
else()
|
||||
qt5_use_modules(${synclib_NAME} Widgets Network Xml WebKitWidgets Sql)
|
||||
qt5_use_modules(${synclib_NAME} Widgets Network Xml WebKitWidgets)
|
||||
endif()
|
||||
|
||||
set_target_properties( ${synclib_NAME} PROPERTIES
|
||||
|
@ -355,7 +356,7 @@ if(NOT BUILD_OWNCLOUD_OSX_BUNDLE AND NOT BUILD_LIBRARIES_ONLY)
|
|||
|
||||
# add_executable( ${APPLICATION_EXECUTABLE} main.cpp ${final_src})
|
||||
add_executable( ${APPLICATION_EXECUTABLE} WIN32 main.cpp ${final_src})
|
||||
qt5_use_modules(${APPLICATION_EXECUTABLE} Widgets Network Xml WebKitWidgets Sql ${ADDITIONAL_APP_MODULES})
|
||||
qt5_use_modules(${APPLICATION_EXECUTABLE} Widgets Network Xml WebKitWidgets ${ADDITIONAL_APP_MODULES})
|
||||
elseif(NOT BUILD_LIBRARIES_ONLY)
|
||||
set(BIN_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}")
|
||||
# set(CMAKE_INSTALL_PREFIX ".") # Examples use /Applications. hurmpf.
|
||||
|
@ -363,7 +364,7 @@ elseif(NOT BUILD_LIBRARIES_ONLY)
|
|||
|
||||
# we must add MACOSX_BUNDLE only if building a bundle
|
||||
add_executable( ${APPLICATION_EXECUTABLE} WIN32 MACOSX_BUNDLE main.cpp ${final_src})
|
||||
qt5_use_modules(${APPLICATION_EXECUTABLE} Widgets Network Xml WebKitWidgets Sql ${ADDITIONAL_APP_MODULES})
|
||||
qt5_use_modules(${APPLICATION_EXECUTABLE} Widgets Network Xml WebKitWidgets ${ADDITIONAL_APP_MODULES})
|
||||
|
||||
set (QM_DIR ${OWNCLOUD_OSX_BUNDLE}/Contents/Resources/Translations)
|
||||
install(FILES ${mirall_I18N} DESTINATION ${QM_DIR})
|
||||
|
@ -420,7 +421,7 @@ set(OWNCLOUDCMD_SRC owncloudcmd/simplesslerrorhandler.cpp owncloudcmd/owncloudcm
|
|||
if(NOT BUILD_LIBRARIES_ONLY)
|
||||
|
||||
add_executable(${owncloudcmd_NAME} ${OWNCLOUDCMD_SRC})
|
||||
qt5_use_modules(${owncloudcmd_NAME} Network Sql)
|
||||
qt5_use_modules(${owncloudcmd_NAME} Network)
|
||||
set_target_properties(${owncloudcmd_NAME} PROPERTIES
|
||||
RUNTIME_OUTPUT_DIRECTORY ${BIN_OUTPUT_DIRECTORY} )
|
||||
set_target_properties(${owncloudcmd_NAME} PROPERTIES
|
||||
|
|
|
@ -82,6 +82,7 @@ int main(int argc, char **argv)
|
|||
|
||||
// if the application is already running, notify it.
|
||||
if( app.isRunning() ) {
|
||||
qDebug() << Q_FUNC_INFO << "Already running, exiting...";
|
||||
QStringList args = app.arguments();
|
||||
if ( args.size() > 1 && ! app.giveHelp() ) {
|
||||
QString msg = args.join( QLatin1String("|") );
|
||||
|
|
|
@ -512,11 +512,90 @@ QString Folder::configFile()
|
|||
return _configFile;
|
||||
}
|
||||
|
||||
static void addErroredSyncItemPathsToList(const SyncFileItemVector& items, QSet<QString>* set) {
|
||||
Q_FOREACH(const SyncFileItem &item, items) {
|
||||
if (item.hasErrorStatus()) {
|
||||
set->insert(item._file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Folder::slotThreadTreeWalkResult(const SyncFileItemVector& items)
|
||||
{
|
||||
addErroredSyncItemPathsToList(items, &this->_stateLastSyncItemsWithError);
|
||||
_syncResult.setSyncFileItemVector(items);
|
||||
}
|
||||
|
||||
void Folder::slotAboutToPropagate(const SyncFileItemVector& items)
|
||||
{
|
||||
// empty the tainted list since the status generation code will use the _syncedItems
|
||||
// (which imply the folder) to generate the syncing state icon now.
|
||||
_stateTaintedFolders.clear();
|
||||
|
||||
addErroredSyncItemPathsToList(items, &this->_stateLastSyncItemsWithError);
|
||||
}
|
||||
|
||||
|
||||
bool Folder::estimateState(QString fn, csync_ftw_type_e t, SyncFileStatus* s)
|
||||
{
|
||||
if (t == CSYNC_FTW_TYPE_DIR) {
|
||||
qDebug() << Q_FUNC_INFO << "ASKING ERROR FOLDERS" << fn;
|
||||
if (Utility::doesSetContainPrefix(_stateLastSyncItemsWithError, fn)) {
|
||||
s->set(SyncFileStatus::STATUS_ERROR);
|
||||
return true;
|
||||
}
|
||||
// If sync is running, check _syncedItems, possibly give it STATUS_EVAL (=syncing down)
|
||||
if (!_engine.isNull()) {
|
||||
qDebug() << Q_FUNC_INFO << "SYNC IS RUNNING, asking SyncEngine" << fn;
|
||||
if (_engine->estimateState(fn, t, s)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
qDebug() << Q_FUNC_INFO << "ASKING TAINTED FOLDERS" << fn;
|
||||
if (Utility::doesSetContainPrefix(_stateTaintedFolders, fn)) {
|
||||
qDebug() << Q_FUNC_INFO << "Folder is tainted, EVAL!" << fn;
|
||||
s->set(SyncFileStatus::STATUS_EVAL);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} else if ( t== CSYNC_FTW_TYPE_FILE) {
|
||||
// check if errorList has the directory/file
|
||||
if (Utility::doesSetContainPrefix(_stateLastSyncItemsWithError, fn)) {
|
||||
s->set(SyncFileStatus::STATUS_ERROR);
|
||||
return true;
|
||||
}
|
||||
// If sync running: _syncedItems -> SyncingState
|
||||
if (!_engine.isNull()) {
|
||||
if (_engine->estimateState(fn, t, s)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Folder::watcherSlot(QString fn)
|
||||
{
|
||||
// FIXME: On OS X we could not do this "if" since on OS X the file watcher ignores events for ourselves
|
||||
// however to have the same behaviour atm on all platforms, we don't do it
|
||||
if (!_engine.isNull()) {
|
||||
qDebug() << Q_FUNC_INFO << "Sync running, IGNORE event for " << fn;
|
||||
return;
|
||||
}
|
||||
QFileInfo fi(fn);
|
||||
if (fi.isFile()) {
|
||||
fn = fi.path(); // depending on OS, file watcher might be for dir or file
|
||||
}
|
||||
// Make it a relative path depending on the folder
|
||||
QString relativePath = fn.remove(0, path().length());
|
||||
qDebug() << Q_FUNC_INFO << fi.canonicalFilePath() << fn << relativePath;
|
||||
_stateTaintedFolders.insert(relativePath);
|
||||
|
||||
// Notify the SocketAPI?
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Folder::slotTerminateSync()
|
||||
{
|
||||
qDebug() << "folder " << alias() << " Terminating!";
|
||||
|
@ -639,6 +718,8 @@ void Folder::startSync(const QStringList &pathList)
|
|||
|
||||
connect( _engine.data(), SIGNAL(treeWalkResult(const SyncFileItemVector&)),
|
||||
this, SLOT(slotThreadTreeWalkResult(const SyncFileItemVector&)), Qt::QueuedConnection);
|
||||
connect( _engine.data(), SIGNAL(aboutToPropagate(const SyncFileItemVector&)),
|
||||
this, SLOT(slotAboutToPropagate(const SyncFileItemVector&)), Qt::QueuedConnection);
|
||||
|
||||
connect(_engine.data(), SIGNAL(started()), SLOT(slotSyncStarted()), Qt::QueuedConnection);
|
||||
connect(_engine.data(), SIGNAL(finished()), SLOT(slotSyncFinished()), Qt::QueuedConnection);
|
||||
|
@ -720,6 +801,15 @@ void Folder::slotSyncFinished()
|
|||
// _watcher->setEventsEnabledDelayed(2000);
|
||||
|
||||
|
||||
// This is for sync state calculation
|
||||
_stateLastSyncItemsWithError = _stateLastSyncItemsWithErrorNew;
|
||||
_stateLastSyncItemsWithErrorNew.clear();
|
||||
_stateTaintedFolders.clear(); // heuristic: assume the sync had been done, new file watches needed to taint dirs
|
||||
if (_csyncError || _csyncUnavail) {
|
||||
// Taint the whole sync dir, we cannot give reliable state information
|
||||
_stateTaintedFolders.insert(QLatin1String("/"));
|
||||
}
|
||||
|
||||
|
||||
if (_csyncError) {
|
||||
_syncResult.setStatus(SyncResult::Error);
|
||||
|
@ -786,6 +876,10 @@ void Folder::slotTransmissionProgress(const Progress::Info &pi)
|
|||
// a job is completed: count the errors and forward to the ProgressDispatcher
|
||||
void Folder::slotJobCompleted(const SyncFileItem &item)
|
||||
{
|
||||
if (item.hasErrorStatus()) {
|
||||
_stateLastSyncItemsWithError.insert(item._file);
|
||||
}
|
||||
|
||||
if (Progress::isWarningKind(item._status)) {
|
||||
// Count all error conditions.
|
||||
_syncResult.setWarnCount(_syncResult.warnCount()+1);
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
|
||||
#include <QDir>
|
||||
#include <QHash>
|
||||
#include <QSet>
|
||||
#include <QObject>
|
||||
#include <QStringList>
|
||||
|
||||
|
@ -34,15 +35,12 @@
|
|||
#include <QTimer>
|
||||
#include <qelapsedtimer.h>
|
||||
|
||||
class QFileSystemWatcher;
|
||||
class QThread;
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
class SyncEngine;
|
||||
|
||||
class FolderWatcher;
|
||||
|
||||
class Folder : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
@ -119,12 +117,12 @@ public:
|
|||
|
||||
// Used by the Socket API
|
||||
SyncJournalDb *journalDb() { return &_journal; }
|
||||
CSYNC *csyncContext() { return _csync_ctx; }
|
||||
|
||||
QStringList selectiveSyncBlackList() { return _selectiveSyncBlackList; }
|
||||
void setSelectiveSyncBlackList(const QStringList &blackList)
|
||||
{ _selectiveSyncBlackList = blackList; }
|
||||
|
||||
bool estimateState(QString fn, csync_ftw_type_e t, SyncFileStatus* s);
|
||||
|
||||
signals:
|
||||
void syncStateChange();
|
||||
|
@ -170,10 +168,13 @@ private slots:
|
|||
void etagRetreived(const QString &);
|
||||
void slotNetworkUnavailable();
|
||||
|
||||
void slotThreadTreeWalkResult(const SyncFileItemVector& );
|
||||
void slotAboutToPropagate(const SyncFileItemVector& );
|
||||
void slotThreadTreeWalkResult(const SyncFileItemVector& ); // after sync is done
|
||||
|
||||
void slotEmitFinishedDelayed();
|
||||
|
||||
void watcherSlot(QString);
|
||||
|
||||
private:
|
||||
bool init();
|
||||
|
||||
|
@ -204,6 +205,11 @@ private:
|
|||
QElapsedTimer _timeSinceLastSync;
|
||||
bool _forceSyncOnPollTimeout;
|
||||
|
||||
// For the SocketAPI folder states
|
||||
QSet<QString> _stateLastSyncItemsWithErrorNew; // gets moved to _stateLastSyncItemsWithError at end of sync
|
||||
QSet<QString> _stateLastSyncItemsWithError;
|
||||
QSet<QString> _stateTaintedFolders;
|
||||
|
||||
SyncJournalDb _journal;
|
||||
|
||||
ClientProxy _clientProxy;
|
||||
|
|
|
@ -147,6 +147,9 @@ void FolderMan::registerFolderMonitor( Folder *folder )
|
|||
connect(fw, SIGNAL(folderChanged(QString)), _folderWatcherSignalMapper, SLOT(map()));
|
||||
_folderWatcherSignalMapper->setMapping(fw, folder->alias());
|
||||
_folderWatchers.insert(folder->alias(), fw);
|
||||
|
||||
// This is at the moment only for the behaviour of the SocketApi.
|
||||
connect(fw, SIGNAL(folderChanged(QString)), folder, SLOT(watcherSlot(QString)));
|
||||
}
|
||||
|
||||
// register the folder with the socket API
|
||||
|
@ -437,19 +440,27 @@ void FolderMan::slotScheduleAllFolders()
|
|||
*/
|
||||
void FolderMan::slotScheduleSync( const QString& alias )
|
||||
{
|
||||
if( alias.isEmpty() || ! _folderMap.contains(alias) ) {
|
||||
qDebug() << "Not scheduling sync for empty or unknown folder" << alias;
|
||||
return;
|
||||
}
|
||||
|
||||
// The folder watcher fires a lot of bogus notifications during
|
||||
// a sync operation, both for actual user files and the database
|
||||
// and log. Never enqueue a folder for sync while it is syncing.
|
||||
// We lose some genuine sync requests that way, but that can't be
|
||||
// helped.
|
||||
// ^^ FIXME: Note that this is not the case on OS X
|
||||
if( _currentSyncFolder == alias ) {
|
||||
qDebug() << "folder " << alias << " is currently syncing. NOT scheduling.";
|
||||
return;
|
||||
}
|
||||
|
||||
if( alias.isEmpty() || ! _folderMap.contains(alias) ) {
|
||||
qDebug() << "Not scheduling sync for empty or unknown folder" << alias;
|
||||
return;
|
||||
if( _socketApi ) {
|
||||
// We want the SocketAPI to already now update so that it can show the EVAL icon
|
||||
// for files/folders. Only do this when not syncing, else we might get a lot
|
||||
// of those notifications.
|
||||
_socketApi->slotUpdateFolderView(alias);
|
||||
}
|
||||
|
||||
qDebug() << "Schedule folder " << alias << " to sync!";
|
||||
|
@ -578,11 +589,11 @@ Folder *FolderMan::folderForPath(const QString &path)
|
|||
const QString folderPath = QDir::cleanPath(folder->path())+QLatin1Char('/');
|
||||
|
||||
if(absolutePath.startsWith(folderPath)) {
|
||||
qDebug() << "found folder: " << folder->path() << " for " << absolutePath;
|
||||
//qDebug() << "found folder: " << folder->path() << " for " << absolutePath;
|
||||
return folder;
|
||||
}
|
||||
}
|
||||
|
||||
qDebug() << "ERROR: could not find folder for " << absolutePath;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -81,7 +81,7 @@ bool FolderWatcher::pathIsIgnored( const QString& path )
|
|||
|
||||
QFileInfo fInfo(path);
|
||||
if( fInfo.isHidden() ) {
|
||||
qDebug() << "* Discarded as is hidden!";
|
||||
qDebug() << "* Discarded as is hidden!" << fInfo.filePath();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
259
src/mirall/ownsql.cpp
Normal file
259
src/mirall/ownsql.cpp
Normal file
|
@ -0,0 +1,259 @@
|
|||
/*
|
||||
* Copyright (C) by Klaas Freitag <freitag@owncloud.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* 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 General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QString>
|
||||
#include <QDebug>
|
||||
|
||||
#include "ownsql.h"
|
||||
|
||||
#define SQLITE_DO(A) if(1) { \
|
||||
_errId = (A); if(_errId != SQLITE_OK) { _error= QString::fromUtf8(sqlite3_errmsg(_db)); \
|
||||
} }
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
SqlDatabase::SqlDatabase()
|
||||
:_db(0),
|
||||
_errId(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool SqlDatabase::isOpen()
|
||||
{
|
||||
return _db != 0;
|
||||
}
|
||||
|
||||
bool SqlDatabase::open( const QString& filename )
|
||||
{
|
||||
if(isOpen()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int flag = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|SQLITE_OPEN_NOMUTEX;
|
||||
SQLITE_DO( sqlite3_open_v2(filename.toUtf8().constData(), &_db, flag, 0) );
|
||||
|
||||
if( _errId != SQLITE_OK ) {
|
||||
close(); // FIXME: Correct?
|
||||
_db = 0;
|
||||
}
|
||||
return isOpen();
|
||||
}
|
||||
|
||||
QString SqlDatabase::error() const
|
||||
{
|
||||
const QString err(_error);
|
||||
// _error.clear();
|
||||
return err;
|
||||
}
|
||||
|
||||
void SqlDatabase::close()
|
||||
{
|
||||
if( _db ) {
|
||||
SQLITE_DO(sqlite3_close_v2(_db) );
|
||||
_db = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool SqlDatabase::transaction()
|
||||
{
|
||||
if( ! _db ) {
|
||||
return false;
|
||||
}
|
||||
SQLITE_DO(sqlite3_exec(_db, "BEGIN", 0, 0, 0));
|
||||
return _errId == SQLITE_OK;
|
||||
}
|
||||
|
||||
bool SqlDatabase::commit()
|
||||
{
|
||||
if( ! _db ) {
|
||||
return false;
|
||||
}
|
||||
SQLITE_DO(sqlite3_exec(_db, "COMMIT", 0, 0, 0));
|
||||
return _errId == SQLITE_OK;
|
||||
}
|
||||
|
||||
sqlite3* SqlDatabase::sqliteDb()
|
||||
{
|
||||
return _db;
|
||||
}
|
||||
|
||||
/* =========================================================================================== */
|
||||
|
||||
SqlQuery::SqlQuery( SqlDatabase db )
|
||||
:_db(db.sqliteDb()),
|
||||
_stmt(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
SqlQuery::~SqlQuery()
|
||||
{
|
||||
if( _stmt ) {
|
||||
sqlite3_finalize(_stmt);
|
||||
}
|
||||
}
|
||||
|
||||
SqlQuery::SqlQuery(const QString& sql, SqlDatabase db)
|
||||
:_db(db.sqliteDb()),
|
||||
_stmt(0)
|
||||
{
|
||||
prepare(sql);
|
||||
}
|
||||
|
||||
int SqlQuery::prepare( const QString& sql)
|
||||
{
|
||||
QString s(sql);
|
||||
_sql = s.trimmed();
|
||||
if(_stmt ) {
|
||||
finish();
|
||||
}
|
||||
if(!_sql.isEmpty() ) {
|
||||
SQLITE_DO(sqlite3_prepare_v2(_db, _sql.toUtf8().constData(), -1, &_stmt, 0));
|
||||
if( _errId != SQLITE_OK ) {
|
||||
qDebug() << "Sqlite prepare statement error:" << _error << "in"<<_sql;
|
||||
}
|
||||
// Q_ASSERT(_errId == SQLITE_OK);
|
||||
}
|
||||
return _errId;
|
||||
}
|
||||
|
||||
bool SqlQuery::isSelect()
|
||||
{
|
||||
return (!_sql.isEmpty() && _sql.startsWith("SELECT", Qt::CaseInsensitive));
|
||||
}
|
||||
|
||||
bool SqlQuery::isPragma()
|
||||
{
|
||||
return (!_sql.isEmpty() && _sql.startsWith("PRAGMA", Qt::CaseInsensitive));
|
||||
}
|
||||
|
||||
bool SqlQuery::exec()
|
||||
{
|
||||
// Don't do anything for selects, that is how we use the lib :-|
|
||||
if(_stmt && !isSelect() && !isPragma() ) {
|
||||
SQLITE_DO(sqlite3_step(_stmt));
|
||||
return (_errId == SQLITE_DONE); // either SQLITE_ROW or SQLITE_DONE
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SqlQuery::next()
|
||||
{
|
||||
SQLITE_DO(sqlite3_step(_stmt));
|
||||
return _errId == SQLITE_ROW;
|
||||
}
|
||||
|
||||
void SqlQuery::bindValue(int pos, const QVariant& value)
|
||||
{
|
||||
int res = -1;
|
||||
if( _stmt ) {
|
||||
switch (value.type()) {
|
||||
case QVariant::Int:
|
||||
case QVariant::Bool:
|
||||
res = sqlite3_bind_int(_stmt, pos, value.toInt());
|
||||
break;
|
||||
case QVariant::Double:
|
||||
res = sqlite3_bind_double(_stmt, pos, value.toDouble());
|
||||
break;
|
||||
case QVariant::UInt:
|
||||
case QVariant::LongLong:
|
||||
res = sqlite3_bind_int64(_stmt, pos, value.toLongLong());
|
||||
break;
|
||||
case QVariant::DateTime: {
|
||||
const QDateTime dateTime = value.toDateTime();
|
||||
const QString str = dateTime.toString(QLatin1String("yyyy-MM-ddThh:mm:ss.zzz"));
|
||||
res = sqlite3_bind_text16(_stmt, pos, str.utf16(),
|
||||
str.size() * sizeof(ushort), SQLITE_TRANSIENT);
|
||||
break;
|
||||
}
|
||||
case QVariant::Time: {
|
||||
const QTime time = value.toTime();
|
||||
const QString str = time.toString(QLatin1String("hh:mm:ss.zzz"));
|
||||
res = sqlite3_bind_text16(_stmt, pos, str.utf16(),
|
||||
str.size() * sizeof(ushort), SQLITE_TRANSIENT);
|
||||
break;
|
||||
}
|
||||
case QVariant::String: {
|
||||
if( !value.toString().isNull() ) {
|
||||
// lifetime of string == lifetime of its qvariant
|
||||
const QString *str = static_cast<const QString*>(value.constData());
|
||||
res = sqlite3_bind_text16(_stmt, pos, str->utf16(),
|
||||
(str->size()) * sizeof(QChar), SQLITE_TRANSIENT);
|
||||
} else {
|
||||
// unbound value create a null entry.
|
||||
res = SQLITE_OK;
|
||||
}
|
||||
break; }
|
||||
default: {
|
||||
QString str = value.toString();
|
||||
// SQLITE_TRANSIENT makes sure that sqlite buffers the data
|
||||
res = sqlite3_bind_text16(_stmt, pos, str.utf16(),
|
||||
(str.size()) * sizeof(QChar), SQLITE_TRANSIENT);
|
||||
break; }
|
||||
}
|
||||
}
|
||||
Q_ASSERT( res == SQLITE_OK );
|
||||
}
|
||||
|
||||
QString SqlQuery::stringValue(int index)
|
||||
{
|
||||
return QString::fromUtf16(static_cast<const ushort*>(sqlite3_column_text16(_stmt, index)));
|
||||
}
|
||||
|
||||
int SqlQuery::intValue(int index)
|
||||
{
|
||||
return sqlite3_column_int(_stmt, index);
|
||||
}
|
||||
|
||||
quint64 SqlQuery::int64Value(int index)
|
||||
{
|
||||
return sqlite3_column_int64(_stmt, index);
|
||||
}
|
||||
|
||||
QByteArray SqlQuery::baValue(int index)
|
||||
{
|
||||
return QByteArray( static_cast<const char*>(sqlite3_column_blob(_stmt, index)),
|
||||
sqlite3_column_bytes(_stmt, index));
|
||||
}
|
||||
|
||||
QString SqlQuery::error() const
|
||||
{
|
||||
return _error;
|
||||
}
|
||||
|
||||
QString SqlQuery::lastQuery() const
|
||||
{
|
||||
return _sql;
|
||||
}
|
||||
|
||||
int SqlQuery::numRowsAffected()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
void SqlQuery::finish()
|
||||
{
|
||||
SQLITE_DO(sqlite3_finalize(_stmt));
|
||||
_stmt = 0;
|
||||
}
|
||||
|
||||
void SqlQuery::reset()
|
||||
{
|
||||
SQLITE_DO(sqlite3_reset(_stmt));
|
||||
}
|
||||
|
||||
} // namespace Mirall
|
81
src/mirall/ownsql.h
Normal file
81
src/mirall/ownsql.h
Normal file
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* Copyright (C) by Klaas Freitag <freitag@owncloud.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* 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 General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#ifndef OWNSQL_H
|
||||
#define OWNSQL_H
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QVariant>
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
class SqlDatabase
|
||||
{
|
||||
public:
|
||||
explicit SqlDatabase();
|
||||
|
||||
bool isOpen();
|
||||
bool open( const QString& filename );
|
||||
bool transaction();
|
||||
bool commit();
|
||||
void close();
|
||||
QString error() const;
|
||||
sqlite3* sqliteDb();
|
||||
|
||||
private:
|
||||
sqlite3 *_db;
|
||||
QString _error; // last error string
|
||||
int _errId;
|
||||
|
||||
};
|
||||
|
||||
class SqlQuery
|
||||
{
|
||||
Q_DISABLE_COPY(SqlQuery)
|
||||
public:
|
||||
explicit SqlQuery();
|
||||
explicit SqlQuery(SqlDatabase db);
|
||||
explicit SqlQuery(const QString& sql, SqlDatabase db);
|
||||
|
||||
~SqlQuery();
|
||||
QString error() const;
|
||||
|
||||
QString stringValue(int index);
|
||||
int intValue(int index);
|
||||
quint64 int64Value(int index);
|
||||
QByteArray baValue(int index);
|
||||
|
||||
bool isSelect();
|
||||
bool isPragma();
|
||||
bool exec();
|
||||
int prepare( const QString& sql );
|
||||
bool next();
|
||||
void bindValue(int pos, const QVariant& value);
|
||||
QString lastQuery() const;
|
||||
int numRowsAffected();
|
||||
void reset();
|
||||
void finish();
|
||||
|
||||
private:
|
||||
sqlite3 *_db;
|
||||
sqlite3_stmt *_stmt;
|
||||
QString _error;
|
||||
int _errId;
|
||||
QString _sql;
|
||||
};
|
||||
|
||||
} // namespace Mirall
|
||||
|
||||
#endif // OWNSQL_H
|
|
@ -35,6 +35,9 @@
|
|||
#include <QApplication>
|
||||
#include <QLocalSocket>
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
|
||||
#include <QStandardPaths>
|
||||
#endif
|
||||
|
@ -64,159 +67,6 @@ namespace Mirall {
|
|||
|
||||
#define DEBUG qDebug() << "SocketApi: "
|
||||
|
||||
namespace SocketApiHelper {
|
||||
|
||||
SyncFileStatus fileStatus(Folder *folder, const QString& systemFileName, c_strlist_t *excludes );
|
||||
|
||||
/**
|
||||
* @brief recursiveFolderStatus
|
||||
* @param fileName - the relative file name to examine
|
||||
* @return the resulting status
|
||||
*
|
||||
* The resulting status can only be either SYNC which means all files
|
||||
* are in sync, ERROR if an error occured, or EVAL if something needs
|
||||
* to be synced underneath this dir.
|
||||
*/
|
||||
// compute the file status of a directory recursively. It returns either
|
||||
// "all in sync" or "needs update" or "error", no more details.
|
||||
SyncFileStatus recursiveFolderStatus(Folder *folder, const QString& fileName, c_strlist_t *excludes )
|
||||
{
|
||||
QDir dir(folder->path() + fileName);
|
||||
|
||||
const QStringList dirEntries = dir.entryList( QDir::AllEntries | QDir::NoDotAndDotDot );
|
||||
|
||||
SyncFileStatus result(SyncFileStatus::STATUS_SYNC);
|
||||
|
||||
foreach( const QString entry, dirEntries ) {
|
||||
QString normalizedFile = QString(fileName + QLatin1Char('/') + entry).normalized(QString::NormalizationForm_C);
|
||||
QFileInfo fi(entry);
|
||||
SyncFileStatus sfs;
|
||||
|
||||
if( fi.isDir() ) {
|
||||
sfs = recursiveFolderStatus(folder, normalizedFile, excludes );
|
||||
} else {
|
||||
QString fs( normalizedFile );
|
||||
if( fileName.isEmpty() ) {
|
||||
// toplevel, no slash etc. needed.
|
||||
fs = entry.normalized(QString::NormalizationForm_C);
|
||||
}
|
||||
sfs = fileStatus(folder, fs, excludes);
|
||||
}
|
||||
|
||||
if( sfs.tag() == SyncFileStatus::STATUS_STAT_ERROR || sfs.tag() == SyncFileStatus::STATUS_ERROR ) {
|
||||
return SyncFileStatus::STATUS_ERROR;
|
||||
} else if( sfs.tag() == SyncFileStatus::STATUS_EVAL || sfs.tag() == SyncFileStatus::STATUS_NEW) {
|
||||
result.set(SyncFileStatus::STATUS_EVAL);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
SyncJournalFileRecord dbFileRecord( Folder *folder, QString fileName )
|
||||
{
|
||||
QFileInfo fi(fileName);
|
||||
if( !folder ) {
|
||||
return SyncJournalFileRecord();
|
||||
}
|
||||
if( fi.isAbsolute() ) {
|
||||
fileName.remove(0, folder->path().length());
|
||||
}
|
||||
return( folder->journalDb()->getFileRecord(fileName) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get status about a single file.
|
||||
*/
|
||||
SyncFileStatus fileStatus(Folder *folder, const QString& systemFileName, c_strlist_t *excludes )
|
||||
{
|
||||
QString file = folder->path();
|
||||
QString fileName = systemFileName.normalized(QString::NormalizationForm_C);
|
||||
|
||||
bool isSyncRootFolder = true;
|
||||
if( fileName != QLatin1String("/") && !fileName.isEmpty() ) {
|
||||
file = folder->path() + fileName;
|
||||
isSyncRootFolder = false;
|
||||
}
|
||||
|
||||
if( fileName.endsWith(QLatin1Char('/')) ) {
|
||||
fileName.truncate(fileName.length()-1);
|
||||
qDebug() << "Removed trailing slash: " << fileName;
|
||||
}
|
||||
|
||||
QFileInfo fi(file);
|
||||
|
||||
if( !fi.exists() ) {
|
||||
qDebug() << "OO File " << file << " is not existing";
|
||||
return SyncFileStatus(SyncFileStatus::STATUS_STAT_ERROR);
|
||||
}
|
||||
|
||||
// file is ignored?
|
||||
if( fi.isSymLink() ) {
|
||||
return SyncFileStatus(SyncFileStatus::STATUS_IGNORE);
|
||||
}
|
||||
int type = CSYNC_FTW_TYPE_FILE;
|
||||
if( fi.isDir() ) {
|
||||
type = CSYNC_FTW_TYPE_DIR;
|
||||
}
|
||||
|
||||
// '\' is ignored, so convert to unix path before passing the path in.
|
||||
QString unixFileName = QDir::fromNativeSeparators(fileName);
|
||||
|
||||
CSYNC_EXCLUDE_TYPE excl = csync_excluded_no_ctx(excludes, unixFileName.toUtf8(), type);
|
||||
if( excl != CSYNC_NOT_EXCLUDED ) {
|
||||
return SyncFileStatus(SyncFileStatus::STATUS_IGNORE);
|
||||
}
|
||||
|
||||
// Problem: for the sync dir itself we do not have a record in the sync journal
|
||||
// so the next check must not be used for the sync root folder.
|
||||
SyncJournalFileRecord rec = dbFileRecord(folder, unixFileName );
|
||||
if( !isSyncRootFolder && !rec.isValid() ) {
|
||||
// check the parent folder if it is shared and if it is allowed to create a file/dir within
|
||||
QDir d( fi.path() );
|
||||
QString parentPath = d.path();
|
||||
SyncJournalFileRecord dirRec = dbFileRecord(folder, parentPath);
|
||||
while( !d.isRoot() && !(d.exists() && dirRec.isValid()) ) {
|
||||
d.cdUp(); // returns true if the dir exists.
|
||||
|
||||
parentPath = d.path();
|
||||
// cut the folder path
|
||||
dirRec = dbFileRecord(folder, parentPath);
|
||||
}
|
||||
if( dirRec.isValid() ) {
|
||||
if( dirRec._type == CSYNC_FTW_TYPE_DIR ) {
|
||||
if( !dirRec._remotePerm.contains("K") ) {
|
||||
return SyncFileStatus::STATUS_ERROR;
|
||||
}
|
||||
} else {
|
||||
if( !dirRec._remotePerm.contains("C") ) {
|
||||
return SyncFileStatus::STATUS_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
return SyncFileStatus(SyncFileStatus::STATUS_NEW);
|
||||
}
|
||||
|
||||
SyncFileStatus status(SyncFileStatus::STATUS_NONE);
|
||||
if( type == CSYNC_FTW_TYPE_DIR ) {
|
||||
// compute recursive status of the directory
|
||||
status = recursiveFolderStatus( folder, fileName, excludes );
|
||||
} else if( FileSystem::getModTime(fi.absoluteFilePath()) != Utility::qDateTimeToTime_t(rec._modtime) ) {
|
||||
// file was locally modified.
|
||||
status.set(SyncFileStatus::STATUS_EVAL);
|
||||
} else {
|
||||
status.set(SyncFileStatus::STATUS_SYNC);
|
||||
}
|
||||
|
||||
if (rec._remotePerm.contains("S")) {
|
||||
status.setSharedWithMe(true);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
SocketApi::SocketApi(QObject* parent)
|
||||
: QObject(parent)
|
||||
, _excludes(0)
|
||||
|
@ -374,6 +224,11 @@ void SocketApi::slotUnregisterPath( const QString& alias )
|
|||
Folder *f = FolderMan::instance()->folder(alias);
|
||||
if (f) {
|
||||
broadcastMessage(QLatin1String("UNREGISTER_PATH"), f->path(), QString::null, true );
|
||||
|
||||
if( _dbConnections.contains(f)) {
|
||||
sqlite3_close_v2(_dbConnections[f]._db);
|
||||
}
|
||||
_dbConnections.remove(f);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -393,6 +248,8 @@ void SocketApi::slotUpdateFolderView(const QString& alias)
|
|||
} else {
|
||||
broadcastMessage(QLatin1String("UPDATE_VIEW"), f->path() );
|
||||
}
|
||||
} else {
|
||||
qDebug() << "Not sending UPDATE_VIEW for" << alias << "because status() is" << f->syncResult().status();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -464,7 +321,8 @@ void SocketApi::broadcastMessage( const QString& verb, const QString& path, cons
|
|||
msg.append(QDir::toNativeSeparators(path));
|
||||
}
|
||||
|
||||
DEBUG << "Broadcasting to" << _listeners.count() << "listeners: " << msg;
|
||||
// sendMessage already has a debug output
|
||||
//DEBUG << "Broadcasting to" << _listeners.count() << "listeners: " << msg;
|
||||
foreach(SocketType *socket, _listeners) {
|
||||
sendMessage(socket, msg, doWait);
|
||||
}
|
||||
|
@ -495,10 +353,11 @@ void SocketApi::command_RETRIEVE_FILE_STATUS(const QString& argument, SocketType
|
|||
DEBUG << "folder offline or not watched:" << argument;
|
||||
statusString = QLatin1String("NOP");
|
||||
} else {
|
||||
const QString file = argument.mid(syncFolder->path().length());
|
||||
SyncFileStatus fileStatus = SocketApiHelper::fileStatus(syncFolder, file, _excludes);
|
||||
QString file = argument.mid(syncFolder->path().length());
|
||||
if( file.isEmpty() ) file = QLatin1String("/");
|
||||
SyncFileStatus fStatus = this->fileStatus(syncFolder, file, _excludes);
|
||||
|
||||
statusString = fileStatus.toSocketAPIString();
|
||||
statusString = fStatus.toSocketAPIString();
|
||||
}
|
||||
|
||||
QString message = QLatin1String("STATUS:")+statusString+QLatin1Char(':')
|
||||
|
@ -511,6 +370,178 @@ void SocketApi::command_VERSION(const QString&, SocketType* socket)
|
|||
sendMessage(socket, QLatin1String("VERSION:" MIRALL_VERSION_STRING ":" MIRALL_SOCKET_API_VERSION));
|
||||
}
|
||||
|
||||
SqliteHandle SocketApi::getSqliteHandle( Folder *folder )
|
||||
{
|
||||
if( _dbConnections.contains(folder) ) {
|
||||
return _dbConnections[folder];
|
||||
}
|
||||
SqliteHandle h;
|
||||
h._db = NULL;
|
||||
h._stmt = NULL;
|
||||
|
||||
if( !folder ) {
|
||||
return h;
|
||||
}
|
||||
int rc;
|
||||
const char* query = "SELECT inode, mode, modtime, type, md5, fileid, remotePerm FROM "
|
||||
"metadata WHERE phash=:ph";
|
||||
QString dbFileName = folder->journalDb()->databaseFilePath();
|
||||
|
||||
if( sqlite3_open_v2(dbFileName.toUtf8().constData(), &(h._db),
|
||||
SQLITE_OPEN_READONLY+SQLITE_OPEN_NOMUTEX, NULL) == SQLITE_OK ) {
|
||||
|
||||
rc = sqlite3_prepare_v2(h._db, query, strlen(query), &(h._stmt), NULL);
|
||||
if( rc != SQLITE_OK ) {
|
||||
qDebug() << "Unable to prepare the query statement.";
|
||||
return h; // do not insert into hash
|
||||
}
|
||||
_dbConnections.insert( folder, h);
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
SyncJournalFileRecord SocketApi::dbFileRecord_capi( Folder *folder, QString fileName )
|
||||
{
|
||||
if( !(folder && folder->journalDb()) ) {
|
||||
return SyncJournalFileRecord();
|
||||
}
|
||||
|
||||
if( fileName.startsWith( folder->path() )) {
|
||||
fileName.remove(0, folder->path().length());
|
||||
}
|
||||
|
||||
SqliteHandle h = getSqliteHandle(folder);
|
||||
|
||||
sqlite3_stmt *stmt = h._stmt;
|
||||
SyncJournalFileRecord rec;
|
||||
|
||||
if( h._db && h._stmt ) {
|
||||
qlonglong phash = SyncJournalDb::getPHash( fileName );
|
||||
sqlite3_bind_int64(stmt, 1, (long long signed int)phash);
|
||||
|
||||
// int column_count = sqlite3_column_count(stmt);
|
||||
|
||||
int rc = sqlite3_step(stmt);
|
||||
|
||||
if (rc == SQLITE_ROW ) {
|
||||
rec._path = fileName;
|
||||
rec._inode = sqlite3_column_int64(stmt,0);;
|
||||
rec._mode = sqlite3_column_int(stmt, 1);
|
||||
rec._modtime = Utility::qDateTimeFromTime_t( strtoul((char*)sqlite3_column_text(stmt, 2), NULL, 10));
|
||||
rec._type = sqlite3_column_int(stmt, 3);;
|
||||
rec._etag = QByteArray((char*)sqlite3_column_text(stmt, 4));
|
||||
rec._fileId = QByteArray((char*)sqlite3_column_text(stmt, 5));
|
||||
rec._remotePerm = QByteArray((char*)sqlite3_column_text(stmt, 6));
|
||||
}
|
||||
sqlite3_reset(stmt);
|
||||
}
|
||||
return rec;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get status about a single file.
|
||||
*/
|
||||
SyncFileStatus SocketApi::fileStatus(Folder *folder, const QString& systemFileName, c_strlist_t *excludes )
|
||||
{
|
||||
QString file = folder->path();
|
||||
QString fileName = systemFileName.normalized(QString::NormalizationForm_C);
|
||||
|
||||
bool isSyncRootFolder = true;
|
||||
if( fileName != QLatin1String("/") && !fileName.isEmpty() ) {
|
||||
file = folder->path() + fileName;
|
||||
isSyncRootFolder = false;
|
||||
}
|
||||
|
||||
if( fileName.endsWith(QLatin1Char('/')) ) {
|
||||
fileName.truncate(fileName.length()-1);
|
||||
qDebug() << "Removed trailing slash: " << fileName;
|
||||
}
|
||||
|
||||
QFileInfo fi(file);
|
||||
|
||||
if( !fi.exists() ) {
|
||||
qDebug() << "OO File " << file << " is not existing";
|
||||
return SyncFileStatus(SyncFileStatus::STATUS_STAT_ERROR);
|
||||
}
|
||||
|
||||
// file is ignored?
|
||||
if( fi.isSymLink() ) {
|
||||
return SyncFileStatus(SyncFileStatus::STATUS_IGNORE);
|
||||
}
|
||||
|
||||
csync_ftw_type_e type = CSYNC_FTW_TYPE_FILE;
|
||||
if( fi.isDir() ) {
|
||||
type = CSYNC_FTW_TYPE_DIR;
|
||||
}
|
||||
|
||||
// '\' is ignored, so convert to unix path before passing the path in.
|
||||
QString unixFileName = QDir::fromNativeSeparators(fileName);
|
||||
|
||||
CSYNC_EXCLUDE_TYPE excl = csync_excluded_no_ctx(excludes, unixFileName.toUtf8(), type);
|
||||
if( excl != CSYNC_NOT_EXCLUDED ) {
|
||||
return SyncFileStatus(SyncFileStatus::STATUS_IGNORE);
|
||||
}
|
||||
|
||||
|
||||
SyncFileStatus status(SyncFileStatus::STATUS_NONE);
|
||||
if (type == CSYNC_FTW_TYPE_DIR) {
|
||||
if (folder->estimateState(fileName, type, &status)) {
|
||||
qDebug() << Q_FUNC_INFO << "Folder estimated status for" << fileName << "to" << status.toSocketAPIString();
|
||||
return status;
|
||||
}
|
||||
if (fileName == "") {
|
||||
// sync folder itself
|
||||
if (folder->syncResult().status() == SyncResult::Undefined
|
||||
|| folder->syncResult().status() == SyncResult::NotYetStarted
|
||||
|| folder->syncResult().status() == SyncResult::SyncPrepare
|
||||
|| folder->syncResult().status() == SyncResult::SyncRunning
|
||||
|| folder->syncResult().status() == SyncResult::Paused) {
|
||||
status.set(SyncFileStatus::STATUS_EVAL);
|
||||
return status;
|
||||
} else if (folder->syncResult().status() == SyncResult::Success
|
||||
|| folder->syncResult().status() == SyncResult::Problem) {
|
||||
status.set(SyncFileStatus::STATUS_SYNC);
|
||||
return status;
|
||||
} else if (folder->syncResult().status() == SyncResult::Error
|
||||
|| folder->syncResult().status() == SyncResult::SetupError
|
||||
|| folder->syncResult().status() == SyncResult::SyncAbortRequested) {
|
||||
status.set(SyncFileStatus::STATUS_ERROR);
|
||||
return status;
|
||||
}
|
||||
}
|
||||
SyncJournalFileRecord rec = dbFileRecord_capi(folder, unixFileName );
|
||||
if (rec.isValid()) {
|
||||
status.set(SyncFileStatus::STATUS_SYNC);
|
||||
if (rec._remotePerm.contains("S")) {
|
||||
status.setSharedWithMe(true);
|
||||
}
|
||||
} else {
|
||||
status.set(SyncFileStatus::STATUS_EVAL);
|
||||
}
|
||||
} else if (type == CSYNC_FTW_TYPE_FILE) {
|
||||
if (folder->estimateState(fileName, type, &status)) {
|
||||
return status;
|
||||
}
|
||||
SyncJournalFileRecord rec = dbFileRecord_capi(folder, unixFileName );
|
||||
if (rec.isValid()) {
|
||||
if (rec._remotePerm.contains("S")) {
|
||||
status.setSharedWithMe(true);
|
||||
}
|
||||
if( FileSystem::getModTime(fi.absoluteFilePath()) == Utility::qDateTimeToTime_t(rec._modtime) ) {
|
||||
status.set(SyncFileStatus::STATUS_SYNC);
|
||||
return status;
|
||||
} else {
|
||||
status.set(SyncFileStatus::STATUS_EVAL);
|
||||
return status;
|
||||
}
|
||||
}
|
||||
status.set(SyncFileStatus::STATUS_NEW);
|
||||
return status;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
} // namespace Mirall
|
||||
|
||||
|
|
|
@ -20,13 +20,15 @@ extern "C" {
|
|||
#include <std/c_string.h>
|
||||
}
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
#include <QWeakPointer>
|
||||
#include <QTcpSocket>
|
||||
#include <QTcpServer>
|
||||
#include <QLocalServer>
|
||||
|
||||
#include "mirall/syncfileitem.h"
|
||||
|
||||
#include "mirall/syncjournalfilerecord.h"
|
||||
class QUrl;
|
||||
class QLocalSocket;
|
||||
class QStringList;
|
||||
|
@ -35,6 +37,15 @@ namespace Mirall {
|
|||
|
||||
typedef QLocalSocket SocketType;
|
||||
|
||||
class SyncFileStatus;
|
||||
class Folder;
|
||||
|
||||
struct SqliteHandle
|
||||
{
|
||||
sqlite3 *_db;
|
||||
sqlite3_stmt *_stmt;
|
||||
};
|
||||
|
||||
class SocketApi : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
@ -57,6 +68,12 @@ private slots:
|
|||
void slotSyncItemDiscovered(const QString &, const SyncFileItem &);
|
||||
|
||||
private:
|
||||
SyncFileStatus fileStatus(Folder *folder, const QString& systemFileName, c_strlist_t *excludes );
|
||||
SyncJournalFileRecord dbFileRecord( Folder *folder, QString fileName );
|
||||
SyncJournalFileRecord dbFileRecord_capi( Folder *folder, QString fileName );
|
||||
SyncFileStatus recursiveFolderStatus(Folder *folder, const QString& fileName, c_strlist_t *excludes );
|
||||
SqliteHandle getSqliteHandle( Folder *folder );
|
||||
|
||||
void sendMessage(SocketType* socket, const QString& message, bool doWait = false);
|
||||
void broadcastMessage(const QString& verb, const QString &path, const QString &status = QString::null, bool doWait = false);
|
||||
|
||||
|
@ -65,7 +82,6 @@ private:
|
|||
|
||||
Q_INVOKABLE void command_VERSION(const QString& argument, SocketType* socket);
|
||||
|
||||
private:
|
||||
#ifdef SOCKETAPI_TCP
|
||||
QTcpServer _localServer;
|
||||
#else
|
||||
|
@ -73,6 +89,7 @@ private:
|
|||
#endif
|
||||
QList<SocketType*> _listeners;
|
||||
c_strlist_t *_excludes;
|
||||
QHash<Folder*, SqliteHandle> _dbConnections;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "discoveryphase.h"
|
||||
#include "creds/abstractcredentials.h"
|
||||
#include "csync_util.h"
|
||||
#include "mirall/syncfilestatus.h"
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include <windows.h>
|
||||
|
@ -681,14 +682,16 @@ void SyncEngine::slotDiscoveryJobFinished(int discoveryResult)
|
|||
Q_ASSERT(session);
|
||||
|
||||
// post update phase script: allow to tweak stuff by a custom script in debug mode.
|
||||
#ifndef NDEBUG
|
||||
if( !qgetenv("OWNCLOUD_POST_UPDATE_SCRIPT").isEmpty() ) {
|
||||
#ifndef NDEBUG
|
||||
QString script = qgetenv("OWNCLOUD_POST_UPDATE_SCRIPT");
|
||||
|
||||
qDebug() << "OOO => Post Update Script: " << script;
|
||||
QProcess::execute(script.toUtf8());
|
||||
}
|
||||
#else
|
||||
qDebug() << "**** Attention: POST_UPDATE_SCRIPT installed, but not executed because compiled with NDEBUG";
|
||||
#endif
|
||||
}
|
||||
|
||||
// do a database commit
|
||||
_journal->commit("post treewalk");
|
||||
|
@ -1069,7 +1072,18 @@ void SyncEngine::setSelectiveSyncBlackList(const QStringList& list)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
bool SyncEngine::estimateState(QString fn, csync_ftw_type_e t, SyncFileStatus* s)
|
||||
{
|
||||
Q_FOREACH(const SyncFileItem &item, _syncedItems) {
|
||||
//qDebug() << Q_FUNC_INFO << fn << item._file << fn.startsWith(item._file) << item._file.startsWith(fn);
|
||||
if (item._file.startsWith(fn)) {
|
||||
qDebug() << Q_FUNC_INFO << "Setting" << fn << " to STATUS_EVAL";
|
||||
s->set(SyncFileStatus::STATUS_EVAL);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SyncEngine::abort()
|
||||
{
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include "mirall/syncfileitem.h"
|
||||
#include "mirall/progressdispatcher.h"
|
||||
#include "mirall/utility.h"
|
||||
#include "mirall/syncfilestatus.h"
|
||||
|
||||
class QProcess;
|
||||
|
||||
|
@ -66,6 +67,8 @@ public:
|
|||
/* Return true if we detected that another sync is needed to complete the sync */
|
||||
bool isAnotherSyncNeeded() { return _anotherSyncNeeded; }
|
||||
|
||||
bool estimateState(QString fn, csync_ftw_type_e t, SyncFileStatus* s);
|
||||
|
||||
signals:
|
||||
void csyncError( const QString& );
|
||||
void csyncUnavailable();
|
||||
|
@ -123,7 +126,11 @@ private:
|
|||
void finalize();
|
||||
|
||||
static bool _syncRunning; //true when one sync is running somewhere (for debugging)
|
||||
|
||||
QMap<QString, SyncFileItem> _syncItemMap;
|
||||
|
||||
// should be called _syncItems (present tense). It's the items from the _syncItemMap but
|
||||
// sorted and re-adjusted based on permissions.
|
||||
SyncFileItemVector _syncedItems;
|
||||
|
||||
CSYNC *_csync_ctx;
|
||||
|
|
|
@ -72,6 +72,13 @@ public:
|
|||
return _file.isEmpty();
|
||||
}
|
||||
|
||||
bool hasErrorStatus() const {
|
||||
return _status == SyncFileItem::SoftError
|
||||
|| _status == SyncFileItem::NormalError
|
||||
|| _status == SyncFileItem::FatalError
|
||||
|| !_errorString.isEmpty();
|
||||
}
|
||||
|
||||
// Variables usefull for everybody
|
||||
QString _file;
|
||||
QString _renameTarget;
|
||||
|
|
|
@ -14,8 +14,7 @@
|
|||
#include <QFile>
|
||||
#include <QStringList>
|
||||
#include <QDebug>
|
||||
#include <QSqlError>
|
||||
#include <QSqlQuery>
|
||||
#include "mirall/ownsql.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
|
@ -26,8 +25,6 @@
|
|||
|
||||
#include "../../csync/src/std/c_jhash.h"
|
||||
|
||||
#define QSQLITE "QSQLITE"
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
SyncJournalDb::SyncJournalDb(const QString& path, QObject *parent) :
|
||||
|
@ -49,15 +46,20 @@ bool SyncJournalDb::exists()
|
|||
return (!_dbFile.isEmpty() && QFile::exists(_dbFile));
|
||||
}
|
||||
|
||||
QString SyncJournalDb::databaseFilePath()
|
||||
{
|
||||
return _dbFile;
|
||||
}
|
||||
|
||||
void SyncJournalDb::startTransaction()
|
||||
{
|
||||
if( _transaction == 0 ) {
|
||||
if( !_db.transaction() ) {
|
||||
qDebug() << "ERROR starting transaction: " << _db.lastError().text();
|
||||
qDebug() << "ERROR starting transaction: " << _db.error();
|
||||
return;
|
||||
}
|
||||
_transaction = 1;
|
||||
// qDebug() << "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Transaction start!";
|
||||
// qDebug() << "XXX Transaction start!";
|
||||
} else {
|
||||
qDebug() << "Database Transaction is running, do not starting another one!";
|
||||
}
|
||||
|
@ -67,20 +69,20 @@ void SyncJournalDb::commitTransaction()
|
|||
{
|
||||
if( _transaction == 1 ) {
|
||||
if( ! _db.commit() ) {
|
||||
qDebug() << "ERROR committing to the database: " << _db.lastError().text();
|
||||
qDebug() << "ERROR committing to the database: " << _db.error();
|
||||
return;
|
||||
}
|
||||
_transaction = 0;
|
||||
// qDebug() << "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Transaction END!";
|
||||
// qDebug() << "XXX Transaction END!";
|
||||
} else {
|
||||
qDebug() << "No database Transaction to commit";
|
||||
}
|
||||
}
|
||||
|
||||
bool SyncJournalDb::sqlFail( const QString& log, const QSqlQuery& query )
|
||||
bool SyncJournalDb::sqlFail( const QString& log, const SqlQuery& query )
|
||||
{
|
||||
commitTransaction();
|
||||
qWarning() << "SQL Error" << log << query.lastError().text();
|
||||
qWarning() << "SQL Error" << log << query.error();
|
||||
Q_ASSERT(!"SQL ERROR");
|
||||
_db.close();
|
||||
return false;
|
||||
|
@ -97,38 +99,19 @@ bool SyncJournalDb::checkConnect()
|
|||
return false;
|
||||
}
|
||||
|
||||
QStringList list = QSqlDatabase::drivers();
|
||||
if( list.size() == 0 ) {
|
||||
qDebug() << "Database Drivers could not be loaded.";
|
||||
return false ;
|
||||
} else {
|
||||
if( list.indexOf( QSQLITE ) == -1 ) {
|
||||
qDebug() << "Database Driver QSQLITE could not be loaded!";
|
||||
if( !_db.open(_dbFile) ) {
|
||||
QString error = _db.error();
|
||||
qDebug() << "Error opening the db: " << error;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the connection
|
||||
_db = QSqlDatabase::addDatabase( QSQLITE, _dbFile);
|
||||
|
||||
// Open the file
|
||||
_db.setDatabaseName(_dbFile);
|
||||
|
||||
if (!_db.isOpen()) {
|
||||
if( !_db.open() ) {
|
||||
QSqlError error = _db.lastError();
|
||||
qDebug() << "Error opening the db: " << error.text();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
QSqlQuery pragma1(_db);
|
||||
SqlQuery pragma1(_db);
|
||||
pragma1.prepare("SELECT sqlite_version();");
|
||||
if (!pragma1.exec()) {
|
||||
return sqlFail("SELECT sqlite_version()", pragma1);
|
||||
} else {
|
||||
pragma1.next();
|
||||
qDebug() << "sqlite3 version" << pragma1.value(0).toString();
|
||||
qDebug() << "sqlite3 version" << pragma1.stringValue(0);
|
||||
}
|
||||
|
||||
pragma1.prepare("PRAGMA synchronous = 1;");
|
||||
|
@ -143,7 +126,7 @@ bool SyncJournalDb::checkConnect()
|
|||
/* Because insert are so slow, e do everything in a transaction, and one need to call commit */
|
||||
startTransaction();
|
||||
|
||||
QSqlQuery createQuery(_db);
|
||||
SqlQuery createQuery(_db);
|
||||
createQuery.prepare("CREATE TABLE IF NOT EXISTS metadata("
|
||||
"phash INTEGER(8),"
|
||||
"pathlen INTEGER,"
|
||||
|
@ -214,7 +197,7 @@ bool SyncJournalDb::checkConnect()
|
|||
return sqlFail("Create table version", createQuery);
|
||||
}
|
||||
|
||||
QSqlQuery versionQuery("SELECT major, minor FROM version;", _db);
|
||||
SqlQuery versionQuery("SELECT major, minor FROM version;", _db);
|
||||
if (!versionQuery.next()) {
|
||||
// If there was no entry in the table, it means we are likely upgrading from 1.5
|
||||
_possibleUpgradeFromMirall_1_5 = true;
|
||||
|
@ -225,10 +208,10 @@ bool SyncJournalDb::checkConnect()
|
|||
return sqlFail("Remove version", createQuery);
|
||||
}
|
||||
}
|
||||
createQuery.prepare("INSERT INTO version (major, minor, patch) VALUES ( ? , ? , ? );");
|
||||
createQuery.bindValue(0, MIRALL_VERSION_MAJOR);
|
||||
createQuery.bindValue(1, MIRALL_VERSION_MINOR);
|
||||
createQuery.bindValue(2, MIRALL_VERSION_PATCH);
|
||||
createQuery.prepare("INSERT INTO version (major, minor, patch) VALUES ( ?1, ?2 , ?3 );");
|
||||
createQuery.bindValue(1, MIRALL_VERSION_MAJOR);
|
||||
createQuery.bindValue(2, MIRALL_VERSION_MINOR);
|
||||
createQuery.bindValue(3, MIRALL_VERSION_PATCH);
|
||||
if (!createQuery.exec()) {
|
||||
return sqlFail("Insert Version", createQuery);
|
||||
}
|
||||
|
@ -240,54 +223,54 @@ bool SyncJournalDb::checkConnect()
|
|||
qDebug() << "WARN: Failed to update the database structure!";
|
||||
}
|
||||
|
||||
_getFileRecordQuery.reset(new QSqlQuery(_db));
|
||||
_getFileRecordQuery.reset(new SqlQuery(_db));
|
||||
_getFileRecordQuery->prepare("SELECT path, inode, uid, gid, mode, modtime, type, md5, fileid, remotePerm FROM "
|
||||
"metadata WHERE phash=:ph" );
|
||||
"metadata WHERE phash=?1" );
|
||||
|
||||
_setFileRecordQuery.reset(new QSqlQuery(_db) );
|
||||
_setFileRecordQuery.reset(new SqlQuery(_db) );
|
||||
_setFileRecordQuery->prepare("INSERT OR REPLACE INTO metadata "
|
||||
"(phash, pathlen, path, inode, uid, gid, mode, modtime, type, md5, fileid, remotePerm) "
|
||||
"VALUES ( ? , ?, ? , ? , ? , ? , ?, ? , ? , ?, ?, ? )" );
|
||||
"VALUES (?1 , ?2, ?3 , ?4 , ?5 , ?6 , ?7, ?8 , ?9 , ?10, ?11, ?12);" );
|
||||
|
||||
_getDownloadInfoQuery.reset(new QSqlQuery(_db) );
|
||||
_getDownloadInfoQuery.reset(new SqlQuery(_db) );
|
||||
_getDownloadInfoQuery->prepare( "SELECT tmpfile, etag, errorcount FROM "
|
||||
"downloadinfo WHERE path=:pa" );
|
||||
"downloadinfo WHERE path=?1" );
|
||||
|
||||
_setDownloadInfoQuery.reset(new QSqlQuery(_db) );
|
||||
_setDownloadInfoQuery.reset(new SqlQuery(_db) );
|
||||
_setDownloadInfoQuery->prepare( "INSERT OR REPLACE INTO downloadinfo "
|
||||
"(path, tmpfile, etag, errorcount) "
|
||||
"VALUES ( ? , ?, ? , ? )" );
|
||||
"VALUES ( ?1 , ?2, ?3, ?4 )" );
|
||||
|
||||
_deleteDownloadInfoQuery.reset(new QSqlQuery(_db) );
|
||||
_deleteDownloadInfoQuery->prepare( "DELETE FROM downloadinfo WHERE path=?" );
|
||||
_deleteDownloadInfoQuery.reset(new SqlQuery(_db) );
|
||||
_deleteDownloadInfoQuery->prepare( "DELETE FROM downloadinfo WHERE path=?1" );
|
||||
|
||||
_getUploadInfoQuery.reset(new QSqlQuery(_db));
|
||||
_getUploadInfoQuery.reset(new SqlQuery(_db));
|
||||
_getUploadInfoQuery->prepare( "SELECT chunk, transferid, errorcount, size, modtime FROM "
|
||||
"uploadinfo WHERE path=:pa" );
|
||||
"uploadinfo WHERE path=?1" );
|
||||
|
||||
_setUploadInfoQuery.reset(new QSqlQuery(_db));
|
||||
_setUploadInfoQuery.reset(new SqlQuery(_db));
|
||||
_setUploadInfoQuery->prepare( "INSERT OR REPLACE INTO uploadinfo "
|
||||
"(path, chunk, transferid, errorcount, size, modtime) "
|
||||
"VALUES ( ? , ?, ? , ? , ? , ? )");
|
||||
"VALUES ( ?1 , ?2, ?3 , ?4 , ?5, ?6 )");
|
||||
|
||||
_deleteUploadInfoQuery.reset(new QSqlQuery(_db));
|
||||
_deleteUploadInfoQuery->prepare("DELETE FROM uploadinfo WHERE path=?" );
|
||||
_deleteUploadInfoQuery.reset(new SqlQuery(_db));
|
||||
_deleteUploadInfoQuery->prepare("DELETE FROM uploadinfo WHERE path=?1" );
|
||||
|
||||
|
||||
_deleteFileRecordPhash.reset(new QSqlQuery(_db));
|
||||
_deleteFileRecordPhash->prepare("DELETE FROM metadata WHERE phash=?");
|
||||
_deleteFileRecordPhash.reset(new SqlQuery(_db));
|
||||
_deleteFileRecordPhash->prepare("DELETE FROM metadata WHERE phash=?1");
|
||||
|
||||
_deleteFileRecordRecursively.reset(new QSqlQuery(_db));
|
||||
_deleteFileRecordRecursively.reset(new SqlQuery(_db));
|
||||
_deleteFileRecordRecursively->prepare("DELETE FROM metadata WHERE path LIKE(?||'/%')");
|
||||
|
||||
QString sql( "SELECT lastTryEtag, lastTryModtime, retrycount, errorstring "
|
||||
"FROM blacklist WHERE path=:path");
|
||||
"FROM blacklist WHERE path=?1");
|
||||
if( Utility::fsCasePreserving() ) {
|
||||
// if the file system is case preserving we have to check the blacklist
|
||||
// case insensitively
|
||||
sql += QLatin1String(" COLLATE NOCASE");
|
||||
}
|
||||
_blacklistQuery.reset(new QSqlQuery(_db));
|
||||
_blacklistQuery.reset(new SqlQuery(_db));
|
||||
_blacklistQuery->prepare(sql);
|
||||
|
||||
return rc;
|
||||
|
@ -313,8 +296,7 @@ void SyncJournalDb::close()
|
|||
_possibleUpgradeFromMirall_1_5 = false;
|
||||
|
||||
_db.close();
|
||||
_db = QSqlDatabase(); // avoid the warning QSqlDatabasePrivate::removeDatabase: connection [...] still in use
|
||||
QSqlDatabase::removeDatabase(_dbFile);
|
||||
_db = SqlDatabase(); // avoid the warning SqlDatabasePrivate::removeDatabase: connection [...] still in use
|
||||
_avoidReadFromDbOnNextSyncFilter.clear();
|
||||
}
|
||||
|
||||
|
@ -330,7 +312,7 @@ bool SyncJournalDb::updateDatabaseStructure()
|
|||
}
|
||||
|
||||
if( columns.indexOf(QLatin1String("fileid")) == -1 ) {
|
||||
QSqlQuery query(_db);
|
||||
SqlQuery query(_db);
|
||||
query.prepare("ALTER TABLE metadata ADD COLUMN fileid VARCHAR(128);");
|
||||
if( !query.exec() ) {
|
||||
sqlFail("updateDatabaseStructure: Add column fileid", query);
|
||||
|
@ -346,7 +328,7 @@ bool SyncJournalDb::updateDatabaseStructure()
|
|||
}
|
||||
if( columns.indexOf(QLatin1String("remotePerm")) == -1 ) {
|
||||
|
||||
QSqlQuery query(_db);
|
||||
SqlQuery query(_db);
|
||||
query.prepare("ALTER TABLE metadata ADD COLUMN remotePerm VARCHAR(128);");
|
||||
if( !query.exec()) {
|
||||
sqlFail("updateDatabaseStructure: add column remotePerm", query);
|
||||
|
@ -356,7 +338,7 @@ bool SyncJournalDb::updateDatabaseStructure()
|
|||
}
|
||||
|
||||
if( 1 ) {
|
||||
QSqlQuery query(_db);
|
||||
SqlQuery query(_db);
|
||||
query.prepare("CREATE INDEX IF NOT EXISTS metadata_inode ON metadata(inode);");
|
||||
if( !query.exec()) {
|
||||
sqlFail("updateDatabaseStructure: create index inode", query);
|
||||
|
@ -367,7 +349,7 @@ bool SyncJournalDb::updateDatabaseStructure()
|
|||
}
|
||||
|
||||
if( 1 ) {
|
||||
QSqlQuery query(_db);
|
||||
SqlQuery query(_db);
|
||||
query.prepare("CREATE INDEX IF NOT EXISTS metadata_pathlen ON metadata(pathlen);");
|
||||
if( !query.exec()) {
|
||||
sqlFail("updateDatabaseStructure: create index pathlen", query);
|
||||
|
@ -385,18 +367,18 @@ QStringList SyncJournalDb::tableColumns( const QString& table )
|
|||
if( !table.isEmpty() ) {
|
||||
|
||||
if( checkConnect() ) {
|
||||
QString q = QString("PRAGMA table_info(%1);").arg(table);
|
||||
QSqlQuery query(_db);
|
||||
QString q = QString("PRAGMA table_info('%1');").arg(table);
|
||||
SqlQuery query(_db);
|
||||
query.prepare(q);
|
||||
|
||||
if(!query.exec()) {
|
||||
QString err = query.lastError().text();
|
||||
QString err = query.error();
|
||||
qDebug() << "Error creating prepared statement: " << query.lastQuery() << ", Error:" << err;;
|
||||
return columns;
|
||||
}
|
||||
|
||||
while( query.next() ) {
|
||||
columns.append( query.value(1).toString() );
|
||||
columns.append( query.stringValue(1) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -405,7 +387,7 @@ QStringList SyncJournalDb::tableColumns( const QString& table )
|
|||
return columns;
|
||||
}
|
||||
|
||||
qint64 SyncJournalDb::getPHash(const QString& file) const
|
||||
qint64 SyncJournalDb::getPHash(const QString& file)
|
||||
{
|
||||
QByteArray utf8File = file.toUtf8();
|
||||
int64_t h;
|
||||
|
@ -448,23 +430,22 @@ bool SyncJournalDb::setFileRecord( const SyncJournalFileRecord& _record )
|
|||
if( fileId.isEmpty() ) fileId = "";
|
||||
QString remotePerm (record._remotePerm);
|
||||
if (remotePerm.isEmpty()) remotePerm = QString(); // have NULL in DB (vs empty)
|
||||
|
||||
_setFileRecordQuery->bindValue(0, QString::number(phash));
|
||||
_setFileRecordQuery->bindValue(1, plen);
|
||||
_setFileRecordQuery->bindValue(2, record._path );
|
||||
_setFileRecordQuery->bindValue(3, record._inode );
|
||||
_setFileRecordQuery->bindValue(4, 0 ); // uid Not used
|
||||
_setFileRecordQuery->bindValue(5, 0 ); // gid Not used
|
||||
_setFileRecordQuery->bindValue(6, record._mode );
|
||||
_setFileRecordQuery->bindValue(7, QString::number(Utility::qDateTimeToTime_t(record._modtime)));
|
||||
_setFileRecordQuery->bindValue(8, QString::number(record._type) );
|
||||
_setFileRecordQuery->bindValue(9, etag );
|
||||
_setFileRecordQuery->bindValue(10, fileId );
|
||||
_setFileRecordQuery->bindValue(11, remotePerm );
|
||||
_setFileRecordQuery->bindValue(1, QString::number(phash));
|
||||
_setFileRecordQuery->bindValue(2, plen);
|
||||
_setFileRecordQuery->bindValue(3, record._path );
|
||||
_setFileRecordQuery->bindValue(4, record._inode );
|
||||
_setFileRecordQuery->bindValue(5, 0 ); // uid Not used
|
||||
_setFileRecordQuery->bindValue(6, 0 ); // gid Not used
|
||||
_setFileRecordQuery->bindValue(7, record._mode );
|
||||
_setFileRecordQuery->bindValue(8, QString::number(Utility::qDateTimeToTime_t(record._modtime)));
|
||||
_setFileRecordQuery->bindValue(9, QString::number(record._type) );
|
||||
_setFileRecordQuery->bindValue(10, etag );
|
||||
_setFileRecordQuery->bindValue(11, fileId );
|
||||
_setFileRecordQuery->bindValue(12, remotePerm );
|
||||
|
||||
if( !_setFileRecordQuery->exec() ) {
|
||||
qWarning() << "Error SQL statement setFileRecord: " << _setFileRecordQuery->lastQuery() << " :"
|
||||
<< _setFileRecordQuery->lastError().text();
|
||||
<< _setFileRecordQuery->error();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -472,7 +453,7 @@ bool SyncJournalDb::setFileRecord( const SyncJournalFileRecord& _record )
|
|||
<< record._mode
|
||||
<< QString::number(Utility::qDateTimeToTime_t(record._modtime)) << QString::number(record._type)
|
||||
<< record._etag << record._fileId << record._remotePerm;
|
||||
_setFileRecordQuery->finish();
|
||||
_setFileRecordQuery->reset();
|
||||
|
||||
return true;
|
||||
} else {
|
||||
|
@ -490,26 +471,26 @@ bool SyncJournalDb::deleteFileRecord(const QString& filename, bool recursively)
|
|||
// always delete the actual file.
|
||||
|
||||
qlonglong phash = getPHash(filename);
|
||||
_deleteFileRecordPhash->bindValue( 0, QString::number(phash) );
|
||||
_deleteFileRecordPhash->bindValue( 1, QString::number(phash) );
|
||||
|
||||
if( !_deleteFileRecordPhash->exec() ) {
|
||||
qWarning() << "Exec error of SQL statement: "
|
||||
<< _deleteFileRecordPhash->lastQuery()
|
||||
<< " : " << _deleteFileRecordPhash->lastError().text();
|
||||
<< " : " << _deleteFileRecordPhash->error();
|
||||
return false;
|
||||
}
|
||||
qDebug() << _deleteFileRecordPhash->executedQuery() << phash << filename;
|
||||
_deleteFileRecordPhash->finish();
|
||||
qDebug() << _deleteFileRecordPhash->lastQuery() << phash << filename;
|
||||
_deleteFileRecordPhash->reset();
|
||||
if( recursively) {
|
||||
_deleteFileRecordRecursively->bindValue(0, filename);
|
||||
_deleteFileRecordRecursively->bindValue(1, filename);
|
||||
if( !_deleteFileRecordRecursively->exec() ) {
|
||||
qWarning() << "Exec error of SQL statement: "
|
||||
<< _deleteFileRecordRecursively->lastQuery()
|
||||
<< " : " << _deleteFileRecordRecursively->lastError().text();
|
||||
<< " : " << _deleteFileRecordRecursively->error();
|
||||
return false;
|
||||
}
|
||||
qDebug() << _deleteFileRecordRecursively->executedQuery() << filename;
|
||||
_deleteFileRecordRecursively->finish();
|
||||
qDebug() << _deleteFileRecordRecursively->lastQuery() << filename;
|
||||
_deleteFileRecordRecursively->reset();
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
|
@ -527,30 +508,29 @@ SyncJournalFileRecord SyncJournalDb::getFileRecord( const QString& filename )
|
|||
SyncJournalFileRecord rec;
|
||||
|
||||
if( checkConnect() ) {
|
||||
_getFileRecordQuery->bindValue(":ph", QString::number(phash));
|
||||
_getFileRecordQuery->bindValue(1, QString::number(phash));
|
||||
|
||||
if (!_getFileRecordQuery->exec()) {
|
||||
QString err = _getFileRecordQuery->lastError().text();
|
||||
QString err = _getFileRecordQuery->error();
|
||||
qDebug() << "Error creating prepared statement: " << _getFileRecordQuery->lastQuery() << ", Error:" << err;;
|
||||
return rec;
|
||||
}
|
||||
|
||||
if( _getFileRecordQuery->next() ) {
|
||||
bool ok;
|
||||
rec._path = _getFileRecordQuery->value(0).toString();
|
||||
rec._inode = _getFileRecordQuery->value(1).toInt(&ok);
|
||||
rec._path = _getFileRecordQuery->stringValue(0);
|
||||
rec._inode = _getFileRecordQuery->intValue(1);
|
||||
//rec._uid = _getFileRecordQuery->value(2).toInt(&ok); Not Used
|
||||
//rec._gid = _getFileRecordQuery->value(3).toInt(&ok); Not Used
|
||||
rec._mode = _getFileRecordQuery->value(4).toInt(&ok);
|
||||
rec._modtime = Utility::qDateTimeFromTime_t(_getFileRecordQuery->value(5).toLongLong(&ok));
|
||||
rec._type = _getFileRecordQuery->value(6).toInt(&ok);
|
||||
rec._etag = _getFileRecordQuery->value(7).toByteArray();
|
||||
rec._fileId = _getFileRecordQuery->value(8).toByteArray();
|
||||
rec._remotePerm = _getFileRecordQuery->value(9).toByteArray();
|
||||
rec._mode = _getFileRecordQuery->intValue(4);
|
||||
rec._modtime = Utility::qDateTimeFromTime_t(_getFileRecordQuery->int64Value(5));
|
||||
rec._type = _getFileRecordQuery->intValue(6);
|
||||
rec._etag = _getFileRecordQuery->baValue(7);
|
||||
rec._fileId = _getFileRecordQuery->baValue(8);
|
||||
rec._remotePerm = _getFileRecordQuery->baValue(9);
|
||||
|
||||
_getFileRecordQuery->finish();
|
||||
_getFileRecordQuery->reset();
|
||||
} else {
|
||||
QString err = _getFileRecordQuery->lastError().text();
|
||||
QString err = _getFileRecordQuery->error();
|
||||
qDebug() << "No journal entry found for " << filename;
|
||||
}
|
||||
}
|
||||
|
@ -565,11 +545,11 @@ bool SyncJournalDb::postSyncCleanup(const QSet<QString> &items )
|
|||
return false;
|
||||
}
|
||||
|
||||
QSqlQuery query(_db);
|
||||
SqlQuery query(_db);
|
||||
query.prepare("SELECT phash, path FROM metadata order by path");
|
||||
|
||||
if (!query.exec()) {
|
||||
QString err = query.lastError().text();
|
||||
QString err = query.error();
|
||||
qDebug() << "Error creating prepared statement: " << query.lastQuery() << ", Error:" << err;;
|
||||
return false;
|
||||
}
|
||||
|
@ -577,20 +557,20 @@ bool SyncJournalDb::postSyncCleanup(const QSet<QString> &items )
|
|||
QStringList superfluousItems;
|
||||
|
||||
while(query.next()) {
|
||||
const QString file = query.value(1).toString();
|
||||
const QString file = query.stringValue(1);
|
||||
bool contained = items.contains(file);
|
||||
if( !contained ) {
|
||||
superfluousItems.append(query.value(0).toString());
|
||||
superfluousItems.append(query.stringValue(0));
|
||||
}
|
||||
}
|
||||
|
||||
if( superfluousItems.count() ) {
|
||||
QString sql = "DELETE FROM metadata WHERE phash in ("+ superfluousItems.join(",")+")";
|
||||
qDebug() << "Sync Journal cleanup: " << sql;
|
||||
QSqlQuery delQuery(_db);
|
||||
SqlQuery delQuery(_db);
|
||||
delQuery.prepare(sql);
|
||||
if( !delQuery.exec() ) {
|
||||
QString err = delQuery.lastError().text();
|
||||
QString err = delQuery.error();
|
||||
qDebug() << "Error removing superfluous journal entries: " << delQuery.lastQuery() << ", Error:" << err;;
|
||||
return false;
|
||||
}
|
||||
|
@ -606,46 +586,49 @@ int SyncJournalDb::getFileRecordCount()
|
|||
return -1;
|
||||
}
|
||||
|
||||
QSqlQuery query(_db);
|
||||
SqlQuery query(_db);
|
||||
query.prepare("SELECT COUNT(*) FROM metadata");
|
||||
|
||||
if (!query.exec()) {
|
||||
QString err = query.lastError().text();
|
||||
QString err = query.error();
|
||||
qDebug() << "Error creating prepared statement: " << query.lastQuery() << ", Error:" << err;;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (query.next()) {
|
||||
int count = query.value(0).toInt();
|
||||
int count = query.intValue(0);
|
||||
return count;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void toDownloadInfo(const QSqlQuery & query, SyncJournalDb::DownloadInfo * res)
|
||||
static void toDownloadInfo(SqlQuery &query, SyncJournalDb::DownloadInfo * res)
|
||||
{
|
||||
bool ok = true;
|
||||
res->_tmpfile = query.value(0).toString();
|
||||
res->_etag = query.value(1).toByteArray();
|
||||
res->_errorCount = query.value(2).toInt(&ok);
|
||||
res->_tmpfile = query.stringValue(0);
|
||||
res->_etag = query.baValue(1);
|
||||
res->_errorCount = query.intValue(2);
|
||||
res->_valid = ok;
|
||||
}
|
||||
|
||||
static bool deleteBatch(QSqlQuery & query, const QStringList & entries, const QString & name)
|
||||
static bool deleteBatch(SqlQuery & query, const QStringList & entries, const QString & name)
|
||||
{
|
||||
if (entries.isEmpty())
|
||||
return true;
|
||||
|
||||
qDebug() << "Removing stale " << qPrintable(name) << " entries: " << entries.join(", ");
|
||||
query.bindValue(0, entries);
|
||||
if (!query.execBatch()) {
|
||||
QString err = query.lastError().text();
|
||||
// FIXME: Was ported from execBatch, check if correct!
|
||||
foreach( const QString& entry, entries ) {
|
||||
query.bindValue(1, entry);
|
||||
if (!query.exec()) {
|
||||
QString err = query.error();
|
||||
qDebug() << "Error removing stale " << qPrintable(name) << " entries: "
|
||||
<< query.lastQuery() << ", Error:" << err;
|
||||
return false;
|
||||
}
|
||||
query.finish();
|
||||
}
|
||||
query.reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -656,18 +639,20 @@ SyncJournalDb::DownloadInfo SyncJournalDb::getDownloadInfo(const QString& file)
|
|||
DownloadInfo res;
|
||||
|
||||
if( checkConnect() ) {
|
||||
_getDownloadInfoQuery->bindValue(":pa", file);
|
||||
_getDownloadInfoQuery->bindValue(1, file);
|
||||
|
||||
if (!_getDownloadInfoQuery->exec()) {
|
||||
QString err = _getDownloadInfoQuery->lastError().text();
|
||||
QString err = _getDownloadInfoQuery->error();
|
||||
qDebug() << "Database error for file " << file << " : " << _getDownloadInfoQuery->lastQuery() << ", Error:" << err;;
|
||||
return res;
|
||||
}
|
||||
|
||||
if( _getDownloadInfoQuery->next() ) {
|
||||
toDownloadInfo(*_getDownloadInfoQuery, &res);
|
||||
} else {
|
||||
res._valid = false;
|
||||
}
|
||||
_getDownloadInfoQuery->finish();
|
||||
_getDownloadInfoQuery->reset();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
@ -681,28 +666,28 @@ void SyncJournalDb::setDownloadInfo(const QString& file, const SyncJournalDb::Do
|
|||
}
|
||||
|
||||
if (i._valid) {
|
||||
_setDownloadInfoQuery->bindValue(0, file);
|
||||
_setDownloadInfoQuery->bindValue(1, i._tmpfile);
|
||||
_setDownloadInfoQuery->bindValue(2, i._etag );
|
||||
_setDownloadInfoQuery->bindValue(3, i._errorCount );
|
||||
_setDownloadInfoQuery->bindValue(1, file);
|
||||
_setDownloadInfoQuery->bindValue(2, i._tmpfile);
|
||||
_setDownloadInfoQuery->bindValue(3, i._etag );
|
||||
_setDownloadInfoQuery->bindValue(4, i._errorCount );
|
||||
|
||||
if( !_setDownloadInfoQuery->exec() ) {
|
||||
qWarning() << "Exec error of SQL statement: " << _setDownloadInfoQuery->lastQuery() << " :" << _setDownloadInfoQuery->lastError().text();
|
||||
qWarning() << "Exec error of SQL statement: " << _setDownloadInfoQuery->lastQuery() << " :" << _setDownloadInfoQuery->error();
|
||||
return;
|
||||
}
|
||||
|
||||
qDebug() << _setDownloadInfoQuery->lastQuery() << file << i._tmpfile << i._etag << i._errorCount;
|
||||
_setDownloadInfoQuery->finish();
|
||||
_setDownloadInfoQuery->reset();
|
||||
|
||||
} else {
|
||||
_deleteDownloadInfoQuery->bindValue( 0, file );
|
||||
_deleteDownloadInfoQuery->bindValue( 1, file );
|
||||
|
||||
if( !_deleteDownloadInfoQuery->exec() ) {
|
||||
qWarning() << "Exec error of SQL statement: " << _deleteDownloadInfoQuery->lastQuery() << " : " << _deleteDownloadInfoQuery->lastError().text();
|
||||
qWarning() << "Exec error of SQL statement: " << _deleteDownloadInfoQuery->lastQuery() << " : " << _deleteDownloadInfoQuery->error();
|
||||
return;
|
||||
}
|
||||
qDebug() << _deleteDownloadInfoQuery->executedQuery() << file;
|
||||
_deleteDownloadInfoQuery->finish();
|
||||
qDebug() << _deleteDownloadInfoQuery->lastQuery() << file;
|
||||
_deleteDownloadInfoQuery->reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -715,12 +700,12 @@ QVector<SyncJournalDb::DownloadInfo> SyncJournalDb::getAndDeleteStaleDownloadInf
|
|||
return empty_result;
|
||||
}
|
||||
|
||||
QSqlQuery query(_db);
|
||||
SqlQuery query(_db);
|
||||
// The selected values *must* match the ones expected by toDownloadInfo().
|
||||
query.prepare("SELECT tmpfile, etag, errorcount, path FROM downloadinfo");
|
||||
|
||||
if (!query.exec()) {
|
||||
QString err = query.lastError().text();
|
||||
QString err = query.error();
|
||||
qDebug() << "Error creating prepared statement: " << query.lastQuery() << ", Error:" << err;
|
||||
return empty_result;
|
||||
}
|
||||
|
@ -729,7 +714,7 @@ QVector<SyncJournalDb::DownloadInfo> SyncJournalDb::getAndDeleteStaleDownloadInf
|
|||
QVector<SyncJournalDb::DownloadInfo> deleted_entries;
|
||||
|
||||
while (query.next()) {
|
||||
const QString file = query.value(3).toString(); // path
|
||||
const QString file = query.stringValue(3); // path
|
||||
if (!keep.contains(file)) {
|
||||
superfluousPaths.append(file);
|
||||
DownloadInfo info;
|
||||
|
@ -752,24 +737,24 @@ SyncJournalDb::UploadInfo SyncJournalDb::getUploadInfo(const QString& file)
|
|||
|
||||
if( checkConnect() ) {
|
||||
|
||||
_getUploadInfoQuery->bindValue(":pa", file);
|
||||
_getUploadInfoQuery->bindValue(1, file);
|
||||
|
||||
if (!_getUploadInfoQuery->exec()) {
|
||||
QString err = _getUploadInfoQuery->lastError().text();
|
||||
QString err = _getUploadInfoQuery->error();
|
||||
qDebug() << "Database error for file " << file << " : " << _getUploadInfoQuery->lastQuery() << ", Error:" << err;
|
||||
return res;
|
||||
}
|
||||
|
||||
if( _getUploadInfoQuery->next() ) {
|
||||
bool ok = true;
|
||||
res._chunk = _getUploadInfoQuery->value(0).toInt(&ok);
|
||||
res._transferid = _getUploadInfoQuery->value(1).toInt(&ok);
|
||||
res._errorCount = _getUploadInfoQuery->value(2).toInt(&ok);
|
||||
res._size = _getUploadInfoQuery->value(3).toLongLong(&ok);
|
||||
res._modtime = Utility::qDateTimeFromTime_t(_getUploadInfoQuery->value(4).toLongLong(&ok));
|
||||
res._chunk = _getUploadInfoQuery->intValue(0);
|
||||
res._transferid = _getUploadInfoQuery->intValue(1);
|
||||
res._errorCount = _getUploadInfoQuery->intValue(2);
|
||||
res._size = _getUploadInfoQuery->int64Value(3);
|
||||
res._modtime = Utility::qDateTimeFromTime_t(_getUploadInfoQuery->int64Value(4));
|
||||
res._valid = ok;
|
||||
}
|
||||
_getUploadInfoQuery->finish();
|
||||
_getUploadInfoQuery->reset();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
@ -783,29 +768,29 @@ void SyncJournalDb::setUploadInfo(const QString& file, const SyncJournalDb::Uplo
|
|||
}
|
||||
|
||||
if (i._valid) {
|
||||
_setUploadInfoQuery->bindValue(0, file);
|
||||
_setUploadInfoQuery->bindValue(1, i._chunk);
|
||||
_setUploadInfoQuery->bindValue(2, i._transferid );
|
||||
_setUploadInfoQuery->bindValue(3, i._errorCount );
|
||||
_setUploadInfoQuery->bindValue(4, i._size );
|
||||
_setUploadInfoQuery->bindValue(5, Utility::qDateTimeToTime_t(i._modtime) );
|
||||
_setUploadInfoQuery->bindValue(1, file);
|
||||
_setUploadInfoQuery->bindValue(2, i._chunk);
|
||||
_setUploadInfoQuery->bindValue(3, i._transferid );
|
||||
_setUploadInfoQuery->bindValue(4, i._errorCount );
|
||||
_setUploadInfoQuery->bindValue(5, i._size );
|
||||
_setUploadInfoQuery->bindValue(6, Utility::qDateTimeToTime_t(i._modtime) );
|
||||
|
||||
if( !_setUploadInfoQuery->exec() ) {
|
||||
qWarning() << "Exec error of SQL statement: " << _setUploadInfoQuery->lastQuery() << " :" << _setUploadInfoQuery->lastError().text();
|
||||
qWarning() << "Exec error of SQL statement: " << _setUploadInfoQuery->lastQuery() << " :" << _setUploadInfoQuery->error();
|
||||
return;
|
||||
}
|
||||
|
||||
qDebug() << _setUploadInfoQuery->lastQuery() << file << i._chunk << i._transferid << i._errorCount;
|
||||
_setUploadInfoQuery->finish();
|
||||
_setUploadInfoQuery->reset();
|
||||
} else {
|
||||
_deleteUploadInfoQuery->bindValue(0, file);
|
||||
_deleteUploadInfoQuery->bindValue(1, file);
|
||||
|
||||
if( !_deleteUploadInfoQuery->exec() ) {
|
||||
qWarning() << "Exec error of SQL statement: " << _deleteUploadInfoQuery->lastQuery() << " : " << _deleteUploadInfoQuery->lastError().text();
|
||||
qWarning() << "Exec error of SQL statement: " << _deleteUploadInfoQuery->lastQuery() << " : " << _deleteUploadInfoQuery->error();
|
||||
return;
|
||||
}
|
||||
qDebug() << _deleteUploadInfoQuery->executedQuery() << file;
|
||||
_deleteUploadInfoQuery->finish();
|
||||
qDebug() << _deleteUploadInfoQuery->lastQuery() << file;
|
||||
_deleteUploadInfoQuery->reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -817,11 +802,11 @@ bool SyncJournalDb::deleteStaleUploadInfos(const QSet<QString> &keep)
|
|||
return false;
|
||||
}
|
||||
|
||||
QSqlQuery query(_db);
|
||||
SqlQuery query(_db);
|
||||
query.prepare("SELECT path FROM uploadinfo");
|
||||
|
||||
if (!query.exec()) {
|
||||
QString err = query.lastError().text();
|
||||
QString err = query.error();
|
||||
qDebug() << "Error creating prepared statement: " << query.lastQuery() << ", Error:" << err;
|
||||
return false;
|
||||
}
|
||||
|
@ -829,7 +814,7 @@ bool SyncJournalDb::deleteStaleUploadInfos(const QSet<QString> &keep)
|
|||
QStringList superfluousPaths;
|
||||
|
||||
while (query.next()) {
|
||||
const QString file = query.value(0).toString();
|
||||
const QString file = query.stringValue(0);
|
||||
if (!keep.contains(file)) {
|
||||
superfluousPaths.append(file);
|
||||
}
|
||||
|
@ -848,21 +833,20 @@ SyncJournalBlacklistRecord SyncJournalDb::blacklistEntry( const QString& file )
|
|||
// SELECT lastTryEtag, lastTryModtime, retrycount, errorstring
|
||||
|
||||
if( checkConnect() ) {
|
||||
_blacklistQuery->bindValue( ":path", file );
|
||||
_blacklistQuery->bindValue( 1, file );
|
||||
if( _blacklistQuery->exec() ){
|
||||
if( _blacklistQuery->next() ) {
|
||||
bool ok;
|
||||
entry._lastTryEtag = _blacklistQuery->value(0).toByteArray();
|
||||
entry._lastTryModtime = _blacklistQuery->value(1).toLongLong(&ok);
|
||||
entry._retryCount = _blacklistQuery->value(2).toInt();
|
||||
entry._errorString = _blacklistQuery->value(3).toString();
|
||||
entry._lastTryEtag = _blacklistQuery->baValue(0);
|
||||
entry._lastTryModtime = _blacklistQuery->int64Value(1);
|
||||
entry._retryCount = _blacklistQuery->intValue(2);
|
||||
entry._errorString = _blacklistQuery->stringValue(3);
|
||||
entry._file = file;
|
||||
}
|
||||
} else {
|
||||
qWarning() << "Exec error blacklist: " << _blacklistQuery->lastQuery() << " : "
|
||||
<< _blacklistQuery->lastError().text();
|
||||
<< _blacklistQuery->error();
|
||||
}
|
||||
_blacklistQuery->finish();
|
||||
_blacklistQuery->reset();
|
||||
}
|
||||
|
||||
return entry;
|
||||
|
@ -876,11 +860,11 @@ bool SyncJournalDb::deleteStaleBlacklistEntries(const QSet<QString> &keep)
|
|||
return false;
|
||||
}
|
||||
|
||||
QSqlQuery query(_db);
|
||||
SqlQuery query(_db);
|
||||
query.prepare("SELECT path FROM blacklist");
|
||||
|
||||
if (!query.exec()) {
|
||||
QString err = query.lastError().text();
|
||||
QString err = query.error();
|
||||
qDebug() << "Error creating prepared statement: " << query.lastQuery() << ", Error:" << err;
|
||||
return false;
|
||||
}
|
||||
|
@ -888,13 +872,13 @@ bool SyncJournalDb::deleteStaleBlacklistEntries(const QSet<QString> &keep)
|
|||
QStringList superfluousPaths;
|
||||
|
||||
while (query.next()) {
|
||||
const QString file = query.value(0).toString();
|
||||
const QString file = query.stringValue(0);
|
||||
if (!keep.contains(file)) {
|
||||
superfluousPaths.append(file);
|
||||
}
|
||||
}
|
||||
|
||||
QSqlQuery delQuery(_db);
|
||||
SqlQuery delQuery(_db);
|
||||
delQuery.prepare("DELETE FROM blacklist WHERE path = ?");
|
||||
return deleteBatch(delQuery, superfluousPaths, "blacklist");
|
||||
}
|
||||
|
@ -905,12 +889,13 @@ int SyncJournalDb::blackListEntryCount()
|
|||
|
||||
QMutexLocker locker(&_mutex);
|
||||
if( checkConnect() ) {
|
||||
QSqlQuery query(_db);
|
||||
if( ! query.exec("SELECT count(*) FROM blacklist") ) {
|
||||
SqlQuery query("SELECT count(*) FROM blacklist", _db);
|
||||
|
||||
if( ! query.exec() ) {
|
||||
sqlFail("Count number of blacklist entries failed", query);
|
||||
}
|
||||
if( query.next() ) {
|
||||
re = query.value(0).toInt();
|
||||
re = query.intValue(0);
|
||||
}
|
||||
}
|
||||
return re;
|
||||
|
@ -920,7 +905,7 @@ int SyncJournalDb::wipeBlacklist()
|
|||
{
|
||||
QMutexLocker locker(&_mutex);
|
||||
if( checkConnect() ) {
|
||||
QSqlQuery query(_db);
|
||||
SqlQuery query(_db);
|
||||
|
||||
query.prepare("DELETE FROM blacklist");
|
||||
|
||||
|
@ -941,10 +926,10 @@ void SyncJournalDb::wipeBlacklistEntry( const QString& file )
|
|||
|
||||
QMutexLocker locker(&_mutex);
|
||||
if( checkConnect() ) {
|
||||
QSqlQuery query(_db);
|
||||
SqlQuery query(_db);
|
||||
|
||||
query.prepare("DELETE FROM blacklist WHERE path=:path");
|
||||
query.bindValue(":path", file);
|
||||
query.prepare("DELETE FROM blacklist WHERE path=?1");
|
||||
query.bindValue(1, file);
|
||||
if( ! query.exec() ) {
|
||||
sqlFail("Deletion of blacklist item failed.", query);
|
||||
}
|
||||
|
@ -969,32 +954,32 @@ void SyncJournalDb::updateBlacklistEntry( const SyncJournalBlacklistRecord& item
|
|||
retries = rec._retryCount;
|
||||
}
|
||||
|
||||
QSqlQuery iQuery(_db);
|
||||
SqlQuery iQuery(_db);
|
||||
|
||||
if( haveRecord ) {
|
||||
retries--;
|
||||
if( retries < 0 ) retries = 0;
|
||||
|
||||
iQuery.prepare( "UPDATE blacklist SET lastTryEtag = :etag, lastTryModtime = :modtime, "
|
||||
"retrycount = :retries, errorstring = :errStr WHERE path=:path");
|
||||
iQuery.bindValue(":etag", item._lastTryEtag);
|
||||
iQuery.bindValue(":modtime", QString::number(item._lastTryModtime));
|
||||
iQuery.bindValue(":retries", retries);
|
||||
iQuery.bindValue(":errStr", item._errorString);
|
||||
iQuery.bindValue(":path", item._file);
|
||||
iQuery.prepare( "UPDATE blacklist SET lastTryEtag = ?1, lastTryModtime = ?2, "
|
||||
"retrycount = ?3, errorstring = ?4 WHERE path=?5;");
|
||||
iQuery.bindValue(1, item._lastTryEtag);
|
||||
iQuery.bindValue(2, QString::number(item._lastTryModtime));
|
||||
iQuery.bindValue(3, retries);
|
||||
iQuery.bindValue(4, item._errorString);
|
||||
iQuery.bindValue(5, item._file);
|
||||
} else {
|
||||
// there is no entry yet.
|
||||
iQuery.prepare("INSERT INTO blacklist (path, lastTryEtag, lastTryModtime, retrycount, errorstring) "
|
||||
"VALUES (:path, :lastEtag, :lastMTime, :retrycount, :errorstring);");
|
||||
"VALUES (?1, ?2, ?3, ?4, ?5);");
|
||||
|
||||
iQuery.bindValue(":path", item._file );
|
||||
iQuery.bindValue(":lastEtag", item._lastTryEtag);
|
||||
iQuery.bindValue(":lastMTime", QString::number(item._lastTryModtime));
|
||||
iQuery.bindValue(":retrycount", item._retryCount);
|
||||
iQuery.bindValue(":errorstring", item._errorString);
|
||||
iQuery.bindValue(1, item._file );
|
||||
iQuery.bindValue(2, item._lastTryEtag);
|
||||
iQuery.bindValue(3, QString::number(item._lastTryModtime));
|
||||
iQuery.bindValue(4, item._retryCount);
|
||||
iQuery.bindValue(5, item._errorString);
|
||||
}
|
||||
if( !iQuery.exec() ) {
|
||||
QString bug = iQuery.lastError().text();
|
||||
QString bug = iQuery.error();
|
||||
qDebug() << "SQL exec blacklistitem insert/update failed: "<< bug;
|
||||
}
|
||||
|
||||
|
@ -1008,14 +993,14 @@ void SyncJournalDb::avoidRenamesOnNextSync(const QString& path)
|
|||
return;
|
||||
}
|
||||
|
||||
QSqlQuery query(_db);
|
||||
query.prepare("UPDATE metadata SET fileid = '', inode = '0' WHERE path == ? OR path LIKE(?||'/%')");
|
||||
query.bindValue(0, path);
|
||||
SqlQuery query(_db);
|
||||
query.prepare("UPDATE metadata SET fileid = '', inode = '0' WHERE path == ?1 OR path LIKE(?2||'/%')");
|
||||
query.bindValue(1, path);
|
||||
query.bindValue(2, path);
|
||||
if( !query.exec() ) {
|
||||
qDebug() << Q_FUNC_INFO << "SQL error in avoidRenamesOnNextSync: "<< query.lastError().text();
|
||||
qDebug() << Q_FUNC_INFO << "SQL error in avoidRenamesOnNextSync: "<< query.error();
|
||||
} else {
|
||||
qDebug() << Q_FUNC_INFO << query.executedQuery() << path << "(" << query.numRowsAffected() << " rows)";
|
||||
qDebug() << Q_FUNC_INFO << query.lastQuery() << path << "(" << query.numRowsAffected() << " rows)";
|
||||
}
|
||||
|
||||
// We also need to remove the ETags so the update phase refreshes the directory paths
|
||||
|
@ -1036,14 +1021,14 @@ void SyncJournalDb::avoidReadFromDbOnNextSync(const QString& fileName)
|
|||
return;
|
||||
}
|
||||
|
||||
QSqlQuery query(_db);
|
||||
SqlQuery query(_db);
|
||||
// This query will match entries for whitch the path is a prefix of fileName
|
||||
query.prepare("UPDATE metadata SET md5='_invalid_' WHERE ? LIKE(path||'/%') AND type == 2"); // CSYNC_FTW_TYPE_DIR == 2
|
||||
query.bindValue(0, fileName);
|
||||
query.prepare("UPDATE metadata SET md5='_invalid_' WHERE ?1 LIKE(path||'/%') AND type == 2;"); // CSYNC_FTW_TYPE_DIR == 2
|
||||
query.bindValue(1, fileName);
|
||||
if( !query.exec() ) {
|
||||
qDebug() << Q_FUNC_INFO << "SQL error in avoidRenamesOnNextSync: "<< query.lastError().text();
|
||||
qDebug() << Q_FUNC_INFO << "SQL error in avoidRenamesOnNextSync: "<< query.error();
|
||||
} else {
|
||||
qDebug() << Q_FUNC_INFO << query.executedQuery() << fileName << "(" << query.numRowsAffected() << " rows)";
|
||||
qDebug() << Q_FUNC_INFO << query.lastQuery() << fileName << "(" << query.numRowsAffected() << " rows)";
|
||||
}
|
||||
|
||||
// Prevent future overwrite of the etag for this sync
|
||||
|
@ -1059,7 +1044,7 @@ void SyncJournalDb::commit(const QString& context, bool startTrans)
|
|||
|
||||
void SyncJournalDb::commitInternal(const QString& context, bool startTrans )
|
||||
{
|
||||
qDebug() << "Transaction Start " << context;
|
||||
qDebug() << Q_FUNC_INFO << "Transaction commit " << context << (startTrans ? "and starting new transaction" : "");
|
||||
commitTransaction();
|
||||
|
||||
if( startTrans ) {
|
||||
|
|
|
@ -17,11 +17,10 @@
|
|||
#include <QObject>
|
||||
#include <qmutex.h>
|
||||
#include <QDateTime>
|
||||
#include <QSqlDatabase>
|
||||
#include <QHash>
|
||||
#include <QSqlQuery>
|
||||
|
||||
#include "utility.h"
|
||||
#include "ownsql.h"
|
||||
|
||||
namespace Mirall {
|
||||
class SyncJournalFileRecord;
|
||||
|
@ -44,6 +43,9 @@ public:
|
|||
int getFileRecordCount();
|
||||
bool exists();
|
||||
|
||||
QString databaseFilePath();
|
||||
static qint64 getPHash(const QString& );
|
||||
|
||||
void updateBlacklistEntry( const SyncJournalBlacklistRecord& item );
|
||||
void wipeBlacklistEntry(const QString& file);
|
||||
int wipeBlacklist();
|
||||
|
@ -106,31 +108,30 @@ public:
|
|||
bool isUpdateFrom_1_5();
|
||||
|
||||
private:
|
||||
qint64 getPHash(const QString& ) const;
|
||||
bool updateDatabaseStructure();
|
||||
bool sqlFail(const QString& log, const QSqlQuery &query );
|
||||
bool sqlFail(const QString& log, const SqlQuery &query );
|
||||
void commitInternal(const QString &context, bool startTrans = true);
|
||||
void startTransaction();
|
||||
void commitTransaction();
|
||||
QStringList tableColumns( const QString& table );
|
||||
bool checkConnect();
|
||||
|
||||
QSqlDatabase _db;
|
||||
SqlDatabase _db;
|
||||
QString _dbFile;
|
||||
QMutex _mutex; // Public functions are protected with the mutex.
|
||||
int _transaction;
|
||||
bool _possibleUpgradeFromMirall_1_5;
|
||||
QScopedPointer<QSqlQuery> _getFileRecordQuery;
|
||||
QScopedPointer<QSqlQuery> _setFileRecordQuery;
|
||||
QScopedPointer<QSqlQuery> _getDownloadInfoQuery;
|
||||
QScopedPointer<QSqlQuery> _setDownloadInfoQuery;
|
||||
QScopedPointer<QSqlQuery> _deleteDownloadInfoQuery;
|
||||
QScopedPointer<QSqlQuery> _getUploadInfoQuery;
|
||||
QScopedPointer<QSqlQuery> _setUploadInfoQuery;
|
||||
QScopedPointer<QSqlQuery> _deleteUploadInfoQuery;
|
||||
QScopedPointer<QSqlQuery> _deleteFileRecordPhash;
|
||||
QScopedPointer<QSqlQuery> _deleteFileRecordRecursively;
|
||||
QScopedPointer<QSqlQuery> _blacklistQuery;
|
||||
QScopedPointer<SqlQuery> _getFileRecordQuery;
|
||||
QScopedPointer<SqlQuery> _setFileRecordQuery;
|
||||
QScopedPointer<SqlQuery> _getDownloadInfoQuery;
|
||||
QScopedPointer<SqlQuery> _setDownloadInfoQuery;
|
||||
QScopedPointer<SqlQuery> _deleteDownloadInfoQuery;
|
||||
QScopedPointer<SqlQuery> _getUploadInfoQuery;
|
||||
QScopedPointer<SqlQuery> _setUploadInfoQuery;
|
||||
QScopedPointer<SqlQuery> _deleteUploadInfoQuery;
|
||||
QScopedPointer<SqlQuery> _deleteFileRecordPhash;
|
||||
QScopedPointer<SqlQuery> _deleteFileRecordRecursively;
|
||||
QScopedPointer<SqlQuery> _blacklistQuery;
|
||||
|
||||
/* This is the list of paths we called avoidReadFromDbOnNextSync on.
|
||||
* It means that they should not be written to the DB in any case since doing
|
||||
|
|
|
@ -224,6 +224,18 @@ QString Utility::toCSyncScheme(const QString &urlStr)
|
|||
return url.toString();
|
||||
}
|
||||
|
||||
bool Utility::doesSetContainPrefix(QSet<QString> &l, QString &p) {
|
||||
|
||||
Q_FOREACH (const QString &setPath, l) {
|
||||
//qDebug() << Q_FUNC_INFO << p << setPath << setPath.startsWith(p);
|
||||
if (setPath.startsWith(p)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
//qDebug() << "-> NOOOOO!!!" << p << l.count();
|
||||
return false;
|
||||
}
|
||||
|
||||
QString Utility::escape(const QString &in)
|
||||
{
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
|
||||
|
|
|
@ -42,6 +42,10 @@ namespace Utility
|
|||
OWNCLOUDSYNC_EXPORT QString toCSyncScheme(const QString &urlStr);
|
||||
/** Like QLocale::toString(double, 'f', prec), but drops trailing zeros after the decimal point */
|
||||
|
||||
OWNCLOUDSYNC_EXPORT bool doesSetContainPrefix(QSet<QString> &l, QString &p);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief compactFormatDouble - formats a double value human readable.
|
||||
*
|
||||
|
|
|
@ -27,5 +27,7 @@ endif(UNIX AND NOT APPLE)
|
|||
|
||||
owncloud_add_test(CSyncSqlite "")
|
||||
owncloud_add_test(NetrcParser ../src/owncloudcmd/netrcparser.cpp)
|
||||
owncloud_add_test(OwnSql ../src/mirall/ownsql.cpp)
|
||||
|
||||
|
||||
|
||||
|
|
155
test/testownsql.h
Normal file
155
test/testownsql.h
Normal file
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
* This software is in the public domain, furnished "as is", without technical
|
||||
* support, and with no warranty, express or implied, as to its usefulness for
|
||||
* any purpose.
|
||||
* */
|
||||
|
||||
#ifndef MIRALL_TESTOWNSQL_H
|
||||
#define MIRALL_TESTOWNSQL_H
|
||||
|
||||
#include <QtTest>
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
#include "mirall/ownsql.h"
|
||||
|
||||
using namespace Mirall;
|
||||
|
||||
namespace {
|
||||
|
||||
const char testdbC[] = "/tmp/testdb.sqlite";
|
||||
}
|
||||
|
||||
class TestOwnSql : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void initTestCase() {
|
||||
QFileInfo fi( testdbC );
|
||||
|
||||
if( fi.exists() ) {
|
||||
QFile::remove(testdbC);
|
||||
}
|
||||
fi.refresh();
|
||||
QVERIFY(!fi.exists());
|
||||
}
|
||||
|
||||
void cleanupTestCase() {
|
||||
// QFile::remove(testdbC);
|
||||
}
|
||||
|
||||
void testOpenDb() {
|
||||
QFileInfo fi( testdbC );
|
||||
QVERIFY( !fi.exists() ); // must not exist
|
||||
_db.open(testdbC);
|
||||
fi.refresh();
|
||||
QVERIFY(fi.exists());
|
||||
|
||||
}
|
||||
|
||||
void testCreate() {
|
||||
const char *sql = "CREATE TABLE addresses ( id INTEGER, name VARCHAR(4096), "
|
||||
"address VARCHAR(4096), entered INTEGER(8), PRIMARY KEY(id));";
|
||||
|
||||
SqlQuery q(_db);
|
||||
q.prepare(sql);
|
||||
QVERIFY(q.exec());
|
||||
}
|
||||
|
||||
void testIsSelect() {
|
||||
SqlQuery q(_db);
|
||||
q.prepare("SELECT foo FROM bar;");
|
||||
QVERIFY( q.isSelect() );
|
||||
|
||||
q.prepare("UPDATE bla SET foo = 1;");
|
||||
QVERIFY( !q.isSelect());
|
||||
}
|
||||
|
||||
void testInsert() {
|
||||
const char *sql = "INSERT INTO addresses (id, name, address, entered) VALUES "
|
||||
"(1, 'Gonzo Alberto', 'Moriabata 24, Palermo', 1403100844);";
|
||||
SqlQuery q(_db);
|
||||
q.prepare(sql);
|
||||
QVERIFY(q.exec());
|
||||
}
|
||||
|
||||
void testInsert2() {
|
||||
const char *sql = "INSERT INTO addresses (id, name, address, entered) VALUES "
|
||||
"(?1, ?2, ?3, ?4);";
|
||||
SqlQuery q(_db);
|
||||
q.prepare(sql);
|
||||
q.bindValue(1, 2);
|
||||
q.bindValue(2, "Brucely Lafayette");
|
||||
q.bindValue(3, "Nurderway5, New York");
|
||||
q.bindValue(4, 1403101224);
|
||||
QVERIFY(q.exec());
|
||||
}
|
||||
|
||||
void testSelect() {
|
||||
const char *sql = "SELECT * FROM addresses;";
|
||||
|
||||
SqlQuery q(_db);
|
||||
q.prepare(sql);
|
||||
|
||||
q.exec();
|
||||
while( q.next() ) {
|
||||
qDebug() << "Name: " << q.stringValue(1);
|
||||
qDebug() << "Address: " << q.stringValue(2);
|
||||
}
|
||||
}
|
||||
|
||||
void testSelect2() {
|
||||
const char *sql = "SELECT * FROM addresses WHERE id=?1";
|
||||
SqlQuery q(_db);
|
||||
q.prepare(sql);
|
||||
q.bindValue(1, 2);
|
||||
q.exec();
|
||||
if( q.next() ) {
|
||||
qDebug() << "Name:" << q.stringValue(1);
|
||||
qDebug() << "Address:" << q.stringValue(2);
|
||||
}
|
||||
}
|
||||
|
||||
void testPragma() {
|
||||
const char *sql = "PRAGMA table_info(addresses)";
|
||||
|
||||
SqlQuery q(_db);
|
||||
int rc = q.prepare(sql);
|
||||
qDebug() << "Pragma:" << rc;
|
||||
q.exec();
|
||||
if( q.next() ) {
|
||||
qDebug() << "P:" << q.stringValue(1);
|
||||
}
|
||||
}
|
||||
|
||||
void testUnicode() {
|
||||
const char *sql = "INSERT INTO addresses (id, name, address, entered) VALUES "
|
||||
"(?1, ?2, ?3, ?4);";
|
||||
SqlQuery q(_db);
|
||||
q.prepare(sql);
|
||||
q.bindValue(1, 3);
|
||||
q.bindValue(2, QString::fromUtf8("пятницы"));
|
||||
q.bindValue(3, QString::fromUtf8("проспект"));
|
||||
q.bindValue(4, 1403002224);
|
||||
QVERIFY(q.exec());
|
||||
}
|
||||
|
||||
void testReadUnicode() {
|
||||
const char *sql = "SELECT * FROM addresses WHERE id=3;";
|
||||
SqlQuery q(_db);
|
||||
q.prepare(sql);
|
||||
|
||||
if(q.next()) {
|
||||
QString name = q.stringValue(1);
|
||||
QString address = q.stringValue(2);
|
||||
QVERIFY( name == QString::fromUtf8("пятницы") );
|
||||
QVERIFY( address == QString::fromUtf8("проспект"));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
SqlDatabase _db;
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Reference in a new issue