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:
Daniel Molkentin 2014-10-17 12:11:27 +02:00
commit 52a5729298
31 changed files with 157569 additions and 489 deletions

View file

@ -123,6 +123,12 @@ find_package(Sphinx)
find_package(PdfLatex) 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) configure_file(config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h)

View file

@ -34,7 +34,6 @@ LIBRARY_SEARCH_PATH=['/usr/local/lib', '.']
QT_PLUGINS = [ QT_PLUGINS = [
'accessible/libqtaccessiblewidgets.dylib', 'accessible/libqtaccessiblewidgets.dylib',
'sqldrivers/libqsqlite.dylib',
'platforms/libqcocoa.dylib', 'platforms/libqcocoa.dylib',
'imageformats/libqgif.dylib', 'imageformats/libqgif.dylib',
'imageformats/libqico.dylib', 'imageformats/libqico.dylib',

View file

@ -14,7 +14,8 @@ if (${CMAKE_C_COMPILER_ID} MATCHES "(GNU|Clang)")
# add -Wconversion ? # add -Wconversion ?
# cannot be pedantic with sqlite3 directly linked # 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") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -pedantic -pedantic-errors")
endif() endif()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wshadow -Wmissing-prototypes") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wshadow -Wmissing-prototypes")

View file

@ -50,8 +50,18 @@ if (SQLite3_FIND_VERSION AND _SQLITE3_VERSION)
set(SQLite3_VERSION _SQLITE3_VERSION) set(SQLite3_VERSION _SQLITE3_VERSION)
endif (SQLite3_FIND_VERSION AND _SQLITE3_VERSION) endif (SQLite3_FIND_VERSION AND _SQLITE3_VERSION)
include(FindPackageHandleStandardArgs) if (APPLE OR WINDOWS)
find_package_handle_standard_args(SQLite3 DEFAULT_MSG SQLITE3_LIBRARIES SQLITE3_INCLUDE_DIRS) 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 # show the SQLITE3_INCLUDE_DIRS and SQLITE3_LIBRARIES variables only in the advanced view
mark_as_advanced(SQLITE3_INCLUDE_DIRS SQLITE3_LIBRARIES) mark_as_advanced(SQLITE3_INCLUDE_DIRS SQLITE3_LIBRARIES)

View file

@ -37,7 +37,6 @@
!define SOURCE_PATH "@CMAKE_SOURCE_DIR@" !define SOURCE_PATH "@CMAKE_SOURCE_DIR@"
!define QT_DLL_PATH "${MING_BIN}" !define QT_DLL_PATH "${MING_BIN}"
!define ACCESSIBLE_DLL_PATH "${MING_LIB}/qt5/plugins/accessible" !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 IMAGEFORMATS_DLL_PATH "${MING_LIB}/qt5/plugins/imageformats"
!define PLATFORMS_DLL_PATH "${MING_LIB}/qt5/plugins/platforms" !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}\qjpeg.dll"
File "${IMAGEFORMATS_DLL_PATH}\qico.dll" File "${IMAGEFORMATS_DLL_PATH}\qico.dll"
SetOutPath "$INSTDIR\sqldrivers"
File "${SQLITE_DLL_PATH}\qsqlite.dll"
SetOutPath "$INSTDIR" SetOutPath "$INSTDIR"
;License & release notes. ;License & release notes.
File "@CPACK_RESOURCE_FILE_LICENSE@" File "@CPACK_RESOURCE_FILE_LICENSE@"
@ -421,7 +417,6 @@ Section "${APPLICATION_NAME}" SEC_APPLICATION
File "${QT_DLL_PATH}\Qt5Qml.dll" File "${QT_DLL_PATH}\Qt5Qml.dll"
File "${QT_DLL_PATH}\Qt5Quick.dll" File "${QT_DLL_PATH}\Qt5Quick.dll"
File "${QT_DLL_PATH}\Qt5Sensors.dll" File "${QT_DLL_PATH}\Qt5Sensors.dll"
File "${QT_DLL_PATH}\Qt5Sql.dll"
File "${QT_DLL_PATH}\Qt5WebKit.dll" File "${QT_DLL_PATH}\Qt5WebKit.dll"
File "${QT_DLL_PATH}\Qt5WebKitWidgets.dll" File "${QT_DLL_PATH}\Qt5WebKitWidgets.dll"
File "${QT_DLL_PATH}\Qt5Widgets.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}\libjpeg-8.dll"
File "${MING_BIN}\libpcre16-0.dll" File "${MING_BIN}\libpcre16-0.dll"
File "${MING_BIN}\libproxy.dll" File "${MING_BIN}\libproxy.dll"
File "${MING_BIN}\libsqlite3-0.dll"
File "${MING_BIN}\libcrypto-10.dll" File "${MING_BIN}\libcrypto-10.dll"
File "${MING_BIN}\libssl-10.dll" File "${MING_BIN}\libssl-10.dll"
File "${MING_BIN}\libstdc++-6.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}\libxslt-1.dll"
File "${MING_BIN}\zlib1.dll" File "${MING_BIN}\zlib1.dll"
;QtSql and csync dep
File "${MING_BIN}\libsqlite3-0.dll"
;QtKeyChain stuff ;QtKeyChain stuff
File "${MING_BIN}\libqt5keychain.dll" File "${MING_BIN}\libqt5keychain.dll"

View file

@ -4,13 +4,6 @@ add_subdirectory(std)
add_subdirectory(httpbf) add_subdirectory(httpbf)
# Statically include sqlite # 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 set(CSYNC_PUBLIC_INCLUDE_DIRS
${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}
@ -86,8 +79,8 @@ set(csync_HDRS
) )
# Statically include sqlite # Statically include sqlite
if (CSYNC_STATIC_COMPILE_DIR) if (USE_OUR_OWN_SQLITE3)
list(APPEND csync_SRCS ${CSYNC_STATIC_COMPILE_DIR}/dictionary.c ${CSYNC_STATIC_COMPILE_DIR}/sqlite3.c) list(APPEND csync_SRCS ${SQLITE3_SOURCE})
endif() endif()
include_directories( include_directories(

View file

@ -63,7 +63,7 @@ our %config;
@ISA = qw(Exporter); @ISA = qw(Exporter);
@EXPORT = qw( initTesting createRemoteDir removeRemoteDir createLocalDir cleanup csync @EXPORT = qw( initTesting createRemoteDir removeRemoteDir createLocalDir cleanup csync
assertLocalDirs assertLocalAndRemoteDir glob_put put_to_dir assertLocalDirs assertLocalAndRemoteDir glob_put put_to_dir
putToDirLWP localDir remoteDir localCleanup createLocalFile md5OfFile putToDirLWP localDir remoteDir localCleanup createLocalFile md5OfFile
remoteCleanup server initLocalDir initRemoteDir moveRemoteFile remoteCleanup server initLocalDir initRemoteDir moveRemoteFile
printInfo remoteFileId createShare removeShare assert printInfo remoteFileId createShare removeShare assert
@ -73,12 +73,12 @@ sub server
{ {
return $owncloud; return $owncloud;
} }
sub fromFileName($) sub fromFileName($)
{ {
my ($file) = @_; my ($file) = @_;
if ( $^O eq "darwin" ) { if ( $^O eq "darwin" ) {
my $fromFileName = NFC( Encode::decode('utf-8', $file) ); my $fromFileName = NFC( Encode::decode('utf-8', $file) );
return $fromFileName; return $fromFileName;
} else { } else {
return $file; return $file;
@ -89,7 +89,7 @@ sub fromFileName($)
sub initTesting(;$) sub initTesting(;$)
{ {
my ($prefix) = @_; my ($prefix) = @_;
my $cfgFile = "./t1.cfg"; my $cfgFile = "./t1.cfg";
$cfgFile = "/etc/ownCloud/t1.cfg" if( -r "/etc/ownCloud/t1.cfg" ); $cfgFile = "/etc/ownCloud/t1.cfg" if( -r "/etc/ownCloud/t1.cfg" );
@ -131,15 +131,15 @@ sub initTesting(;$)
-pass=> $passwd ); -pass=> $passwd );
# $d->DebugLevel(3); # $d->DebugLevel(3);
$prefix = "t1" unless( defined $prefix ); $prefix = "t1" unless( defined $prefix );
my $dirId = sprintf("%02d", rand(100)); my $dirId = sprintf("%02d", rand(100));
my $dateTime = strftime('%Y%m%d%H%M%S',localtime); my $dateTime = strftime('%Y%m%d%H%M%S',localtime);
my $dir = sprintf( "%s-%s-%s/", $prefix, $dateTime, $dirId ); my $dir = sprintf( "%s-%s-%s/", $prefix, $dateTime, $dirId );
$localDir = $dir; $localDir = $dir;
$localDir .= "/" unless( $localDir =~ /\/$/ ); $localDir .= "/" unless( $localDir =~ /\/$/ );
$remoteDir = $dir; $remoteDir = $dir;
initRemoteDir(); initRemoteDir();
initLocalDir(); initLocalDir();
printf( "Test directory name is %s\n", $dir ); printf( "Test directory name is %s\n", $dir );
@ -208,7 +208,7 @@ sub removeRemoteDir($;$)
if( $re == 0 ) { if( $re == 0 ) {
print "Failed to remove directory <$url>:" . $d->message() ."\n"; print "Failed to remove directory <$url>:" . $d->message() ."\n";
} }
return $re; return $re;
} }
@ -340,7 +340,7 @@ sub localDir()
return $localDir; return $localDir;
} }
sub remoteDir() sub remoteDir()
{ {
return $remoteDir; return $remoteDir;
} }
@ -385,7 +385,7 @@ sub traverse( $$;$ )
{ {
my ($remote, $acceptConflicts, $aurl) = @_; my ($remote, $acceptConflicts, $aurl) = @_;
$remote .= '/' unless $remote =~ /(^|\/)$/; $remote .= '/' unless $remote =~ /(^|\/)$/;
my $url = testDirUrl() . $remote; my $url = testDirUrl() . $remote;
if( $aurl ) { if( $aurl ) {
$url = $aurl . $remote; $url = $aurl . $remote;
@ -493,13 +493,13 @@ sub glob_put( $$;$ )
print " *** Putting $lfile to $puturl\n"; print " *** Putting $lfile to $puturl\n";
# putToDirLWP( $lfile, $puturl ); # putToDirLWP( $lfile, $puturl );
put_to_dir($lfile, $puturl, $optionsRef); put_to_dir($lfile, $puturl, $optionsRef);
# if( ! $d->put( -local=>$lfile, -url=> $puturl ) ) { # if( ! $d->put( -local=>$lfile, -url=> $puturl ) ) {
#print " ### FAILED to put: ". $d->message . '\n'; #print " ### FAILED to put: ". $d->message . '\n';
# s} # s}
} }
} }
} }
} }
@ -529,7 +529,7 @@ sub put_to_dir( $$;$ )
} }
} }
# The HTTP DAV module often does a PROPFIND before it really PUTs. That # The HTTP DAV module often does a PROPFIND before it really PUTs. That
# is not neccessary if we know that the directory is really there. # is not neccessary if we know that the directory is really there.
# Use this function in this case: # Use this function in this case:
sub putToDirLWP($$) sub putToDirLWP($$)
@ -555,7 +555,7 @@ sub putToDirLWP($$)
Content => $string; Content => $string;
$req->authorization_basic($user, $passwd); $req->authorization_basic($user, $passwd);
my $response = $ua->request($req); my $response = $ua->request($req);
if ($response->is_success()) { if ($response->is_success()) {
# print "OK: ", $response->content; # print "OK: ", $response->content;
} else { } else {
@ -598,15 +598,15 @@ sub getToFileLWP( $$ )
} }
} }
sub createLocalFile( $$ ) sub createLocalFile( $$ )
{ {
my ($fname, $size) = @_; my ($fname, $size) = @_;
$size = 1024 unless( $size ); $size = 1024 unless( $size );
my $md5 = Digest::MD5->new; my $md5 = Digest::MD5->new;
open(FILE, ">", $fname) or die "Can't open $fname for writing ($!)"; open(FILE, ">", $fname) or die "Can't open $fname for writing ($!)";
my $minimum = 32; my $minimum = 32;
my $range = 96; my $range = 96;
@ -624,20 +624,20 @@ sub createLocalFile( $$ )
print FILE $s; print FILE $s;
$md5->add($s); $md5->add($s);
close FILE; close FILE;
return $md5->hexdigest; return $md5->hexdigest;
} }
sub md5OfFile( $ ) sub md5OfFile( $ )
{ {
my ($file) = @_; my ($file) = @_;
open FILE, "$file"; open FILE, "$file";
my $ctx = Digest::MD5->new; my $ctx = Digest::MD5->new;
$ctx->addfile (*FILE); $ctx->addfile (*FILE);
my $hash = $ctx->hexdigest; my $hash = $ctx->hexdigest;
close (FILE); close (FILE);
return $hash; return $hash;
} }
@ -651,27 +651,27 @@ sub moveRemoteFile($$;$)
my $fromUrl = testDirUrl(). $from; my $fromUrl = testDirUrl(). $from;
my $toUrl = testDirUrl() . $to; my $toUrl = testDirUrl() . $to;
if( $no_testdir ) { if( $no_testdir ) {
$fromUrl = $from; $fromUrl = $from;
$toUrl = $to; $toUrl = $to;
} }
$d->move($fromUrl, $toUrl); $d->move($fromUrl, $toUrl);
} }
sub printInfo($) sub printInfo($)
{ {
my ($info) = @_; my ($info) = @_;
my $tt = 6+length( $info ); my $tt = 6+length( $info );
print "#" x $tt; print "#" x $tt;
printf( "\n# %2d. %s", $infoCnt, $info ); printf( "\n# %2d. %s", $infoCnt, $info );
print "\n" unless $info =~ /\n$/; print "\n" unless $info =~ /\n$/;
print "#" x $tt; print "#" x $tt;
print "\n"; print "\n";
$infoCnt++; $infoCnt++;
} }
@ -722,16 +722,15 @@ sub createShare($$)
my $re = $dd->mkcol( $url ); my $re = $dd->mkcol( $url );
if( $re == 0 ) { if( $re == 0 ) {
print "Failed to create test dir $url\n"; print "Failed to create test dir $url\n";
} }
my $ua = LWP::UserAgent->new(ssl_opts => { verify_hostname => 0 } ); my $ua = LWP::UserAgent->new(ssl_opts => { verify_hostname => 0 } );
$ua->agent( "ownCloudTest_sharing"); $ua->agent( "ownCloudTest_sharing");
# http://localhost/ocm/ocs/v1.php/apps/files_sharing/api/v1/shares # 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"; 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; my $req = POST $puturl, Content => $string;
$req->authorization_basic($share_user, $share_passwd); $req->authorization_basic($share_user, $share_passwd);
@ -763,15 +762,14 @@ sub removeShare($$)
my $ua = LWP::UserAgent->new(ssl_opts => { verify_hostname => 0 }); my $ua = LWP::UserAgent->new(ssl_opts => { verify_hostname => 0 });
$ua->agent( "ownCloudTest_sharing"); $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; my $req = DELETE $url;
$req->authorization_basic($share_user, $share_passwd); $req->authorization_basic($share_user, $share_passwd);
my $response = $ua->request($req); my $response = $ua->request($req);
if ($response->is_success()) { if ($response->is_success()) {
# print "OK: ", $response->content;
print $response->decoded_content; print $response->decoded_content;
if( $response->decoded_content =~ /<status_code>(\d+)<\/status_code>/m) { if( $response->decoded_content =~ /<status_code>(\d+)<\/status_code>/m) {
my $code = $1; my $code = $1;

View file

@ -33,11 +33,13 @@ print "Hello, this is t6, a tester for csync with ownCloud.\n";
initTesting(); initTesting();
sub createPostUpdateScript() sub createPostUpdateScript($)
{ {
my $srcFile = localDir()."BIG.file"; my ($name) = @_;
my $srcFile = localDir().'BIG1.file';
my $cred = configValue("user") . ":" . configValue("passwd"); 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"; my $script = "/tmp/post_update_script.sh";
open SC, ">$script" || die("Can not create script file"); open SC, ">$script" || die("Can not create script file");
print SC "#!/bin/bash\n"; print SC "#!/bin/bash\n";
@ -48,11 +50,11 @@ sub createPostUpdateScript()
return $script; 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 $!; open(my $fh, '-|', $sql) or die $!;
my $etag = <$fh>; my $etag = <$fh>;
close $fh; close $fh;
@ -61,14 +63,14 @@ sub getETagFromJournal($)
return $etag; return $etag;
} }
sub chunkFileTest( $$ ) sub chunkFileTest( $$ )
{ {
my ($name, $size) = @_; my ($name, $size) = @_;
# Big file chunking # Big file chunking
createLocalFile( localDir().$name, $size ); createLocalFile( localDir().$name, $size );
assert( -e localDir().$name ); assert( -e localDir().$name );
my $bigMd5 = md5OfFile( localDir().$name ); my $bigMd5 = md5OfFile( localDir().$name );
csync(); csync();
@ -89,26 +91,39 @@ sub chunkFileTest( $$ )
} }
printInfo("Big file that needs chunking with default chunk size"); 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"); printInfo("Update the existing file and trigger reupload");
# change the existing file again -> update # change the existing file again -> update
chunkFileTest( "BIG.file", 21762122 ); chunkFileTest( "BIG2.file", 21762122 );
printInfo("Cause a precondition failed error"); printInfo("Cause a precondition failed error");
# Now overwrite the existing file to change it # 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 # and create a post update script
my $script = createPostUpdateScript(); my $script = createPostUpdateScript('BIG3.file');
$ENV{'OWNCLOUD_POST_UPDATE_SCRIPT'} = $script; $ENV{'OWNCLOUD_POST_UPDATE_SCRIPT'} = $script;
# Save the etag before the sync # Save the etag before the sync
my $firstETag = getETagFromJournal('First'); my $firstETag = getETagFromJournal('BIG3.file', 'First');
csync(); # Sync, which ends in a precondition failed error sleep(2);
csync(); # Sync, which ends in a precondition failed error
# get the etag again. It has to be unchanged because of the error. # get the etag again. It has to be unchanged because of the error.
my $secondETag = getETagFromJournal('Second'); my $secondETag = getETagFromJournal('BIG3.file', 'Second');
assert( $firstETag eq $secondETag, "Different ETags, no precondition error." );
# 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); unlink($script);
# Set a custom chunk size in environment. # Set a custom chunk size in environment.

View file

@ -73,13 +73,12 @@ To cross-compile:
``zypper install cmake make mingw32-cross-binutils mingw32-cross-cpp mingw32-cross-gcc \ ``zypper install cmake make mingw32-cross-binutils mingw32-cross-cpp mingw32-cross-gcc \
mingw32-cross-gcc-c++ mingw32-cross-pkg-config mingw32-filesystem \ mingw32-cross-gcc-c++ mingw32-cross-pkg-config mingw32-filesystem \
mingw32-headers mingw32-runtime site-config mingw32-libqt4-sql \ mingw32-headers mingw32-runtime site-config \
mingw32-libqt4-sql-sqlite mingw32-sqlite mingw32-libsqlite-devel \
mingw32-dlfcn-devel mingw32-libssh2-devel kdewin-png2ico \ mingw32-dlfcn-devel mingw32-libssh2-devel kdewin-png2ico \
mingw32-libqt4 mingw32-libqt4-devel mingw32-libgcrypt \ mingw32-libqt4 mingw32-libqt4-devel mingw32-libgcrypt \
mingw32-libgnutls mingw32-libneon-openssl mingw32-libneon-devel \ mingw32-libgnutls mingw32-libneon-openssl mingw32-libneon-devel \
mingw32-libbeecrypt mingw32-libopenssl mingw32-openssl \ 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-qtkeychain-devel mingw32-dlfcn mingw32-libintl-devel \
mingw32-libneon-devel mingw32-libopenssl-devel mingw32-libproxy-devel \ mingw32-libneon-devel mingw32-libopenssl-devel mingw32-libproxy-devel \
mingw32-libxml2-devel mingw32-zlib-devel`` mingw32-libxml2-devel mingw32-zlib-devel``

View file

@ -34,6 +34,7 @@ class syncStateExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.Info
GObject.timeout_add(5000, self.connectToSocketServer) GObject.timeout_add(5000, self.connectToSocketServer)
def connectToSocketServer(self): def connectToSocketServer(self):
do_reconnect = True
try: try:
self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
postfix = "/"+self.appname+"/socket" postfix = "/"+self.appname+"/socket"
@ -44,15 +45,18 @@ class syncStateExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.Info
print("Socket File: "+sock_file) print("Socket File: "+sock_file)
self.sock.connect(sock_file) self.sock.connect(sock_file)
self.connected = True 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) self.watch_id = GObject.io_add_watch(self.sock, GObject.IO_IN, self.handle_notify)
except: do_reconnect = False
print("Could not connect to unix socket.") except Exception, e:
print("Could not connect to unix socket." + str(e))
else: else:
print("Sock-File not valid: "+sock_file) print("Sock-File not valid: "+sock_file)
except: except Exception, e:
print("Connect could not be established, try again later!") print("Connect could not be established, try again later " + str(e))
self.sock.close() self.sock.close()
return not self.connected # print("Returning %r" % do_reconnect)
return do_reconnect
def sendCommand(self, cmd): def sendCommand(self, cmd):
if self.connected: if self.connected:
@ -122,7 +126,7 @@ class syncStateExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.Info
if( not itemStore['state'] or newState != itemStore['state'] ): if( not itemStore['state'] or newState != itemStore['state'] ):
item = itemStore['item'] item = itemStore['item']
item.add_emblem(emblem) 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} self.nautilusVFSFile_table[parts[2]] = {'item': item, 'state':newState}
elif action == 'UPDATE_VIEW': elif action == 'UPDATE_VIEW':

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

File diff suppressed because it is too large Load diff

View file

@ -75,6 +75,7 @@ set(libsync_SRCS
mirall/propagator_legacy.cpp mirall/propagator_legacy.cpp
mirall/syncjournalfilerecord.cpp mirall/syncjournalfilerecord.cpp
mirall/syncjournaldb.cpp mirall/syncjournaldb.cpp
mirall/ownsql.cpp
mirall/theme.cpp mirall/theme.cpp
mirall/owncloudtheme.cpp mirall/owncloudtheme.cpp
mirall/logger.cpp mirall/logger.cpp
@ -173,9 +174,9 @@ GENERATE_EXPORT_HEADER( ${synclib_NAME}
if(TOKEN_AUTH_ONLY) if(TOKEN_AUTH_ONLY)
qt5_use_modules(${synclib_NAME} Network Xml Sql) qt5_use_modules(${synclib_NAME} Network Xml)
else() else()
qt5_use_modules(${synclib_NAME} Widgets Network Xml WebKitWidgets Sql) qt5_use_modules(${synclib_NAME} Widgets Network Xml WebKitWidgets)
endif() endif()
set_target_properties( ${synclib_NAME} PROPERTIES 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} main.cpp ${final_src})
add_executable( ${APPLICATION_EXECUTABLE} WIN32 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) elseif(NOT BUILD_LIBRARIES_ONLY)
set(BIN_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}") set(BIN_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}")
# set(CMAKE_INSTALL_PREFIX ".") # Examples use /Applications. hurmpf. # 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 # we must add MACOSX_BUNDLE only if building a bundle
add_executable( ${APPLICATION_EXECUTABLE} WIN32 MACOSX_BUNDLE main.cpp ${final_src}) 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) set (QM_DIR ${OWNCLOUD_OSX_BUNDLE}/Contents/Resources/Translations)
install(FILES ${mirall_I18N} DESTINATION ${QM_DIR}) install(FILES ${mirall_I18N} DESTINATION ${QM_DIR})
@ -420,7 +421,7 @@ set(OWNCLOUDCMD_SRC owncloudcmd/simplesslerrorhandler.cpp owncloudcmd/owncloudcm
if(NOT BUILD_LIBRARIES_ONLY) if(NOT BUILD_LIBRARIES_ONLY)
add_executable(${owncloudcmd_NAME} ${OWNCLOUDCMD_SRC}) 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 set_target_properties(${owncloudcmd_NAME} PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${BIN_OUTPUT_DIRECTORY} ) RUNTIME_OUTPUT_DIRECTORY ${BIN_OUTPUT_DIRECTORY} )
set_target_properties(${owncloudcmd_NAME} PROPERTIES set_target_properties(${owncloudcmd_NAME} PROPERTIES

View file

@ -82,6 +82,7 @@ int main(int argc, char **argv)
// if the application is already running, notify it. // if the application is already running, notify it.
if( app.isRunning() ) { if( app.isRunning() ) {
qDebug() << Q_FUNC_INFO << "Already running, exiting...";
QStringList args = app.arguments(); QStringList args = app.arguments();
if ( args.size() > 1 && ! app.giveHelp() ) { if ( args.size() > 1 && ! app.giveHelp() ) {
QString msg = args.join( QLatin1String("|") ); QString msg = args.join( QLatin1String("|") );

View file

@ -512,11 +512,90 @@ QString Folder::configFile()
return _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) void Folder::slotThreadTreeWalkResult(const SyncFileItemVector& items)
{ {
addErroredSyncItemPathsToList(items, &this->_stateLastSyncItemsWithError);
_syncResult.setSyncFileItemVector(items); _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() void Folder::slotTerminateSync()
{ {
qDebug() << "folder " << alias() << " Terminating!"; qDebug() << "folder " << alias() << " Terminating!";
@ -639,6 +718,8 @@ void Folder::startSync(const QStringList &pathList)
connect( _engine.data(), SIGNAL(treeWalkResult(const SyncFileItemVector&)), connect( _engine.data(), SIGNAL(treeWalkResult(const SyncFileItemVector&)),
this, SLOT(slotThreadTreeWalkResult(const SyncFileItemVector&)), Qt::QueuedConnection); 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(started()), SLOT(slotSyncStarted()), Qt::QueuedConnection);
connect(_engine.data(), SIGNAL(finished()), SLOT(slotSyncFinished()), Qt::QueuedConnection); connect(_engine.data(), SIGNAL(finished()), SLOT(slotSyncFinished()), Qt::QueuedConnection);
@ -720,6 +801,15 @@ void Folder::slotSyncFinished()
// _watcher->setEventsEnabledDelayed(2000); // _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) { if (_csyncError) {
_syncResult.setStatus(SyncResult::Error); _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 // a job is completed: count the errors and forward to the ProgressDispatcher
void Folder::slotJobCompleted(const SyncFileItem &item) void Folder::slotJobCompleted(const SyncFileItem &item)
{ {
if (item.hasErrorStatus()) {
_stateLastSyncItemsWithError.insert(item._file);
}
if (Progress::isWarningKind(item._status)) { if (Progress::isWarningKind(item._status)) {
// Count all error conditions. // Count all error conditions.
_syncResult.setWarnCount(_syncResult.warnCount()+1); _syncResult.setWarnCount(_syncResult.warnCount()+1);

View file

@ -27,6 +27,7 @@
#include <QDir> #include <QDir>
#include <QHash> #include <QHash>
#include <QSet>
#include <QObject> #include <QObject>
#include <QStringList> #include <QStringList>
@ -34,15 +35,12 @@
#include <QTimer> #include <QTimer>
#include <qelapsedtimer.h> #include <qelapsedtimer.h>
class QFileSystemWatcher;
class QThread; class QThread;
namespace Mirall { namespace Mirall {
class SyncEngine; class SyncEngine;
class FolderWatcher;
class Folder : public QObject class Folder : public QObject
{ {
Q_OBJECT Q_OBJECT
@ -119,12 +117,12 @@ public:
// Used by the Socket API // Used by the Socket API
SyncJournalDb *journalDb() { return &_journal; } SyncJournalDb *journalDb() { return &_journal; }
CSYNC *csyncContext() { return _csync_ctx; }
QStringList selectiveSyncBlackList() { return _selectiveSyncBlackList; } QStringList selectiveSyncBlackList() { return _selectiveSyncBlackList; }
void setSelectiveSyncBlackList(const QStringList &blackList) void setSelectiveSyncBlackList(const QStringList &blackList)
{ _selectiveSyncBlackList = blackList; } { _selectiveSyncBlackList = blackList; }
bool estimateState(QString fn, csync_ftw_type_e t, SyncFileStatus* s);
signals: signals:
void syncStateChange(); void syncStateChange();
@ -170,10 +168,13 @@ private slots:
void etagRetreived(const QString &); void etagRetreived(const QString &);
void slotNetworkUnavailable(); void slotNetworkUnavailable();
void slotThreadTreeWalkResult(const SyncFileItemVector& ); void slotAboutToPropagate(const SyncFileItemVector& );
void slotThreadTreeWalkResult(const SyncFileItemVector& ); // after sync is done
void slotEmitFinishedDelayed(); void slotEmitFinishedDelayed();
void watcherSlot(QString);
private: private:
bool init(); bool init();
@ -204,6 +205,11 @@ private:
QElapsedTimer _timeSinceLastSync; QElapsedTimer _timeSinceLastSync;
bool _forceSyncOnPollTimeout; 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; SyncJournalDb _journal;
ClientProxy _clientProxy; ClientProxy _clientProxy;

View file

@ -147,6 +147,9 @@ void FolderMan::registerFolderMonitor( Folder *folder )
connect(fw, SIGNAL(folderChanged(QString)), _folderWatcherSignalMapper, SLOT(map())); connect(fw, SIGNAL(folderChanged(QString)), _folderWatcherSignalMapper, SLOT(map()));
_folderWatcherSignalMapper->setMapping(fw, folder->alias()); _folderWatcherSignalMapper->setMapping(fw, folder->alias());
_folderWatchers.insert(folder->alias(), fw); _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 // register the folder with the socket API
@ -437,19 +440,27 @@ void FolderMan::slotScheduleAllFolders()
*/ */
void FolderMan::slotScheduleSync( const QString& alias ) 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 // The folder watcher fires a lot of bogus notifications during
// a sync operation, both for actual user files and the database // a sync operation, both for actual user files and the database
// and log. Never enqueue a folder for sync while it is syncing. // 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 // We lose some genuine sync requests that way, but that can't be
// helped. // helped.
// ^^ FIXME: Note that this is not the case on OS X
if( _currentSyncFolder == alias ) { if( _currentSyncFolder == alias ) {
qDebug() << "folder " << alias << " is currently syncing. NOT scheduling."; qDebug() << "folder " << alias << " is currently syncing. NOT scheduling.";
return; return;
} }
if( alias.isEmpty() || ! _folderMap.contains(alias) ) { if( _socketApi ) {
qDebug() << "Not scheduling sync for empty or unknown folder" << alias; // We want the SocketAPI to already now update so that it can show the EVAL icon
return; // 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!"; qDebug() << "Schedule folder " << alias << " to sync!";
@ -578,11 +589,11 @@ Folder *FolderMan::folderForPath(const QString &path)
const QString folderPath = QDir::cleanPath(folder->path())+QLatin1Char('/'); const QString folderPath = QDir::cleanPath(folder->path())+QLatin1Char('/');
if(absolutePath.startsWith(folderPath)) { if(absolutePath.startsWith(folderPath)) {
qDebug() << "found folder: " << folder->path() << " for " << absolutePath; //qDebug() << "found folder: " << folder->path() << " for " << absolutePath;
return folder; return folder;
} }
} }
qDebug() << "ERROR: could not find folder for " << absolutePath;
return 0; return 0;
} }

View file

@ -81,7 +81,7 @@ bool FolderWatcher::pathIsIgnored( const QString& path )
QFileInfo fInfo(path); QFileInfo fInfo(path);
if( fInfo.isHidden() ) { if( fInfo.isHidden() ) {
qDebug() << "* Discarded as is hidden!"; qDebug() << "* Discarded as is hidden!" << fInfo.filePath();
return true; return true;
} }

259
src/mirall/ownsql.cpp Normal file
View 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
View 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

View file

@ -35,6 +35,9 @@
#include <QApplication> #include <QApplication>
#include <QLocalSocket> #include <QLocalSocket>
#include <sqlite3.h>
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
#include <QStandardPaths> #include <QStandardPaths>
#endif #endif
@ -64,159 +67,6 @@ namespace Mirall {
#define DEBUG qDebug() << "SocketApi: " #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) SocketApi::SocketApi(QObject* parent)
: QObject(parent) : QObject(parent)
, _excludes(0) , _excludes(0)
@ -374,6 +224,11 @@ void SocketApi::slotUnregisterPath( const QString& alias )
Folder *f = FolderMan::instance()->folder(alias); Folder *f = FolderMan::instance()->folder(alias);
if (f) { if (f) {
broadcastMessage(QLatin1String("UNREGISTER_PATH"), f->path(), QString::null, true ); 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 { } else {
broadcastMessage(QLatin1String("UPDATE_VIEW"), f->path() ); 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)); 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) { foreach(SocketType *socket, _listeners) {
sendMessage(socket, msg, doWait); 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; DEBUG << "folder offline or not watched:" << argument;
statusString = QLatin1String("NOP"); statusString = QLatin1String("NOP");
} else { } else {
const QString file = argument.mid(syncFolder->path().length()); QString file = argument.mid(syncFolder->path().length());
SyncFileStatus fileStatus = SocketApiHelper::fileStatus(syncFolder, file, _excludes); if( file.isEmpty() ) file = QLatin1String("/");
SyncFileStatus fStatus = this->fileStatus(syncFolder, file, _excludes);
statusString = fileStatus.toSocketAPIString(); statusString = fStatus.toSocketAPIString();
} }
QString message = QLatin1String("STATUS:")+statusString+QLatin1Char(':') 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)); 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 } // namespace Mirall

View file

@ -20,13 +20,15 @@ extern "C" {
#include <std/c_string.h> #include <std/c_string.h>
} }
#include <sqlite3.h>
#include <QWeakPointer> #include <QWeakPointer>
#include <QTcpSocket> #include <QTcpSocket>
#include <QTcpServer> #include <QTcpServer>
#include <QLocalServer> #include <QLocalServer>
#include "mirall/syncfileitem.h" #include "mirall/syncfileitem.h"
#include "mirall/syncjournalfilerecord.h"
class QUrl; class QUrl;
class QLocalSocket; class QLocalSocket;
class QStringList; class QStringList;
@ -35,6 +37,15 @@ namespace Mirall {
typedef QLocalSocket SocketType; typedef QLocalSocket SocketType;
class SyncFileStatus;
class Folder;
struct SqliteHandle
{
sqlite3 *_db;
sqlite3_stmt *_stmt;
};
class SocketApi : public QObject class SocketApi : public QObject
{ {
Q_OBJECT Q_OBJECT
@ -57,6 +68,12 @@ private slots:
void slotSyncItemDiscovered(const QString &, const SyncFileItem &); void slotSyncItemDiscovered(const QString &, const SyncFileItem &);
private: 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 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); 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); Q_INVOKABLE void command_VERSION(const QString& argument, SocketType* socket);
private:
#ifdef SOCKETAPI_TCP #ifdef SOCKETAPI_TCP
QTcpServer _localServer; QTcpServer _localServer;
#else #else
@ -73,6 +89,7 @@ private:
#endif #endif
QList<SocketType*> _listeners; QList<SocketType*> _listeners;
c_strlist_t *_excludes; c_strlist_t *_excludes;
QHash<Folder*, SqliteHandle> _dbConnections;
}; };
} }

View file

@ -22,6 +22,7 @@
#include "discoveryphase.h" #include "discoveryphase.h"
#include "creds/abstractcredentials.h" #include "creds/abstractcredentials.h"
#include "csync_util.h" #include "csync_util.h"
#include "mirall/syncfilestatus.h"
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
#include <windows.h> #include <windows.h>
@ -681,14 +682,16 @@ void SyncEngine::slotDiscoveryJobFinished(int discoveryResult)
Q_ASSERT(session); Q_ASSERT(session);
// post update phase script: allow to tweak stuff by a custom script in debug mode. // post update phase script: allow to tweak stuff by a custom script in debug mode.
#ifndef NDEBUG
if( !qgetenv("OWNCLOUD_POST_UPDATE_SCRIPT").isEmpty() ) { if( !qgetenv("OWNCLOUD_POST_UPDATE_SCRIPT").isEmpty() ) {
#ifndef NDEBUG
QString script = qgetenv("OWNCLOUD_POST_UPDATE_SCRIPT"); QString script = qgetenv("OWNCLOUD_POST_UPDATE_SCRIPT");
qDebug() << "OOO => Post Update Script: " << script; qDebug() << "OOO => Post Update Script: " << script;
QProcess::execute(script.toUtf8()); QProcess::execute(script.toUtf8());
} #else
qDebug() << "**** Attention: POST_UPDATE_SCRIPT installed, but not executed because compiled with NDEBUG";
#endif #endif
}
// do a database commit // do a database commit
_journal->commit("post treewalk"); _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() void SyncEngine::abort()
{ {

View file

@ -33,6 +33,7 @@
#include "mirall/syncfileitem.h" #include "mirall/syncfileitem.h"
#include "mirall/progressdispatcher.h" #include "mirall/progressdispatcher.h"
#include "mirall/utility.h" #include "mirall/utility.h"
#include "mirall/syncfilestatus.h"
class QProcess; class QProcess;
@ -66,6 +67,8 @@ public:
/* Return true if we detected that another sync is needed to complete the sync */ /* Return true if we detected that another sync is needed to complete the sync */
bool isAnotherSyncNeeded() { return _anotherSyncNeeded; } bool isAnotherSyncNeeded() { return _anotherSyncNeeded; }
bool estimateState(QString fn, csync_ftw_type_e t, SyncFileStatus* s);
signals: signals:
void csyncError( const QString& ); void csyncError( const QString& );
void csyncUnavailable(); void csyncUnavailable();
@ -123,7 +126,11 @@ private:
void finalize(); void finalize();
static bool _syncRunning; //true when one sync is running somewhere (for debugging) static bool _syncRunning; //true when one sync is running somewhere (for debugging)
QMap<QString, SyncFileItem> _syncItemMap; 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; SyncFileItemVector _syncedItems;
CSYNC *_csync_ctx; CSYNC *_csync_ctx;

View file

@ -72,6 +72,13 @@ public:
return _file.isEmpty(); return _file.isEmpty();
} }
bool hasErrorStatus() const {
return _status == SyncFileItem::SoftError
|| _status == SyncFileItem::NormalError
|| _status == SyncFileItem::FatalError
|| !_errorString.isEmpty();
}
// Variables usefull for everybody // Variables usefull for everybody
QString _file; QString _file;
QString _renameTarget; QString _renameTarget;

View file

@ -14,8 +14,7 @@
#include <QFile> #include <QFile>
#include <QStringList> #include <QStringList>
#include <QDebug> #include <QDebug>
#include <QSqlError> #include "mirall/ownsql.h"
#include <QSqlQuery>
#include <inttypes.h> #include <inttypes.h>
@ -26,8 +25,6 @@
#include "../../csync/src/std/c_jhash.h" #include "../../csync/src/std/c_jhash.h"
#define QSQLITE "QSQLITE"
namespace Mirall { namespace Mirall {
SyncJournalDb::SyncJournalDb(const QString& path, QObject *parent) : SyncJournalDb::SyncJournalDb(const QString& path, QObject *parent) :
@ -49,15 +46,20 @@ bool SyncJournalDb::exists()
return (!_dbFile.isEmpty() && QFile::exists(_dbFile)); return (!_dbFile.isEmpty() && QFile::exists(_dbFile));
} }
QString SyncJournalDb::databaseFilePath()
{
return _dbFile;
}
void SyncJournalDb::startTransaction() void SyncJournalDb::startTransaction()
{ {
if( _transaction == 0 ) { if( _transaction == 0 ) {
if( !_db.transaction() ) { if( !_db.transaction() ) {
qDebug() << "ERROR starting transaction: " << _db.lastError().text(); qDebug() << "ERROR starting transaction: " << _db.error();
return; return;
} }
_transaction = 1; _transaction = 1;
// qDebug() << "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Transaction start!"; // qDebug() << "XXX Transaction start!";
} else { } else {
qDebug() << "Database Transaction is running, do not starting another one!"; qDebug() << "Database Transaction is running, do not starting another one!";
} }
@ -67,20 +69,20 @@ void SyncJournalDb::commitTransaction()
{ {
if( _transaction == 1 ) { if( _transaction == 1 ) {
if( ! _db.commit() ) { if( ! _db.commit() ) {
qDebug() << "ERROR committing to the database: " << _db.lastError().text(); qDebug() << "ERROR committing to the database: " << _db.error();
return; return;
} }
_transaction = 0; _transaction = 0;
// qDebug() << "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Transaction END!"; // qDebug() << "XXX Transaction END!";
} else { } else {
qDebug() << "No database Transaction to commit"; 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(); commitTransaction();
qWarning() << "SQL Error" << log << query.lastError().text(); qWarning() << "SQL Error" << log << query.error();
Q_ASSERT(!"SQL ERROR"); Q_ASSERT(!"SQL ERROR");
_db.close(); _db.close();
return false; return false;
@ -97,38 +99,19 @@ bool SyncJournalDb::checkConnect()
return false; return false;
} }
QStringList list = QSqlDatabase::drivers(); if( !_db.open(_dbFile) ) {
if( list.size() == 0 ) { QString error = _db.error();
qDebug() << "Database Drivers could not be loaded."; qDebug() << "Error opening the db: " << error;
return false ; return false;
} else {
if( list.indexOf( QSQLITE ) == -1 ) {
qDebug() << "Database Driver QSQLITE could not be loaded!";
return false;
}
} }
// Add the connection SqlQuery pragma1(_db);
_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);
pragma1.prepare("SELECT sqlite_version();"); pragma1.prepare("SELECT sqlite_version();");
if (!pragma1.exec()) { if (!pragma1.exec()) {
return sqlFail("SELECT sqlite_version()", pragma1); return sqlFail("SELECT sqlite_version()", pragma1);
} else { } else {
pragma1.next(); pragma1.next();
qDebug() << "sqlite3 version" << pragma1.value(0).toString(); qDebug() << "sqlite3 version" << pragma1.stringValue(0);
} }
pragma1.prepare("PRAGMA synchronous = 1;"); 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 */ /* Because insert are so slow, e do everything in a transaction, and one need to call commit */
startTransaction(); startTransaction();
QSqlQuery createQuery(_db); SqlQuery createQuery(_db);
createQuery.prepare("CREATE TABLE IF NOT EXISTS metadata(" createQuery.prepare("CREATE TABLE IF NOT EXISTS metadata("
"phash INTEGER(8)," "phash INTEGER(8),"
"pathlen INTEGER," "pathlen INTEGER,"
@ -214,7 +197,7 @@ bool SyncJournalDb::checkConnect()
return sqlFail("Create table version", createQuery); 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 (!versionQuery.next()) {
// If there was no entry in the table, it means we are likely upgrading from 1.5 // If there was no entry in the table, it means we are likely upgrading from 1.5
_possibleUpgradeFromMirall_1_5 = true; _possibleUpgradeFromMirall_1_5 = true;
@ -225,10 +208,10 @@ bool SyncJournalDb::checkConnect()
return sqlFail("Remove version", createQuery); return sqlFail("Remove version", createQuery);
} }
} }
createQuery.prepare("INSERT INTO version (major, minor, patch) VALUES ( ? , ? , ? );"); createQuery.prepare("INSERT INTO version (major, minor, patch) VALUES ( ?1, ?2 , ?3 );");
createQuery.bindValue(0, MIRALL_VERSION_MAJOR); createQuery.bindValue(1, MIRALL_VERSION_MAJOR);
createQuery.bindValue(1, MIRALL_VERSION_MINOR); createQuery.bindValue(2, MIRALL_VERSION_MINOR);
createQuery.bindValue(2, MIRALL_VERSION_PATCH); createQuery.bindValue(3, MIRALL_VERSION_PATCH);
if (!createQuery.exec()) { if (!createQuery.exec()) {
return sqlFail("Insert Version", createQuery); return sqlFail("Insert Version", createQuery);
} }
@ -240,54 +223,54 @@ bool SyncJournalDb::checkConnect()
qDebug() << "WARN: Failed to update the database structure!"; 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 " _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 " _setFileRecordQuery->prepare("INSERT OR REPLACE INTO metadata "
"(phash, pathlen, path, inode, uid, gid, mode, modtime, type, md5, fileid, remotePerm) " "(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 " _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 " _setDownloadInfoQuery->prepare( "INSERT OR REPLACE INTO downloadinfo "
"(path, tmpfile, etag, errorcount) " "(path, tmpfile, etag, errorcount) "
"VALUES ( ? , ?, ? , ? )" ); "VALUES ( ?1 , ?2, ?3, ?4 )" );
_deleteDownloadInfoQuery.reset(new QSqlQuery(_db) ); _deleteDownloadInfoQuery.reset(new SqlQuery(_db) );
_deleteDownloadInfoQuery->prepare( "DELETE FROM downloadinfo WHERE path=?" ); _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 " _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 " _setUploadInfoQuery->prepare( "INSERT OR REPLACE INTO uploadinfo "
"(path, chunk, transferid, errorcount, size, modtime) " "(path, chunk, transferid, errorcount, size, modtime) "
"VALUES ( ? , ?, ? , ? , ? , ? )"); "VALUES ( ?1 , ?2, ?3 , ?4 , ?5, ?6 )");
_deleteUploadInfoQuery.reset(new QSqlQuery(_db)); _deleteUploadInfoQuery.reset(new SqlQuery(_db));
_deleteUploadInfoQuery->prepare("DELETE FROM uploadinfo WHERE path=?" ); _deleteUploadInfoQuery->prepare("DELETE FROM uploadinfo WHERE path=?1" );
_deleteFileRecordPhash.reset(new QSqlQuery(_db)); _deleteFileRecordPhash.reset(new SqlQuery(_db));
_deleteFileRecordPhash->prepare("DELETE FROM metadata WHERE phash=?"); _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(?||'/%')"); _deleteFileRecordRecursively->prepare("DELETE FROM metadata WHERE path LIKE(?||'/%')");
QString sql( "SELECT lastTryEtag, lastTryModtime, retrycount, errorstring " QString sql( "SELECT lastTryEtag, lastTryModtime, retrycount, errorstring "
"FROM blacklist WHERE path=:path"); "FROM blacklist WHERE path=?1");
if( Utility::fsCasePreserving() ) { if( Utility::fsCasePreserving() ) {
// if the file system is case preserving we have to check the blacklist // if the file system is case preserving we have to check the blacklist
// case insensitively // case insensitively
sql += QLatin1String(" COLLATE NOCASE"); sql += QLatin1String(" COLLATE NOCASE");
} }
_blacklistQuery.reset(new QSqlQuery(_db)); _blacklistQuery.reset(new SqlQuery(_db));
_blacklistQuery->prepare(sql); _blacklistQuery->prepare(sql);
return rc; return rc;
@ -313,8 +296,7 @@ void SyncJournalDb::close()
_possibleUpgradeFromMirall_1_5 = false; _possibleUpgradeFromMirall_1_5 = false;
_db.close(); _db.close();
_db = QSqlDatabase(); // avoid the warning QSqlDatabasePrivate::removeDatabase: connection [...] still in use _db = SqlDatabase(); // avoid the warning SqlDatabasePrivate::removeDatabase: connection [...] still in use
QSqlDatabase::removeDatabase(_dbFile);
_avoidReadFromDbOnNextSyncFilter.clear(); _avoidReadFromDbOnNextSyncFilter.clear();
} }
@ -330,7 +312,7 @@ bool SyncJournalDb::updateDatabaseStructure()
} }
if( columns.indexOf(QLatin1String("fileid")) == -1 ) { if( columns.indexOf(QLatin1String("fileid")) == -1 ) {
QSqlQuery query(_db); SqlQuery query(_db);
query.prepare("ALTER TABLE metadata ADD COLUMN fileid VARCHAR(128);"); query.prepare("ALTER TABLE metadata ADD COLUMN fileid VARCHAR(128);");
if( !query.exec() ) { if( !query.exec() ) {
sqlFail("updateDatabaseStructure: Add column fileid", query); sqlFail("updateDatabaseStructure: Add column fileid", query);
@ -346,7 +328,7 @@ bool SyncJournalDb::updateDatabaseStructure()
} }
if( columns.indexOf(QLatin1String("remotePerm")) == -1 ) { if( columns.indexOf(QLatin1String("remotePerm")) == -1 ) {
QSqlQuery query(_db); SqlQuery query(_db);
query.prepare("ALTER TABLE metadata ADD COLUMN remotePerm VARCHAR(128);"); query.prepare("ALTER TABLE metadata ADD COLUMN remotePerm VARCHAR(128);");
if( !query.exec()) { if( !query.exec()) {
sqlFail("updateDatabaseStructure: add column remotePerm", query); sqlFail("updateDatabaseStructure: add column remotePerm", query);
@ -356,7 +338,7 @@ bool SyncJournalDb::updateDatabaseStructure()
} }
if( 1 ) { if( 1 ) {
QSqlQuery query(_db); SqlQuery query(_db);
query.prepare("CREATE INDEX IF NOT EXISTS metadata_inode ON metadata(inode);"); query.prepare("CREATE INDEX IF NOT EXISTS metadata_inode ON metadata(inode);");
if( !query.exec()) { if( !query.exec()) {
sqlFail("updateDatabaseStructure: create index inode", query); sqlFail("updateDatabaseStructure: create index inode", query);
@ -367,7 +349,7 @@ bool SyncJournalDb::updateDatabaseStructure()
} }
if( 1 ) { if( 1 ) {
QSqlQuery query(_db); SqlQuery query(_db);
query.prepare("CREATE INDEX IF NOT EXISTS metadata_pathlen ON metadata(pathlen);"); query.prepare("CREATE INDEX IF NOT EXISTS metadata_pathlen ON metadata(pathlen);");
if( !query.exec()) { if( !query.exec()) {
sqlFail("updateDatabaseStructure: create index pathlen", query); sqlFail("updateDatabaseStructure: create index pathlen", query);
@ -385,18 +367,18 @@ QStringList SyncJournalDb::tableColumns( const QString& table )
if( !table.isEmpty() ) { if( !table.isEmpty() ) {
if( checkConnect() ) { if( checkConnect() ) {
QString q = QString("PRAGMA table_info(%1);").arg(table); QString q = QString("PRAGMA table_info('%1');").arg(table);
QSqlQuery query(_db); SqlQuery query(_db);
query.prepare(q); query.prepare(q);
if(!query.exec()) { if(!query.exec()) {
QString err = query.lastError().text(); QString err = query.error();
qDebug() << "Error creating prepared statement: " << query.lastQuery() << ", Error:" << err;; qDebug() << "Error creating prepared statement: " << query.lastQuery() << ", Error:" << err;;
return columns; return columns;
} }
while( query.next() ) { 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; return columns;
} }
qint64 SyncJournalDb::getPHash(const QString& file) const qint64 SyncJournalDb::getPHash(const QString& file)
{ {
QByteArray utf8File = file.toUtf8(); QByteArray utf8File = file.toUtf8();
int64_t h; int64_t h;
@ -448,23 +430,22 @@ bool SyncJournalDb::setFileRecord( const SyncJournalFileRecord& _record )
if( fileId.isEmpty() ) fileId = ""; if( fileId.isEmpty() ) fileId = "";
QString remotePerm (record._remotePerm); QString remotePerm (record._remotePerm);
if (remotePerm.isEmpty()) remotePerm = QString(); // have NULL in DB (vs empty) if (remotePerm.isEmpty()) remotePerm = QString(); // have NULL in DB (vs empty)
_setFileRecordQuery->bindValue(1, QString::number(phash));
_setFileRecordQuery->bindValue(0, QString::number(phash)); _setFileRecordQuery->bindValue(2, plen);
_setFileRecordQuery->bindValue(1, plen); _setFileRecordQuery->bindValue(3, record._path );
_setFileRecordQuery->bindValue(2, record._path ); _setFileRecordQuery->bindValue(4, record._inode );
_setFileRecordQuery->bindValue(3, record._inode ); _setFileRecordQuery->bindValue(5, 0 ); // uid Not used
_setFileRecordQuery->bindValue(4, 0 ); // uid Not used _setFileRecordQuery->bindValue(6, 0 ); // gid Not used
_setFileRecordQuery->bindValue(5, 0 ); // gid Not used _setFileRecordQuery->bindValue(7, record._mode );
_setFileRecordQuery->bindValue(6, record._mode ); _setFileRecordQuery->bindValue(8, QString::number(Utility::qDateTimeToTime_t(record._modtime)));
_setFileRecordQuery->bindValue(7, QString::number(Utility::qDateTimeToTime_t(record._modtime))); _setFileRecordQuery->bindValue(9, QString::number(record._type) );
_setFileRecordQuery->bindValue(8, QString::number(record._type) ); _setFileRecordQuery->bindValue(10, etag );
_setFileRecordQuery->bindValue(9, etag ); _setFileRecordQuery->bindValue(11, fileId );
_setFileRecordQuery->bindValue(10, fileId ); _setFileRecordQuery->bindValue(12, remotePerm );
_setFileRecordQuery->bindValue(11, remotePerm );
if( !_setFileRecordQuery->exec() ) { if( !_setFileRecordQuery->exec() ) {
qWarning() << "Error SQL statement setFileRecord: " << _setFileRecordQuery->lastQuery() << " :" qWarning() << "Error SQL statement setFileRecord: " << _setFileRecordQuery->lastQuery() << " :"
<< _setFileRecordQuery->lastError().text(); << _setFileRecordQuery->error();
return false; return false;
} }
@ -472,7 +453,7 @@ bool SyncJournalDb::setFileRecord( const SyncJournalFileRecord& _record )
<< record._mode << record._mode
<< QString::number(Utility::qDateTimeToTime_t(record._modtime)) << QString::number(record._type) << QString::number(Utility::qDateTimeToTime_t(record._modtime)) << QString::number(record._type)
<< record._etag << record._fileId << record._remotePerm; << record._etag << record._fileId << record._remotePerm;
_setFileRecordQuery->finish(); _setFileRecordQuery->reset();
return true; return true;
} else { } else {
@ -490,26 +471,26 @@ bool SyncJournalDb::deleteFileRecord(const QString& filename, bool recursively)
// always delete the actual file. // always delete the actual file.
qlonglong phash = getPHash(filename); qlonglong phash = getPHash(filename);
_deleteFileRecordPhash->bindValue( 0, QString::number(phash) ); _deleteFileRecordPhash->bindValue( 1, QString::number(phash) );
if( !_deleteFileRecordPhash->exec() ) { if( !_deleteFileRecordPhash->exec() ) {
qWarning() << "Exec error of SQL statement: " qWarning() << "Exec error of SQL statement: "
<< _deleteFileRecordPhash->lastQuery() << _deleteFileRecordPhash->lastQuery()
<< " : " << _deleteFileRecordPhash->lastError().text(); << " : " << _deleteFileRecordPhash->error();
return false; return false;
} }
qDebug() << _deleteFileRecordPhash->executedQuery() << phash << filename; qDebug() << _deleteFileRecordPhash->lastQuery() << phash << filename;
_deleteFileRecordPhash->finish(); _deleteFileRecordPhash->reset();
if( recursively) { if( recursively) {
_deleteFileRecordRecursively->bindValue(0, filename); _deleteFileRecordRecursively->bindValue(1, filename);
if( !_deleteFileRecordRecursively->exec() ) { if( !_deleteFileRecordRecursively->exec() ) {
qWarning() << "Exec error of SQL statement: " qWarning() << "Exec error of SQL statement: "
<< _deleteFileRecordRecursively->lastQuery() << _deleteFileRecordRecursively->lastQuery()
<< " : " << _deleteFileRecordRecursively->lastError().text(); << " : " << _deleteFileRecordRecursively->error();
return false; return false;
} }
qDebug() << _deleteFileRecordRecursively->executedQuery() << filename; qDebug() << _deleteFileRecordRecursively->lastQuery() << filename;
_deleteFileRecordRecursively->finish(); _deleteFileRecordRecursively->reset();
} }
return true; return true;
} else { } else {
@ -527,31 +508,30 @@ SyncJournalFileRecord SyncJournalDb::getFileRecord( const QString& filename )
SyncJournalFileRecord rec; SyncJournalFileRecord rec;
if( checkConnect() ) { if( checkConnect() ) {
_getFileRecordQuery->bindValue(":ph", QString::number(phash)); _getFileRecordQuery->bindValue(1, QString::number(phash));
if (!_getFileRecordQuery->exec()) { if (!_getFileRecordQuery->exec()) {
QString err = _getFileRecordQuery->lastError().text(); QString err = _getFileRecordQuery->error();
qDebug() << "Error creating prepared statement: " << _getFileRecordQuery->lastQuery() << ", Error:" << err;; qDebug() << "Error creating prepared statement: " << _getFileRecordQuery->lastQuery() << ", Error:" << err;;
return rec; return rec;
} }
if( _getFileRecordQuery->next() ) { if( _getFileRecordQuery->next() ) {
bool ok; rec._path = _getFileRecordQuery->stringValue(0);
rec._path = _getFileRecordQuery->value(0).toString(); rec._inode = _getFileRecordQuery->intValue(1);
rec._inode = _getFileRecordQuery->value(1).toInt(&ok);
//rec._uid = _getFileRecordQuery->value(2).toInt(&ok); Not Used //rec._uid = _getFileRecordQuery->value(2).toInt(&ok); Not Used
//rec._gid = _getFileRecordQuery->value(3).toInt(&ok); Not Used //rec._gid = _getFileRecordQuery->value(3).toInt(&ok); Not Used
rec._mode = _getFileRecordQuery->value(4).toInt(&ok); rec._mode = _getFileRecordQuery->intValue(4);
rec._modtime = Utility::qDateTimeFromTime_t(_getFileRecordQuery->value(5).toLongLong(&ok)); rec._modtime = Utility::qDateTimeFromTime_t(_getFileRecordQuery->int64Value(5));
rec._type = _getFileRecordQuery->value(6).toInt(&ok); rec._type = _getFileRecordQuery->intValue(6);
rec._etag = _getFileRecordQuery->value(7).toByteArray(); rec._etag = _getFileRecordQuery->baValue(7);
rec._fileId = _getFileRecordQuery->value(8).toByteArray(); rec._fileId = _getFileRecordQuery->baValue(8);
rec._remotePerm = _getFileRecordQuery->value(9).toByteArray(); rec._remotePerm = _getFileRecordQuery->baValue(9);
_getFileRecordQuery->finish(); _getFileRecordQuery->reset();
} else { } else {
QString err = _getFileRecordQuery->lastError().text(); QString err = _getFileRecordQuery->error();
qDebug() << "No journal entry found for " << filename; qDebug() << "No journal entry found for " << filename;
} }
} }
return rec; return rec;
@ -565,11 +545,11 @@ bool SyncJournalDb::postSyncCleanup(const QSet<QString> &items )
return false; return false;
} }
QSqlQuery query(_db); SqlQuery query(_db);
query.prepare("SELECT phash, path FROM metadata order by path"); query.prepare("SELECT phash, path FROM metadata order by path");
if (!query.exec()) { if (!query.exec()) {
QString err = query.lastError().text(); QString err = query.error();
qDebug() << "Error creating prepared statement: " << query.lastQuery() << ", Error:" << err;; qDebug() << "Error creating prepared statement: " << query.lastQuery() << ", Error:" << err;;
return false; return false;
} }
@ -577,20 +557,20 @@ bool SyncJournalDb::postSyncCleanup(const QSet<QString> &items )
QStringList superfluousItems; QStringList superfluousItems;
while(query.next()) { while(query.next()) {
const QString file = query.value(1).toString(); const QString file = query.stringValue(1);
bool contained = items.contains(file); bool contained = items.contains(file);
if( !contained ) { if( !contained ) {
superfluousItems.append(query.value(0).toString()); superfluousItems.append(query.stringValue(0));
} }
} }
if( superfluousItems.count() ) { if( superfluousItems.count() ) {
QString sql = "DELETE FROM metadata WHERE phash in ("+ superfluousItems.join(",")+")"; QString sql = "DELETE FROM metadata WHERE phash in ("+ superfluousItems.join(",")+")";
qDebug() << "Sync Journal cleanup: " << sql; qDebug() << "Sync Journal cleanup: " << sql;
QSqlQuery delQuery(_db); SqlQuery delQuery(_db);
delQuery.prepare(sql); delQuery.prepare(sql);
if( !delQuery.exec() ) { if( !delQuery.exec() ) {
QString err = delQuery.lastError().text(); QString err = delQuery.error();
qDebug() << "Error removing superfluous journal entries: " << delQuery.lastQuery() << ", Error:" << err;; qDebug() << "Error removing superfluous journal entries: " << delQuery.lastQuery() << ", Error:" << err;;
return false; return false;
} }
@ -606,46 +586,49 @@ int SyncJournalDb::getFileRecordCount()
return -1; return -1;
} }
QSqlQuery query(_db); SqlQuery query(_db);
query.prepare("SELECT COUNT(*) FROM metadata"); query.prepare("SELECT COUNT(*) FROM metadata");
if (!query.exec()) { if (!query.exec()) {
QString err = query.lastError().text(); QString err = query.error();
qDebug() << "Error creating prepared statement: " << query.lastQuery() << ", Error:" << err;; qDebug() << "Error creating prepared statement: " << query.lastQuery() << ", Error:" << err;;
return 0; return 0;
} }
if (query.next()) { if (query.next()) {
int count = query.value(0).toInt(); int count = query.intValue(0);
return count; return count;
} }
return 0; return 0;
} }
static void toDownloadInfo(const QSqlQuery & query, SyncJournalDb::DownloadInfo * res) static void toDownloadInfo(SqlQuery &query, SyncJournalDb::DownloadInfo * res)
{ {
bool ok = true; bool ok = true;
res->_tmpfile = query.value(0).toString(); res->_tmpfile = query.stringValue(0);
res->_etag = query.value(1).toByteArray(); res->_etag = query.baValue(1);
res->_errorCount = query.value(2).toInt(&ok); res->_errorCount = query.intValue(2);
res->_valid = ok; 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()) if (entries.isEmpty())
return true; return true;
qDebug() << "Removing stale " << qPrintable(name) << " entries: " << entries.join(", "); qDebug() << "Removing stale " << qPrintable(name) << " entries: " << entries.join(", ");
query.bindValue(0, entries); // FIXME: Was ported from execBatch, check if correct!
if (!query.execBatch()) { foreach( const QString& entry, entries ) {
QString err = query.lastError().text(); query.bindValue(1, entry);
qDebug() << "Error removing stale " << qPrintable(name) << " entries: " if (!query.exec()) {
<< query.lastQuery() << ", Error:" << err; QString err = query.error();
return false; qDebug() << "Error removing stale " << qPrintable(name) << " entries: "
<< query.lastQuery() << ", Error:" << err;
return false;
}
} }
query.finish(); query.reset();
return true; return true;
} }
@ -656,18 +639,20 @@ SyncJournalDb::DownloadInfo SyncJournalDb::getDownloadInfo(const QString& file)
DownloadInfo res; DownloadInfo res;
if( checkConnect() ) { if( checkConnect() ) {
_getDownloadInfoQuery->bindValue(":pa", file); _getDownloadInfoQuery->bindValue(1, file);
if (!_getDownloadInfoQuery->exec()) { if (!_getDownloadInfoQuery->exec()) {
QString err = _getDownloadInfoQuery->lastError().text(); QString err = _getDownloadInfoQuery->error();
qDebug() << "Database error for file " << file << " : " << _getDownloadInfoQuery->lastQuery() << ", Error:" << err;; qDebug() << "Database error for file " << file << " : " << _getDownloadInfoQuery->lastQuery() << ", Error:" << err;;
return res; return res;
} }
if( _getDownloadInfoQuery->next() ) { if( _getDownloadInfoQuery->next() ) {
toDownloadInfo(*_getDownloadInfoQuery, &res); toDownloadInfo(*_getDownloadInfoQuery, &res);
} else {
res._valid = false;
} }
_getDownloadInfoQuery->finish(); _getDownloadInfoQuery->reset();
} }
return res; return res;
} }
@ -681,28 +666,28 @@ void SyncJournalDb::setDownloadInfo(const QString& file, const SyncJournalDb::Do
} }
if (i._valid) { if (i._valid) {
_setDownloadInfoQuery->bindValue(0, file); _setDownloadInfoQuery->bindValue(1, file);
_setDownloadInfoQuery->bindValue(1, i._tmpfile); _setDownloadInfoQuery->bindValue(2, i._tmpfile);
_setDownloadInfoQuery->bindValue(2, i._etag ); _setDownloadInfoQuery->bindValue(3, i._etag );
_setDownloadInfoQuery->bindValue(3, i._errorCount ); _setDownloadInfoQuery->bindValue(4, i._errorCount );
if( !_setDownloadInfoQuery->exec() ) { 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; return;
} }
qDebug() << _setDownloadInfoQuery->lastQuery() << file << i._tmpfile << i._etag << i._errorCount; qDebug() << _setDownloadInfoQuery->lastQuery() << file << i._tmpfile << i._etag << i._errorCount;
_setDownloadInfoQuery->finish(); _setDownloadInfoQuery->reset();
} else { } else {
_deleteDownloadInfoQuery->bindValue( 0, file ); _deleteDownloadInfoQuery->bindValue( 1, file );
if( !_deleteDownloadInfoQuery->exec() ) { 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; return;
} }
qDebug() << _deleteDownloadInfoQuery->executedQuery() << file; qDebug() << _deleteDownloadInfoQuery->lastQuery() << file;
_deleteDownloadInfoQuery->finish(); _deleteDownloadInfoQuery->reset();
} }
} }
@ -715,12 +700,12 @@ QVector<SyncJournalDb::DownloadInfo> SyncJournalDb::getAndDeleteStaleDownloadInf
return empty_result; return empty_result;
} }
QSqlQuery query(_db); SqlQuery query(_db);
// The selected values *must* match the ones expected by toDownloadInfo(). // The selected values *must* match the ones expected by toDownloadInfo().
query.prepare("SELECT tmpfile, etag, errorcount, path FROM downloadinfo"); query.prepare("SELECT tmpfile, etag, errorcount, path FROM downloadinfo");
if (!query.exec()) { if (!query.exec()) {
QString err = query.lastError().text(); QString err = query.error();
qDebug() << "Error creating prepared statement: " << query.lastQuery() << ", Error:" << err; qDebug() << "Error creating prepared statement: " << query.lastQuery() << ", Error:" << err;
return empty_result; return empty_result;
} }
@ -729,7 +714,7 @@ QVector<SyncJournalDb::DownloadInfo> SyncJournalDb::getAndDeleteStaleDownloadInf
QVector<SyncJournalDb::DownloadInfo> deleted_entries; QVector<SyncJournalDb::DownloadInfo> deleted_entries;
while (query.next()) { while (query.next()) {
const QString file = query.value(3).toString(); // path const QString file = query.stringValue(3); // path
if (!keep.contains(file)) { if (!keep.contains(file)) {
superfluousPaths.append(file); superfluousPaths.append(file);
DownloadInfo info; DownloadInfo info;
@ -752,24 +737,24 @@ SyncJournalDb::UploadInfo SyncJournalDb::getUploadInfo(const QString& file)
if( checkConnect() ) { if( checkConnect() ) {
_getUploadInfoQuery->bindValue(":pa", file); _getUploadInfoQuery->bindValue(1, file);
if (!_getUploadInfoQuery->exec()) { if (!_getUploadInfoQuery->exec()) {
QString err = _getUploadInfoQuery->lastError().text(); QString err = _getUploadInfoQuery->error();
qDebug() << "Database error for file " << file << " : " << _getUploadInfoQuery->lastQuery() << ", Error:" << err; qDebug() << "Database error for file " << file << " : " << _getUploadInfoQuery->lastQuery() << ", Error:" << err;
return res; return res;
} }
if( _getUploadInfoQuery->next() ) { if( _getUploadInfoQuery->next() ) {
bool ok = true; bool ok = true;
res._chunk = _getUploadInfoQuery->value(0).toInt(&ok); res._chunk = _getUploadInfoQuery->intValue(0);
res._transferid = _getUploadInfoQuery->value(1).toInt(&ok); res._transferid = _getUploadInfoQuery->intValue(1);
res._errorCount = _getUploadInfoQuery->value(2).toInt(&ok); res._errorCount = _getUploadInfoQuery->intValue(2);
res._size = _getUploadInfoQuery->value(3).toLongLong(&ok); res._size = _getUploadInfoQuery->int64Value(3);
res._modtime = Utility::qDateTimeFromTime_t(_getUploadInfoQuery->value(4).toLongLong(&ok)); res._modtime = Utility::qDateTimeFromTime_t(_getUploadInfoQuery->int64Value(4));
res._valid = ok; res._valid = ok;
} }
_getUploadInfoQuery->finish(); _getUploadInfoQuery->reset();
} }
return res; return res;
} }
@ -783,29 +768,29 @@ void SyncJournalDb::setUploadInfo(const QString& file, const SyncJournalDb::Uplo
} }
if (i._valid) { if (i._valid) {
_setUploadInfoQuery->bindValue(0, file); _setUploadInfoQuery->bindValue(1, file);
_setUploadInfoQuery->bindValue(1, i._chunk); _setUploadInfoQuery->bindValue(2, i._chunk);
_setUploadInfoQuery->bindValue(2, i._transferid ); _setUploadInfoQuery->bindValue(3, i._transferid );
_setUploadInfoQuery->bindValue(3, i._errorCount ); _setUploadInfoQuery->bindValue(4, i._errorCount );
_setUploadInfoQuery->bindValue(4, i._size ); _setUploadInfoQuery->bindValue(5, i._size );
_setUploadInfoQuery->bindValue(5, Utility::qDateTimeToTime_t(i._modtime) ); _setUploadInfoQuery->bindValue(6, Utility::qDateTimeToTime_t(i._modtime) );
if( !_setUploadInfoQuery->exec() ) { 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; return;
} }
qDebug() << _setUploadInfoQuery->lastQuery() << file << i._chunk << i._transferid << i._errorCount; qDebug() << _setUploadInfoQuery->lastQuery() << file << i._chunk << i._transferid << i._errorCount;
_setUploadInfoQuery->finish(); _setUploadInfoQuery->reset();
} else { } else {
_deleteUploadInfoQuery->bindValue(0, file); _deleteUploadInfoQuery->bindValue(1, file);
if( !_deleteUploadInfoQuery->exec() ) { 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; return;
} }
qDebug() << _deleteUploadInfoQuery->executedQuery() << file; qDebug() << _deleteUploadInfoQuery->lastQuery() << file;
_deleteUploadInfoQuery->finish(); _deleteUploadInfoQuery->reset();
} }
} }
@ -817,11 +802,11 @@ bool SyncJournalDb::deleteStaleUploadInfos(const QSet<QString> &keep)
return false; return false;
} }
QSqlQuery query(_db); SqlQuery query(_db);
query.prepare("SELECT path FROM uploadinfo"); query.prepare("SELECT path FROM uploadinfo");
if (!query.exec()) { if (!query.exec()) {
QString err = query.lastError().text(); QString err = query.error();
qDebug() << "Error creating prepared statement: " << query.lastQuery() << ", Error:" << err; qDebug() << "Error creating prepared statement: " << query.lastQuery() << ", Error:" << err;
return false; return false;
} }
@ -829,7 +814,7 @@ bool SyncJournalDb::deleteStaleUploadInfos(const QSet<QString> &keep)
QStringList superfluousPaths; QStringList superfluousPaths;
while (query.next()) { while (query.next()) {
const QString file = query.value(0).toString(); const QString file = query.stringValue(0);
if (!keep.contains(file)) { if (!keep.contains(file)) {
superfluousPaths.append(file); superfluousPaths.append(file);
} }
@ -848,21 +833,20 @@ SyncJournalBlacklistRecord SyncJournalDb::blacklistEntry( const QString& file )
// SELECT lastTryEtag, lastTryModtime, retrycount, errorstring // SELECT lastTryEtag, lastTryModtime, retrycount, errorstring
if( checkConnect() ) { if( checkConnect() ) {
_blacklistQuery->bindValue( ":path", file ); _blacklistQuery->bindValue( 1, file );
if( _blacklistQuery->exec() ){ if( _blacklistQuery->exec() ){
if( _blacklistQuery->next() ) { if( _blacklistQuery->next() ) {
bool ok; entry._lastTryEtag = _blacklistQuery->baValue(0);
entry._lastTryEtag = _blacklistQuery->value(0).toByteArray(); entry._lastTryModtime = _blacklistQuery->int64Value(1);
entry._lastTryModtime = _blacklistQuery->value(1).toLongLong(&ok); entry._retryCount = _blacklistQuery->intValue(2);
entry._retryCount = _blacklistQuery->value(2).toInt(); entry._errorString = _blacklistQuery->stringValue(3);
entry._errorString = _blacklistQuery->value(3).toString();
entry._file = file; entry._file = file;
} }
} else { } else {
qWarning() << "Exec error blacklist: " << _blacklistQuery->lastQuery() << " : " qWarning() << "Exec error blacklist: " << _blacklistQuery->lastQuery() << " : "
<< _blacklistQuery->lastError().text(); << _blacklistQuery->error();
} }
_blacklistQuery->finish(); _blacklistQuery->reset();
} }
return entry; return entry;
@ -876,11 +860,11 @@ bool SyncJournalDb::deleteStaleBlacklistEntries(const QSet<QString> &keep)
return false; return false;
} }
QSqlQuery query(_db); SqlQuery query(_db);
query.prepare("SELECT path FROM blacklist"); query.prepare("SELECT path FROM blacklist");
if (!query.exec()) { if (!query.exec()) {
QString err = query.lastError().text(); QString err = query.error();
qDebug() << "Error creating prepared statement: " << query.lastQuery() << ", Error:" << err; qDebug() << "Error creating prepared statement: " << query.lastQuery() << ", Error:" << err;
return false; return false;
} }
@ -888,13 +872,13 @@ bool SyncJournalDb::deleteStaleBlacklistEntries(const QSet<QString> &keep)
QStringList superfluousPaths; QStringList superfluousPaths;
while (query.next()) { while (query.next()) {
const QString file = query.value(0).toString(); const QString file = query.stringValue(0);
if (!keep.contains(file)) { if (!keep.contains(file)) {
superfluousPaths.append(file); superfluousPaths.append(file);
} }
} }
QSqlQuery delQuery(_db); SqlQuery delQuery(_db);
delQuery.prepare("DELETE FROM blacklist WHERE path = ?"); delQuery.prepare("DELETE FROM blacklist WHERE path = ?");
return deleteBatch(delQuery, superfluousPaths, "blacklist"); return deleteBatch(delQuery, superfluousPaths, "blacklist");
} }
@ -905,12 +889,13 @@ int SyncJournalDb::blackListEntryCount()
QMutexLocker locker(&_mutex); QMutexLocker locker(&_mutex);
if( checkConnect() ) { if( checkConnect() ) {
QSqlQuery query(_db); SqlQuery query("SELECT count(*) FROM blacklist", _db);
if( ! query.exec("SELECT count(*) FROM blacklist") ) {
if( ! query.exec() ) {
sqlFail("Count number of blacklist entries failed", query); sqlFail("Count number of blacklist entries failed", query);
} }
if( query.next() ) { if( query.next() ) {
re = query.value(0).toInt(); re = query.intValue(0);
} }
} }
return re; return re;
@ -920,7 +905,7 @@ int SyncJournalDb::wipeBlacklist()
{ {
QMutexLocker locker(&_mutex); QMutexLocker locker(&_mutex);
if( checkConnect() ) { if( checkConnect() ) {
QSqlQuery query(_db); SqlQuery query(_db);
query.prepare("DELETE FROM blacklist"); query.prepare("DELETE FROM blacklist");
@ -941,10 +926,10 @@ void SyncJournalDb::wipeBlacklistEntry( const QString& file )
QMutexLocker locker(&_mutex); QMutexLocker locker(&_mutex);
if( checkConnect() ) { if( checkConnect() ) {
QSqlQuery query(_db); SqlQuery query(_db);
query.prepare("DELETE FROM blacklist WHERE path=:path"); query.prepare("DELETE FROM blacklist WHERE path=?1");
query.bindValue(":path", file); query.bindValue(1, file);
if( ! query.exec() ) { if( ! query.exec() ) {
sqlFail("Deletion of blacklist item failed.", query); sqlFail("Deletion of blacklist item failed.", query);
} }
@ -969,32 +954,32 @@ void SyncJournalDb::updateBlacklistEntry( const SyncJournalBlacklistRecord& item
retries = rec._retryCount; retries = rec._retryCount;
} }
QSqlQuery iQuery(_db); SqlQuery iQuery(_db);
if( haveRecord ) { if( haveRecord ) {
retries--; retries--;
if( retries < 0 ) retries = 0; if( retries < 0 ) retries = 0;
iQuery.prepare( "UPDATE blacklist SET lastTryEtag = :etag, lastTryModtime = :modtime, " iQuery.prepare( "UPDATE blacklist SET lastTryEtag = ?1, lastTryModtime = ?2, "
"retrycount = :retries, errorstring = :errStr WHERE path=:path"); "retrycount = ?3, errorstring = ?4 WHERE path=?5;");
iQuery.bindValue(":etag", item._lastTryEtag); iQuery.bindValue(1, item._lastTryEtag);
iQuery.bindValue(":modtime", QString::number(item._lastTryModtime)); iQuery.bindValue(2, QString::number(item._lastTryModtime));
iQuery.bindValue(":retries", retries); iQuery.bindValue(3, retries);
iQuery.bindValue(":errStr", item._errorString); iQuery.bindValue(4, item._errorString);
iQuery.bindValue(":path", item._file); iQuery.bindValue(5, item._file);
} else { } else {
// there is no entry yet. // there is no entry yet.
iQuery.prepare("INSERT INTO blacklist (path, lastTryEtag, lastTryModtime, retrycount, errorstring) " 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(1, item._file );
iQuery.bindValue(":lastEtag", item._lastTryEtag); iQuery.bindValue(2, item._lastTryEtag);
iQuery.bindValue(":lastMTime", QString::number(item._lastTryModtime)); iQuery.bindValue(3, QString::number(item._lastTryModtime));
iQuery.bindValue(":retrycount", item._retryCount); iQuery.bindValue(4, item._retryCount);
iQuery.bindValue(":errorstring", item._errorString); iQuery.bindValue(5, item._errorString);
} }
if( !iQuery.exec() ) { if( !iQuery.exec() ) {
QString bug = iQuery.lastError().text(); QString bug = iQuery.error();
qDebug() << "SQL exec blacklistitem insert/update failed: "<< bug; qDebug() << "SQL exec blacklistitem insert/update failed: "<< bug;
} }
@ -1008,14 +993,14 @@ void SyncJournalDb::avoidRenamesOnNextSync(const QString& path)
return; return;
} }
QSqlQuery query(_db); SqlQuery query(_db);
query.prepare("UPDATE metadata SET fileid = '', inode = '0' WHERE path == ? OR path LIKE(?||'/%')"); query.prepare("UPDATE metadata SET fileid = '', inode = '0' WHERE path == ?1 OR path LIKE(?2||'/%')");
query.bindValue(0, path);
query.bindValue(1, path); query.bindValue(1, path);
query.bindValue(2, path);
if( !query.exec() ) { 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 { } 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 // 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; return;
} }
QSqlQuery query(_db); SqlQuery query(_db);
// This query will match entries for whitch the path is a prefix of fileName // 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.prepare("UPDATE metadata SET md5='_invalid_' WHERE ?1 LIKE(path||'/%') AND type == 2;"); // CSYNC_FTW_TYPE_DIR == 2
query.bindValue(0, fileName); query.bindValue(1, fileName);
if( !query.exec() ) { 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 { } 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 // 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 ) 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(); commitTransaction();
if( startTrans ) { if( startTrans ) {

View file

@ -17,11 +17,10 @@
#include <QObject> #include <QObject>
#include <qmutex.h> #include <qmutex.h>
#include <QDateTime> #include <QDateTime>
#include <QSqlDatabase>
#include <QHash> #include <QHash>
#include <QSqlQuery>
#include "utility.h" #include "utility.h"
#include "ownsql.h"
namespace Mirall { namespace Mirall {
class SyncJournalFileRecord; class SyncJournalFileRecord;
@ -44,6 +43,9 @@ public:
int getFileRecordCount(); int getFileRecordCount();
bool exists(); bool exists();
QString databaseFilePath();
static qint64 getPHash(const QString& );
void updateBlacklistEntry( const SyncJournalBlacklistRecord& item ); void updateBlacklistEntry( const SyncJournalBlacklistRecord& item );
void wipeBlacklistEntry(const QString& file); void wipeBlacklistEntry(const QString& file);
int wipeBlacklist(); int wipeBlacklist();
@ -106,31 +108,30 @@ public:
bool isUpdateFrom_1_5(); bool isUpdateFrom_1_5();
private: private:
qint64 getPHash(const QString& ) const;
bool updateDatabaseStructure(); 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 commitInternal(const QString &context, bool startTrans = true);
void startTransaction(); void startTransaction();
void commitTransaction(); void commitTransaction();
QStringList tableColumns( const QString& table ); QStringList tableColumns( const QString& table );
bool checkConnect(); bool checkConnect();
QSqlDatabase _db; SqlDatabase _db;
QString _dbFile; QString _dbFile;
QMutex _mutex; // Public functions are protected with the mutex. QMutex _mutex; // Public functions are protected with the mutex.
int _transaction; int _transaction;
bool _possibleUpgradeFromMirall_1_5; bool _possibleUpgradeFromMirall_1_5;
QScopedPointer<QSqlQuery> _getFileRecordQuery; QScopedPointer<SqlQuery> _getFileRecordQuery;
QScopedPointer<QSqlQuery> _setFileRecordQuery; QScopedPointer<SqlQuery> _setFileRecordQuery;
QScopedPointer<QSqlQuery> _getDownloadInfoQuery; QScopedPointer<SqlQuery> _getDownloadInfoQuery;
QScopedPointer<QSqlQuery> _setDownloadInfoQuery; QScopedPointer<SqlQuery> _setDownloadInfoQuery;
QScopedPointer<QSqlQuery> _deleteDownloadInfoQuery; QScopedPointer<SqlQuery> _deleteDownloadInfoQuery;
QScopedPointer<QSqlQuery> _getUploadInfoQuery; QScopedPointer<SqlQuery> _getUploadInfoQuery;
QScopedPointer<QSqlQuery> _setUploadInfoQuery; QScopedPointer<SqlQuery> _setUploadInfoQuery;
QScopedPointer<QSqlQuery> _deleteUploadInfoQuery; QScopedPointer<SqlQuery> _deleteUploadInfoQuery;
QScopedPointer<QSqlQuery> _deleteFileRecordPhash; QScopedPointer<SqlQuery> _deleteFileRecordPhash;
QScopedPointer<QSqlQuery> _deleteFileRecordRecursively; QScopedPointer<SqlQuery> _deleteFileRecordRecursively;
QScopedPointer<QSqlQuery> _blacklistQuery; QScopedPointer<SqlQuery> _blacklistQuery;
/* This is the list of paths we called avoidReadFromDbOnNextSync on. /* 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 * It means that they should not be written to the DB in any case since doing

View file

@ -224,6 +224,18 @@ QString Utility::toCSyncScheme(const QString &urlStr)
return url.toString(); 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) QString Utility::escape(const QString &in)
{ {
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)

View file

@ -42,6 +42,10 @@ namespace Utility
OWNCLOUDSYNC_EXPORT QString toCSyncScheme(const QString &urlStr); OWNCLOUDSYNC_EXPORT QString toCSyncScheme(const QString &urlStr);
/** Like QLocale::toString(double, 'f', prec), but drops trailing zeros after the decimal point */ /** 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. * @brief compactFormatDouble - formats a double value human readable.
* *

View file

@ -27,5 +27,7 @@ endif(UNIX AND NOT APPLE)
owncloud_add_test(CSyncSqlite "") owncloud_add_test(CSyncSqlite "")
owncloud_add_test(NetrcParser ../src/owncloudcmd/netrcparser.cpp) owncloud_add_test(NetrcParser ../src/owncloudcmd/netrcparser.cpp)
owncloud_add_test(OwnSql ../src/mirall/ownsql.cpp)

155
test/testownsql.h Normal file
View 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