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(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)

View file

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

View file

@ -14,7 +14,8 @@ if (${CMAKE_C_COMPILER_ID} MATCHES "(GNU|Clang)")
# add -Wconversion ?
# cannot be pedantic with sqlite3 directly linked
if (NOT CSYNC_STATIC_COMPILE_DIR)
# FIXME Can we somehow not use those flags for sqlite3.* but use them for the rest of csync?
if (NOT USE_OUR_OWN_SQLITE3)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -pedantic -pedantic-errors")
endif()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wshadow -Wmissing-prototypes")

View file

@ -50,8 +50,18 @@ if (SQLite3_FIND_VERSION AND _SQLITE3_VERSION)
set(SQLite3_VERSION _SQLITE3_VERSION)
endif (SQLite3_FIND_VERSION AND _SQLITE3_VERSION)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(SQLite3 DEFAULT_MSG SQLITE3_LIBRARIES SQLITE3_INCLUDE_DIRS)
if (APPLE OR WINDOWS)
set(USE_OUR_OWN_SQLITE3 TRUE)
set(SQLITE3_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/src/3rdparty/sqlite3)
set(SQLITE3_LIBRARIES "")
set(SQLITE3_SOURCE ${SQLITE3_INCLUDE_DIR}/sqlite3.c)
MESSAGE(STATUS "Using own sqlite3 from " ${SQLITE3_INCLUDE_DIR})
else()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(SQLite3 DEFAULT_MSG SQLITE3_LIBRARIES SQLITE3_INCLUDE_DIRS)
endif()
# show the SQLITE3_INCLUDE_DIRS and SQLITE3_LIBRARIES variables only in the advanced view
mark_as_advanced(SQLITE3_INCLUDE_DIRS SQLITE3_LIBRARIES)

View file

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

View file

@ -4,13 +4,6 @@ add_subdirectory(std)
add_subdirectory(httpbf)
# Statically include sqlite
if (CSYNC_STATIC_COMPILE_DIR)
set(SQLITE3_INCLUDE_DIRS "")
set(SQLITE3_LIBRARIES "")
include_directories(${CSYNC_STATIC_COMPILE_DIR})
else (CSYNC_STATIC_COMPILE_DIR)
find_package(SQLite3 3.3.9 REQUIRED)
endif()
set(CSYNC_PUBLIC_INCLUDE_DIRS
${CMAKE_CURRENT_BINARY_DIR}
@ -86,8 +79,8 @@ set(csync_HDRS
)
# Statically include sqlite
if (CSYNC_STATIC_COMPILE_DIR)
list(APPEND csync_SRCS ${CSYNC_STATIC_COMPILE_DIR}/dictionary.c ${CSYNC_STATIC_COMPILE_DIR}/sqlite3.c)
if (USE_OUR_OWN_SQLITE3)
list(APPEND csync_SRCS ${SQLITE3_SOURCE})
endif()
include_directories(

View file

@ -722,16 +722,15 @@ sub createShare($$)
my $re = $dd->mkcol( $url );
if( $re == 0 ) {
print "Failed to create test dir $url\n";
}
my $ua = LWP::UserAgent->new(ssl_opts => { verify_hostname => 0 } );
$ua->agent( "ownCloudTest_sharing");
# http://localhost/ocm/ocs/v1.php/apps/files_sharing/api/v1/shares
my $puturl = $ocs_url . "apps/files_sharing/api/v1/shares";
my $puturl = $ocs_url . "ocs/v1.php/apps/files_sharing/api/v1/shares";
my $string = "path=$dir&shareType=0&shareWith=$user&publicUpload=false&permissions=$readWrite";
print ">>>>>>>>>> $string\n";
print ">>>>>>>>>> $puturl $string\n";
my $req = POST $puturl, Content => $string;
$req->authorization_basic($share_user, $share_passwd);
@ -763,15 +762,14 @@ sub removeShare($$)
my $ua = LWP::UserAgent->new(ssl_opts => { verify_hostname => 0 });
$ua->agent( "ownCloudTest_sharing");
# http://localhost/ocm/ocs/v1.php/apps/files_sharing/api/v1/shares
my $url = $ocs_url . "apps/files_sharing/api/v1/shares/" . $shareId;
my $url = $ocs_url . "ocs/v1.php/apps/files_sharing/api/v1/shares/" . $shareId;
my $req = DELETE $url;
$req->authorization_basic($share_user, $share_passwd);
my $response = $ua->request($req);
if ($response->is_success()) {
# print "OK: ", $response->content;
print $response->decoded_content;
if( $response->decoded_content =~ /<status_code>(\d+)<\/status_code>/m) {
my $code = $1;

View file

@ -33,11 +33,13 @@ print "Hello, this is t6, a tester for csync with ownCloud.\n";
initTesting();
sub createPostUpdateScript()
sub createPostUpdateScript($)
{
my $srcFile = localDir()."BIG.file";
my ($name) = @_;
my $srcFile = localDir().'BIG1.file';
my $cred = configValue("user") . ":" . configValue("passwd");
my $cmd = "curl -T $srcFile -u $cred " . testDirUrl();
my $cmd = "curl -T $srcFile -u $cred " . testDirUrl().$name;
my $script = "/tmp/post_update_script.sh";
open SC, ">$script" || die("Can not create script file");
print SC "#!/bin/bash\n";
@ -48,11 +50,11 @@ sub createPostUpdateScript()
return $script;
}
sub getETagFromJournal($)
sub getETagFromJournal($$)
{
my ($num) = @_;
my ($name,$num) = @_;
my $sql = "sqlite3 " . localDir() . ".csync_journal.db \"SELECT md5 FROM metadata WHERE path='BIG.file';\"";
my $sql = "sqlite3 " . localDir() . ".csync_journal.db \"SELECT md5 FROM metadata WHERE path='$name';\"";
open(my $fh, '-|', $sql) or die $!;
my $etag = <$fh>;
close $fh;
@ -89,26 +91,39 @@ sub chunkFileTest( $$ )
}
printInfo("Big file that needs chunking with default chunk size");
chunkFileTest( "BIG.file", 23251233 );
chunkFileTest( "BIG1.file", 23251233 );
printInfo("Update the existing file and trigger reupload");
# change the existing file again -> update
chunkFileTest( "BIG.file", 21762122 );
chunkFileTest( "BIG2.file", 21762122 );
printInfo("Cause a precondition failed error");
# Now overwrite the existing file to change it
createLocalFile( localDir()."BIG.file", 21832199 );
createLocalFile( localDir()."BIG3.file", 21832 );
sleep(2);
csync();
createLocalFile( localDir().'BIG3.file', 34323 );
sleep(2);
# and create a post update script
my $script = createPostUpdateScript();
my $script = createPostUpdateScript('BIG3.file');
$ENV{'OWNCLOUD_POST_UPDATE_SCRIPT'} = $script;
# Save the etag before the sync
my $firstETag = getETagFromJournal('First');
my $firstETag = getETagFromJournal('BIG3.file', 'First');
sleep(2);
csync(); # Sync, which ends in a precondition failed error
# get the etag again. It has to be unchanged because of the error.
my $secondETag = getETagFromJournal('Second');
assert( $firstETag eq $secondETag, "Different ETags, no precondition error." );
my $secondETag = getETagFromJournal('BIG3.file', 'Second');
# Now the result is that there is a conflict file because since 1.7
# the sync is stopped on preconditoin failed and done again.
my $seen = 0;
opendir(my $dh, localDir() );
while(readdir $dh) {
$seen = 1 if ( /BIG3_conflict.*\.file/ );
}
closedir $dh;
assert( $seen == 1, "No conflict file created on precondition failed!" );
unlink($script);
# Set a custom chunk size in environment.

View file

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

View file

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

148882
src/3rdparty/sqlite3/sqlite3.c vendored Normal file

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

View file

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

View file

@ -512,11 +512,90 @@ QString Folder::configFile()
return _configFile;
}
static void addErroredSyncItemPathsToList(const SyncFileItemVector& items, QSet<QString>* set) {
Q_FOREACH(const SyncFileItem &item, items) {
if (item.hasErrorStatus()) {
set->insert(item._file);
}
}
}
void Folder::slotThreadTreeWalkResult(const SyncFileItemVector& items)
{
addErroredSyncItemPathsToList(items, &this->_stateLastSyncItemsWithError);
_syncResult.setSyncFileItemVector(items);
}
void Folder::slotAboutToPropagate(const SyncFileItemVector& items)
{
// empty the tainted list since the status generation code will use the _syncedItems
// (which imply the folder) to generate the syncing state icon now.
_stateTaintedFolders.clear();
addErroredSyncItemPathsToList(items, &this->_stateLastSyncItemsWithError);
}
bool Folder::estimateState(QString fn, csync_ftw_type_e t, SyncFileStatus* s)
{
if (t == CSYNC_FTW_TYPE_DIR) {
qDebug() << Q_FUNC_INFO << "ASKING ERROR FOLDERS" << fn;
if (Utility::doesSetContainPrefix(_stateLastSyncItemsWithError, fn)) {
s->set(SyncFileStatus::STATUS_ERROR);
return true;
}
// If sync is running, check _syncedItems, possibly give it STATUS_EVAL (=syncing down)
if (!_engine.isNull()) {
qDebug() << Q_FUNC_INFO << "SYNC IS RUNNING, asking SyncEngine" << fn;
if (_engine->estimateState(fn, t, s)) {
return true;
}
}
qDebug() << Q_FUNC_INFO << "ASKING TAINTED FOLDERS" << fn;
if (Utility::doesSetContainPrefix(_stateTaintedFolders, fn)) {
qDebug() << Q_FUNC_INFO << "Folder is tainted, EVAL!" << fn;
s->set(SyncFileStatus::STATUS_EVAL);
return true;
}
return false;
} else if ( t== CSYNC_FTW_TYPE_FILE) {
// check if errorList has the directory/file
if (Utility::doesSetContainPrefix(_stateLastSyncItemsWithError, fn)) {
s->set(SyncFileStatus::STATUS_ERROR);
return true;
}
// If sync running: _syncedItems -> SyncingState
if (!_engine.isNull()) {
if (_engine->estimateState(fn, t, s)) {
return true;
}
}
}
return false;
}
void Folder::watcherSlot(QString fn)
{
// FIXME: On OS X we could not do this "if" since on OS X the file watcher ignores events for ourselves
// however to have the same behaviour atm on all platforms, we don't do it
if (!_engine.isNull()) {
qDebug() << Q_FUNC_INFO << "Sync running, IGNORE event for " << fn;
return;
}
QFileInfo fi(fn);
if (fi.isFile()) {
fn = fi.path(); // depending on OS, file watcher might be for dir or file
}
// Make it a relative path depending on the folder
QString relativePath = fn.remove(0, path().length());
qDebug() << Q_FUNC_INFO << fi.canonicalFilePath() << fn << relativePath;
_stateTaintedFolders.insert(relativePath);
// Notify the SocketAPI?
}
void Folder::slotTerminateSync()
{
qDebug() << "folder " << alias() << " Terminating!";
@ -639,6 +718,8 @@ void Folder::startSync(const QStringList &pathList)
connect( _engine.data(), SIGNAL(treeWalkResult(const SyncFileItemVector&)),
this, SLOT(slotThreadTreeWalkResult(const SyncFileItemVector&)), Qt::QueuedConnection);
connect( _engine.data(), SIGNAL(aboutToPropagate(const SyncFileItemVector&)),
this, SLOT(slotAboutToPropagate(const SyncFileItemVector&)), Qt::QueuedConnection);
connect(_engine.data(), SIGNAL(started()), SLOT(slotSyncStarted()), Qt::QueuedConnection);
connect(_engine.data(), SIGNAL(finished()), SLOT(slotSyncFinished()), Qt::QueuedConnection);
@ -720,6 +801,15 @@ void Folder::slotSyncFinished()
// _watcher->setEventsEnabledDelayed(2000);
// This is for sync state calculation
_stateLastSyncItemsWithError = _stateLastSyncItemsWithErrorNew;
_stateLastSyncItemsWithErrorNew.clear();
_stateTaintedFolders.clear(); // heuristic: assume the sync had been done, new file watches needed to taint dirs
if (_csyncError || _csyncUnavail) {
// Taint the whole sync dir, we cannot give reliable state information
_stateTaintedFolders.insert(QLatin1String("/"));
}
if (_csyncError) {
_syncResult.setStatus(SyncResult::Error);
@ -786,6 +876,10 @@ void Folder::slotTransmissionProgress(const Progress::Info &pi)
// a job is completed: count the errors and forward to the ProgressDispatcher
void Folder::slotJobCompleted(const SyncFileItem &item)
{
if (item.hasErrorStatus()) {
_stateLastSyncItemsWithError.insert(item._file);
}
if (Progress::isWarningKind(item._status)) {
// Count all error conditions.
_syncResult.setWarnCount(_syncResult.warnCount()+1);

View file

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

View file

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

View file

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

259
src/mirall/ownsql.cpp Normal file
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 <QLocalSocket>
#include <sqlite3.h>
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
#include <QStandardPaths>
#endif
@ -64,159 +67,6 @@ namespace Mirall {
#define DEBUG qDebug() << "SocketApi: "
namespace SocketApiHelper {
SyncFileStatus fileStatus(Folder *folder, const QString& systemFileName, c_strlist_t *excludes );
/**
* @brief recursiveFolderStatus
* @param fileName - the relative file name to examine
* @return the resulting status
*
* The resulting status can only be either SYNC which means all files
* are in sync, ERROR if an error occured, or EVAL if something needs
* to be synced underneath this dir.
*/
// compute the file status of a directory recursively. It returns either
// "all in sync" or "needs update" or "error", no more details.
SyncFileStatus recursiveFolderStatus(Folder *folder, const QString& fileName, c_strlist_t *excludes )
{
QDir dir(folder->path() + fileName);
const QStringList dirEntries = dir.entryList( QDir::AllEntries | QDir::NoDotAndDotDot );
SyncFileStatus result(SyncFileStatus::STATUS_SYNC);
foreach( const QString entry, dirEntries ) {
QString normalizedFile = QString(fileName + QLatin1Char('/') + entry).normalized(QString::NormalizationForm_C);
QFileInfo fi(entry);
SyncFileStatus sfs;
if( fi.isDir() ) {
sfs = recursiveFolderStatus(folder, normalizedFile, excludes );
} else {
QString fs( normalizedFile );
if( fileName.isEmpty() ) {
// toplevel, no slash etc. needed.
fs = entry.normalized(QString::NormalizationForm_C);
}
sfs = fileStatus(folder, fs, excludes);
}
if( sfs.tag() == SyncFileStatus::STATUS_STAT_ERROR || sfs.tag() == SyncFileStatus::STATUS_ERROR ) {
return SyncFileStatus::STATUS_ERROR;
} else if( sfs.tag() == SyncFileStatus::STATUS_EVAL || sfs.tag() == SyncFileStatus::STATUS_NEW) {
result.set(SyncFileStatus::STATUS_EVAL);
}
}
return result;
}
SyncJournalFileRecord dbFileRecord( Folder *folder, QString fileName )
{
QFileInfo fi(fileName);
if( !folder ) {
return SyncJournalFileRecord();
}
if( fi.isAbsolute() ) {
fileName.remove(0, folder->path().length());
}
return( folder->journalDb()->getFileRecord(fileName) );
}
/**
* Get status about a single file.
*/
SyncFileStatus fileStatus(Folder *folder, const QString& systemFileName, c_strlist_t *excludes )
{
QString file = folder->path();
QString fileName = systemFileName.normalized(QString::NormalizationForm_C);
bool isSyncRootFolder = true;
if( fileName != QLatin1String("/") && !fileName.isEmpty() ) {
file = folder->path() + fileName;
isSyncRootFolder = false;
}
if( fileName.endsWith(QLatin1Char('/')) ) {
fileName.truncate(fileName.length()-1);
qDebug() << "Removed trailing slash: " << fileName;
}
QFileInfo fi(file);
if( !fi.exists() ) {
qDebug() << "OO File " << file << " is not existing";
return SyncFileStatus(SyncFileStatus::STATUS_STAT_ERROR);
}
// file is ignored?
if( fi.isSymLink() ) {
return SyncFileStatus(SyncFileStatus::STATUS_IGNORE);
}
int type = CSYNC_FTW_TYPE_FILE;
if( fi.isDir() ) {
type = CSYNC_FTW_TYPE_DIR;
}
// '\' is ignored, so convert to unix path before passing the path in.
QString unixFileName = QDir::fromNativeSeparators(fileName);
CSYNC_EXCLUDE_TYPE excl = csync_excluded_no_ctx(excludes, unixFileName.toUtf8(), type);
if( excl != CSYNC_NOT_EXCLUDED ) {
return SyncFileStatus(SyncFileStatus::STATUS_IGNORE);
}
// Problem: for the sync dir itself we do not have a record in the sync journal
// so the next check must not be used for the sync root folder.
SyncJournalFileRecord rec = dbFileRecord(folder, unixFileName );
if( !isSyncRootFolder && !rec.isValid() ) {
// check the parent folder if it is shared and if it is allowed to create a file/dir within
QDir d( fi.path() );
QString parentPath = d.path();
SyncJournalFileRecord dirRec = dbFileRecord(folder, parentPath);
while( !d.isRoot() && !(d.exists() && dirRec.isValid()) ) {
d.cdUp(); // returns true if the dir exists.
parentPath = d.path();
// cut the folder path
dirRec = dbFileRecord(folder, parentPath);
}
if( dirRec.isValid() ) {
if( dirRec._type == CSYNC_FTW_TYPE_DIR ) {
if( !dirRec._remotePerm.contains("K") ) {
return SyncFileStatus::STATUS_ERROR;
}
} else {
if( !dirRec._remotePerm.contains("C") ) {
return SyncFileStatus::STATUS_ERROR;
}
}
}
return SyncFileStatus(SyncFileStatus::STATUS_NEW);
}
SyncFileStatus status(SyncFileStatus::STATUS_NONE);
if( type == CSYNC_FTW_TYPE_DIR ) {
// compute recursive status of the directory
status = recursiveFolderStatus( folder, fileName, excludes );
} else if( FileSystem::getModTime(fi.absoluteFilePath()) != Utility::qDateTimeToTime_t(rec._modtime) ) {
// file was locally modified.
status.set(SyncFileStatus::STATUS_EVAL);
} else {
status.set(SyncFileStatus::STATUS_SYNC);
}
if (rec._remotePerm.contains("S")) {
status.setSharedWithMe(true);
}
return status;
}
}
SocketApi::SocketApi(QObject* parent)
: QObject(parent)
, _excludes(0)
@ -374,6 +224,11 @@ void SocketApi::slotUnregisterPath( const QString& alias )
Folder *f = FolderMan::instance()->folder(alias);
if (f) {
broadcastMessage(QLatin1String("UNREGISTER_PATH"), f->path(), QString::null, true );
if( _dbConnections.contains(f)) {
sqlite3_close_v2(_dbConnections[f]._db);
}
_dbConnections.remove(f);
}
}
@ -393,6 +248,8 @@ void SocketApi::slotUpdateFolderView(const QString& alias)
} else {
broadcastMessage(QLatin1String("UPDATE_VIEW"), f->path() );
}
} else {
qDebug() << "Not sending UPDATE_VIEW for" << alias << "because status() is" << f->syncResult().status();
}
}
}
@ -464,7 +321,8 @@ void SocketApi::broadcastMessage( const QString& verb, const QString& path, cons
msg.append(QDir::toNativeSeparators(path));
}
DEBUG << "Broadcasting to" << _listeners.count() << "listeners: " << msg;
// sendMessage already has a debug output
//DEBUG << "Broadcasting to" << _listeners.count() << "listeners: " << msg;
foreach(SocketType *socket, _listeners) {
sendMessage(socket, msg, doWait);
}
@ -495,10 +353,11 @@ void SocketApi::command_RETRIEVE_FILE_STATUS(const QString& argument, SocketType
DEBUG << "folder offline or not watched:" << argument;
statusString = QLatin1String("NOP");
} else {
const QString file = argument.mid(syncFolder->path().length());
SyncFileStatus fileStatus = SocketApiHelper::fileStatus(syncFolder, file, _excludes);
QString file = argument.mid(syncFolder->path().length());
if( file.isEmpty() ) file = QLatin1String("/");
SyncFileStatus fStatus = this->fileStatus(syncFolder, file, _excludes);
statusString = fileStatus.toSocketAPIString();
statusString = fStatus.toSocketAPIString();
}
QString message = QLatin1String("STATUS:")+statusString+QLatin1Char(':')
@ -511,6 +370,178 @@ void SocketApi::command_VERSION(const QString&, SocketType* socket)
sendMessage(socket, QLatin1String("VERSION:" MIRALL_VERSION_STRING ":" MIRALL_SOCKET_API_VERSION));
}
SqliteHandle SocketApi::getSqliteHandle( Folder *folder )
{
if( _dbConnections.contains(folder) ) {
return _dbConnections[folder];
}
SqliteHandle h;
h._db = NULL;
h._stmt = NULL;
if( !folder ) {
return h;
}
int rc;
const char* query = "SELECT inode, mode, modtime, type, md5, fileid, remotePerm FROM "
"metadata WHERE phash=:ph";
QString dbFileName = folder->journalDb()->databaseFilePath();
if( sqlite3_open_v2(dbFileName.toUtf8().constData(), &(h._db),
SQLITE_OPEN_READONLY+SQLITE_OPEN_NOMUTEX, NULL) == SQLITE_OK ) {
rc = sqlite3_prepare_v2(h._db, query, strlen(query), &(h._stmt), NULL);
if( rc != SQLITE_OK ) {
qDebug() << "Unable to prepare the query statement.";
return h; // do not insert into hash
}
_dbConnections.insert( folder, h);
}
return h;
}
SyncJournalFileRecord SocketApi::dbFileRecord_capi( Folder *folder, QString fileName )
{
if( !(folder && folder->journalDb()) ) {
return SyncJournalFileRecord();
}
if( fileName.startsWith( folder->path() )) {
fileName.remove(0, folder->path().length());
}
SqliteHandle h = getSqliteHandle(folder);
sqlite3_stmt *stmt = h._stmt;
SyncJournalFileRecord rec;
if( h._db && h._stmt ) {
qlonglong phash = SyncJournalDb::getPHash( fileName );
sqlite3_bind_int64(stmt, 1, (long long signed int)phash);
// int column_count = sqlite3_column_count(stmt);
int rc = sqlite3_step(stmt);
if (rc == SQLITE_ROW ) {
rec._path = fileName;
rec._inode = sqlite3_column_int64(stmt,0);;
rec._mode = sqlite3_column_int(stmt, 1);
rec._modtime = Utility::qDateTimeFromTime_t( strtoul((char*)sqlite3_column_text(stmt, 2), NULL, 10));
rec._type = sqlite3_column_int(stmt, 3);;
rec._etag = QByteArray((char*)sqlite3_column_text(stmt, 4));
rec._fileId = QByteArray((char*)sqlite3_column_text(stmt, 5));
rec._remotePerm = QByteArray((char*)sqlite3_column_text(stmt, 6));
}
sqlite3_reset(stmt);
}
return rec;
}
/**
* Get status about a single file.
*/
SyncFileStatus SocketApi::fileStatus(Folder *folder, const QString& systemFileName, c_strlist_t *excludes )
{
QString file = folder->path();
QString fileName = systemFileName.normalized(QString::NormalizationForm_C);
bool isSyncRootFolder = true;
if( fileName != QLatin1String("/") && !fileName.isEmpty() ) {
file = folder->path() + fileName;
isSyncRootFolder = false;
}
if( fileName.endsWith(QLatin1Char('/')) ) {
fileName.truncate(fileName.length()-1);
qDebug() << "Removed trailing slash: " << fileName;
}
QFileInfo fi(file);
if( !fi.exists() ) {
qDebug() << "OO File " << file << " is not existing";
return SyncFileStatus(SyncFileStatus::STATUS_STAT_ERROR);
}
// file is ignored?
if( fi.isSymLink() ) {
return SyncFileStatus(SyncFileStatus::STATUS_IGNORE);
}
csync_ftw_type_e type = CSYNC_FTW_TYPE_FILE;
if( fi.isDir() ) {
type = CSYNC_FTW_TYPE_DIR;
}
// '\' is ignored, so convert to unix path before passing the path in.
QString unixFileName = QDir::fromNativeSeparators(fileName);
CSYNC_EXCLUDE_TYPE excl = csync_excluded_no_ctx(excludes, unixFileName.toUtf8(), type);
if( excl != CSYNC_NOT_EXCLUDED ) {
return SyncFileStatus(SyncFileStatus::STATUS_IGNORE);
}
SyncFileStatus status(SyncFileStatus::STATUS_NONE);
if (type == CSYNC_FTW_TYPE_DIR) {
if (folder->estimateState(fileName, type, &status)) {
qDebug() << Q_FUNC_INFO << "Folder estimated status for" << fileName << "to" << status.toSocketAPIString();
return status;
}
if (fileName == "") {
// sync folder itself
if (folder->syncResult().status() == SyncResult::Undefined
|| folder->syncResult().status() == SyncResult::NotYetStarted
|| folder->syncResult().status() == SyncResult::SyncPrepare
|| folder->syncResult().status() == SyncResult::SyncRunning
|| folder->syncResult().status() == SyncResult::Paused) {
status.set(SyncFileStatus::STATUS_EVAL);
return status;
} else if (folder->syncResult().status() == SyncResult::Success
|| folder->syncResult().status() == SyncResult::Problem) {
status.set(SyncFileStatus::STATUS_SYNC);
return status;
} else if (folder->syncResult().status() == SyncResult::Error
|| folder->syncResult().status() == SyncResult::SetupError
|| folder->syncResult().status() == SyncResult::SyncAbortRequested) {
status.set(SyncFileStatus::STATUS_ERROR);
return status;
}
}
SyncJournalFileRecord rec = dbFileRecord_capi(folder, unixFileName );
if (rec.isValid()) {
status.set(SyncFileStatus::STATUS_SYNC);
if (rec._remotePerm.contains("S")) {
status.setSharedWithMe(true);
}
} else {
status.set(SyncFileStatus::STATUS_EVAL);
}
} else if (type == CSYNC_FTW_TYPE_FILE) {
if (folder->estimateState(fileName, type, &status)) {
return status;
}
SyncJournalFileRecord rec = dbFileRecord_capi(folder, unixFileName );
if (rec.isValid()) {
if (rec._remotePerm.contains("S")) {
status.setSharedWithMe(true);
}
if( FileSystem::getModTime(fi.absoluteFilePath()) == Utility::qDateTimeToTime_t(rec._modtime) ) {
status.set(SyncFileStatus::STATUS_SYNC);
return status;
} else {
status.set(SyncFileStatus::STATUS_EVAL);
return status;
}
}
status.set(SyncFileStatus::STATUS_NEW);
return status;
}
return status;
}
} // namespace Mirall

View file

@ -20,13 +20,15 @@ extern "C" {
#include <std/c_string.h>
}
#include <sqlite3.h>
#include <QWeakPointer>
#include <QTcpSocket>
#include <QTcpServer>
#include <QLocalServer>
#include "mirall/syncfileitem.h"
#include "mirall/syncjournalfilerecord.h"
class QUrl;
class QLocalSocket;
class QStringList;
@ -35,6 +37,15 @@ namespace Mirall {
typedef QLocalSocket SocketType;
class SyncFileStatus;
class Folder;
struct SqliteHandle
{
sqlite3 *_db;
sqlite3_stmt *_stmt;
};
class SocketApi : public QObject
{
Q_OBJECT
@ -57,6 +68,12 @@ private slots:
void slotSyncItemDiscovered(const QString &, const SyncFileItem &);
private:
SyncFileStatus fileStatus(Folder *folder, const QString& systemFileName, c_strlist_t *excludes );
SyncJournalFileRecord dbFileRecord( Folder *folder, QString fileName );
SyncJournalFileRecord dbFileRecord_capi( Folder *folder, QString fileName );
SyncFileStatus recursiveFolderStatus(Folder *folder, const QString& fileName, c_strlist_t *excludes );
SqliteHandle getSqliteHandle( Folder *folder );
void sendMessage(SocketType* socket, const QString& message, bool doWait = false);
void broadcastMessage(const QString& verb, const QString &path, const QString &status = QString::null, bool doWait = false);
@ -65,7 +82,6 @@ private:
Q_INVOKABLE void command_VERSION(const QString& argument, SocketType* socket);
private:
#ifdef SOCKETAPI_TCP
QTcpServer _localServer;
#else
@ -73,6 +89,7 @@ private:
#endif
QList<SocketType*> _listeners;
c_strlist_t *_excludes;
QHash<Folder*, SqliteHandle> _dbConnections;
};
}

View file

@ -22,6 +22,7 @@
#include "discoveryphase.h"
#include "creds/abstractcredentials.h"
#include "csync_util.h"
#include "mirall/syncfilestatus.h"
#ifdef Q_OS_WIN
#include <windows.h>
@ -681,14 +682,16 @@ void SyncEngine::slotDiscoveryJobFinished(int discoveryResult)
Q_ASSERT(session);
// post update phase script: allow to tweak stuff by a custom script in debug mode.
#ifndef NDEBUG
if( !qgetenv("OWNCLOUD_POST_UPDATE_SCRIPT").isEmpty() ) {
#ifndef NDEBUG
QString script = qgetenv("OWNCLOUD_POST_UPDATE_SCRIPT");
qDebug() << "OOO => Post Update Script: " << script;
QProcess::execute(script.toUtf8());
}
#else
qDebug() << "**** Attention: POST_UPDATE_SCRIPT installed, but not executed because compiled with NDEBUG";
#endif
}
// do a database commit
_journal->commit("post treewalk");
@ -1069,7 +1072,18 @@ void SyncEngine::setSelectiveSyncBlackList(const QStringList& list)
}
}
bool SyncEngine::estimateState(QString fn, csync_ftw_type_e t, SyncFileStatus* s)
{
Q_FOREACH(const SyncFileItem &item, _syncedItems) {
//qDebug() << Q_FUNC_INFO << fn << item._file << fn.startsWith(item._file) << item._file.startsWith(fn);
if (item._file.startsWith(fn)) {
qDebug() << Q_FUNC_INFO << "Setting" << fn << " to STATUS_EVAL";
s->set(SyncFileStatus::STATUS_EVAL);
return true;
}
}
return false;
}
void SyncEngine::abort()
{

View file

@ -33,6 +33,7 @@
#include "mirall/syncfileitem.h"
#include "mirall/progressdispatcher.h"
#include "mirall/utility.h"
#include "mirall/syncfilestatus.h"
class QProcess;
@ -66,6 +67,8 @@ public:
/* Return true if we detected that another sync is needed to complete the sync */
bool isAnotherSyncNeeded() { return _anotherSyncNeeded; }
bool estimateState(QString fn, csync_ftw_type_e t, SyncFileStatus* s);
signals:
void csyncError( const QString& );
void csyncUnavailable();
@ -123,7 +126,11 @@ private:
void finalize();
static bool _syncRunning; //true when one sync is running somewhere (for debugging)
QMap<QString, SyncFileItem> _syncItemMap;
// should be called _syncItems (present tense). It's the items from the _syncItemMap but
// sorted and re-adjusted based on permissions.
SyncFileItemVector _syncedItems;
CSYNC *_csync_ctx;

View file

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

View file

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

View file

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

View file

@ -224,6 +224,18 @@ QString Utility::toCSyncScheme(const QString &urlStr)
return url.toString();
}
bool Utility::doesSetContainPrefix(QSet<QString> &l, QString &p) {
Q_FOREACH (const QString &setPath, l) {
//qDebug() << Q_FUNC_INFO << p << setPath << setPath.startsWith(p);
if (setPath.startsWith(p)) {
return true;
}
}
//qDebug() << "-> NOOOOO!!!" << p << l.count();
return false;
}
QString Utility::escape(const QString &in)
{
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)

View file

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

View file

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

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