diff --git a/autotests/server/partstreamertest.cpp b/autotests/server/partstreamertest.cpp index bc111c008..5e51c00c4 100644 --- a/autotests/server/partstreamertest.cpp +++ b/autotests/server/partstreamertest.cpp @@ -1,282 +1,283 @@ /* * Copyright (C) 2014 Daniel Vrátil * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include "fakeakonadiserver.h" #include "fakeconnection.h" #include "aktest.h" #include "entities.h" #include #include #include "storage/partstreamer.h" #include #include #include #include #include using namespace Akonadi; using namespace Akonadi::Server; Q_DECLARE_METATYPE(Akonadi::Server::PimItem) Q_DECLARE_METATYPE(Akonadi::Server::Part::Storage) class PartStreamerTest : public QObject { Q_OBJECT public: PartStreamerTest() { // Set a very small treshold for easier testing const QString serverConfigFile = StandardDirs::serverConfigFile(StandardDirs::ReadWrite); QSettings settings(serverConfigFile, QSettings::IniFormat); settings.setValue(QStringLiteral("General/SizeThreshold"), 5); try { FakeAkonadiServer::instance()->init(); } catch (const FakeAkonadiServerException &e) { qWarning() << "Server exception: " << e.what(); qFatal("Fake Akonadi Server failed to start up, aborting test"); } } ~PartStreamerTest() { FakeAkonadiServer::instance()->quit(); } Protocol::ModifyItemsCommandPtr createCommand(const PimItem &item) { auto cmd = Protocol::ModifyItemsCommandPtr::create(item.id()); cmd->setParts({ "PLD:DATA" }); return cmd; } private Q_SLOTS: void testStreamer_data() { QTest::addColumn("scenarios"); QTest::addColumn("expectedPartName"); QTest::addColumn("expectedPartData"); QTest::addColumn("expectedFileData"); QTest::addColumn("expectedPartSize"); QTest::addColumn("expectedChanged"); QTest::addColumn("storage"); QTest::addColumn("pimItem"); PimItem item; item.setCollectionId(Collection::retrieveByName(QStringLiteral("Col A")).id()); item.setMimeType(MimeType::retrieveByName(QStringLiteral("application/octet-stream"))); item.setSize(1); // this will not match reality during the test, but that does not matter, as // that's not the subject of this test QVERIFY(item.insert()); qint64 partId = -1; Part::List parts = Part::retrieveAll(); if (parts.isEmpty()) { partId = 0; } else { partId = parts.last().id() + 1; } // Order of these tests matters! { TestScenario::List scenarios; scenarios << FakeAkonadiServer::loginScenario() << TestScenario::create(5, TestScenario::ClientCmd, createCommand(item)) << TestScenario::create(5, TestScenario::ServerCmd, Protocol::StreamPayloadCommandPtr::create("PLD:DATA", Protocol::StreamPayloadCommand::MetaData)) << TestScenario::create(5, TestScenario::ClientCmd, Protocol::StreamPayloadResponsePtr::create("PLD:DATA", Protocol::PartMetaData("PLD:DATA", 3))) << TestScenario::create(5, TestScenario::ServerCmd, Protocol::StreamPayloadCommandPtr::create("PLD:DATA", Protocol::StreamPayloadCommand::Data)) << TestScenario::create(5, TestScenario::ClientCmd, Protocol::StreamPayloadResponsePtr::create("PLD:DATA", "123")) << TestScenario::create(5, TestScenario::ServerCmd, Protocol::ModifyItemsResponsePtr::create(item.id(), 1)); QTest::newRow("item 1, internal") << scenarios << QByteArray("PLD:DATA") << QByteArray("123") << QByteArray() << 3ll << true << Part::Internal << item; } { TestScenario::List scenarios; scenarios << FakeAkonadiServer::loginScenario() << TestScenario::create(5, TestScenario::ClientCmd, createCommand(item)) << TestScenario::create(5, TestScenario::ServerCmd, Protocol::StreamPayloadCommandPtr::create("PLD:DATA", Protocol::StreamPayloadCommand::MetaData)) << TestScenario::create(5, TestScenario::ClientCmd, Protocol::StreamPayloadResponsePtr::create("PLD:DATA", Protocol::PartMetaData("PLD:DATA", 9))) << TestScenario::create(5, TestScenario::ServerCmd, Protocol::StreamPayloadCommandPtr::create("PLD:DATA", Protocol::StreamPayloadCommand::Data, QStringLiteral("%1_r0").arg(partId))) << TestScenario::create(5, TestScenario::ClientCmd, Protocol::StreamPayloadResponsePtr::create("PLD:DATA")) << TestScenario::create(5, TestScenario::ServerCmd, Protocol::ModifyItemsResponsePtr::create(item.id(), 2)); QTest::newRow("item 1, change to external") << scenarios << QByteArray("PLD:DATA") << QByteArray("15_r0") << QByteArray("123456789") << 9ll << true << Part::External << item; } { TestScenario::List scenarios; scenarios << FakeAkonadiServer::loginScenario() << TestScenario::create(5, TestScenario::ClientCmd, createCommand(item)) << TestScenario::create(5, TestScenario::ServerCmd, Protocol::StreamPayloadCommandPtr::create("PLD:DATA", Protocol::StreamPayloadCommand::MetaData)) << TestScenario::create(5, TestScenario::ClientCmd, Protocol::StreamPayloadResponsePtr::create("PLD:DATA", Protocol::PartMetaData("PLD:DATA", 9))) << TestScenario::create(5, TestScenario::ServerCmd, Protocol::StreamPayloadCommandPtr::create("PLD:DATA", Protocol::StreamPayloadCommand::Data, QStringLiteral("%1_r1").arg(partId))) << TestScenario::create(5, TestScenario::ClientCmd, Protocol::StreamPayloadResponsePtr::create("PLD:DATA")) << TestScenario::create(5, TestScenario::ServerCmd, Protocol::ModifyItemsResponsePtr::create(item.id(), 3)); QTest::newRow("item 1, update external") << scenarios << QByteArray("PLD:DATA") << QByteArray("15_r1") << QByteArray("987654321") << 9ll << true << Part::External << item; } { TestScenario::List scenarios; scenarios << FakeAkonadiServer::loginScenario() << TestScenario::create(5, TestScenario::ClientCmd, createCommand(item)) << TestScenario::create(5, TestScenario::ServerCmd, Protocol::StreamPayloadCommandPtr::create("PLD:DATA", Protocol::StreamPayloadCommand::MetaData)) << TestScenario::create(5, TestScenario::ClientCmd, Protocol::StreamPayloadResponsePtr::create("PLD:DATA", Protocol::PartMetaData("PLD:DATA", 9))) << TestScenario::create(5, TestScenario::ServerCmd, Protocol::StreamPayloadCommandPtr::create("PLD:DATA", Protocol::StreamPayloadCommand::Data, QStringLiteral("%1_r2").arg(partId))) << TestScenario::create(5, TestScenario::ClientCmd, Protocol::StreamPayloadResponsePtr::create("PLD:DATA")) << TestScenario::create(5, TestScenario::ServerCmd, Protocol::ModifyItemsResponsePtr::create(item.id(), 4)); QTest::newRow("item 1, external, no change") << scenarios << QByteArray("PLD:DATA") << QByteArray("15_r2") << QByteArray("987654321") << 9ll << false << Part::External << item; } { TestScenario::List scenarios; scenarios << FakeAkonadiServer::loginScenario() << TestScenario::create(5, TestScenario::ClientCmd, createCommand(item)) << TestScenario::create(5, TestScenario::ServerCmd, Protocol::StreamPayloadCommandPtr::create("PLD:DATA", Protocol::StreamPayloadCommand::MetaData)) << TestScenario::create(5, TestScenario::ClientCmd, Protocol::StreamPayloadResponsePtr::create("PLD:DATA", Protocol::PartMetaData("PLD:DATA", 4))) << TestScenario::create(5, TestScenario::ServerCmd, Protocol::StreamPayloadCommandPtr::create("PLD:DATA", Protocol::StreamPayloadCommand::Data)) << TestScenario::create(5, TestScenario::ClientCmd, Protocol::StreamPayloadResponsePtr::create("PLD:DATA", "1234")) << TestScenario::create(5, TestScenario::ServerCmd, Protocol::ModifyItemsResponsePtr::create(item.id(), 5)); QTest::newRow("item 1, change to internal") << scenarios << QByteArray("PLD:DATA") << QByteArray("1234") << QByteArray() << 4ll << true << Part::Internal << item; } { TestScenario::List scenarios; scenarios << FakeAkonadiServer::loginScenario() << TestScenario::create(5, TestScenario::ClientCmd, createCommand(item)) << TestScenario::create(5, TestScenario::ServerCmd, Protocol::StreamPayloadCommandPtr::create("PLD:DATA", Protocol::StreamPayloadCommand::MetaData)) << TestScenario::create(5, TestScenario::ClientCmd, Protocol::StreamPayloadResponsePtr::create("PLD:DATA", Protocol::PartMetaData("PLD:DATA", 4))) << TestScenario::create(5, TestScenario::ServerCmd, Protocol::StreamPayloadCommandPtr::create("PLD:DATA", Protocol::StreamPayloadCommand::Data)) << TestScenario::create(5, TestScenario::ClientCmd, Protocol::StreamPayloadResponsePtr::create("PLD:DATA", "1234")) << TestScenario::create(5, TestScenario::ServerCmd, Protocol::ModifyItemsResponsePtr::create(item.id(), 6)); QTest::newRow("item 1, internal, no change") << scenarios << QByteArray("PLD:DATA") << QByteArray("1234") << QByteArray() << 4ll << false << Part::Internal << item; } // Insert new item PimItem item2 = item; QVERIFY(item2.insert()); const QString foreignPath = FakeAkonadiServer::basePath() + QStringLiteral("/tmp/foreignPayloadFile"); { TestScenario::List scenarios; scenarios << FakeAkonadiServer::loginScenario() << TestScenario::create(5, TestScenario::ClientCmd, createCommand(item2)) << TestScenario::create(5, TestScenario::ServerCmd, Protocol::StreamPayloadCommandPtr::create("PLD:DATA", Protocol::StreamPayloadCommand::MetaData)) << TestScenario::create(5, TestScenario::ClientCmd, Protocol::StreamPayloadResponsePtr::create("PLD:DATA", Protocol::PartMetaData("PLD:DATA", 3, 0, Protocol::PartMetaData::Foreign))) << TestScenario::create(5, TestScenario::ServerCmd, Protocol::StreamPayloadCommandPtr::create("PLD:DATA", Protocol::StreamPayloadCommand::Data)) << TestScenario::create(5, TestScenario::ClientCmd, Protocol::StreamPayloadResponsePtr::create("PLD:DATA", foreignPath.toUtf8())) << TestScenario::create(5, TestScenario::ServerCmd, Protocol::ModifyItemsResponsePtr::create(item2.id(), 1)); QTest::newRow("item 2, new foreign part") << scenarios << QByteArray("PLD:DATA") << foreignPath.toUtf8() << QByteArray("123") << 3ll << false << Part::Foreign << item2; } } void testStreamer() { QFETCH(TestScenario::List, scenarios); QFETCH(QByteArray, expectedPartName); QFETCH(QByteArray, expectedPartData); QFETCH(QByteArray, expectedFileData); QFETCH(qint64, expectedPartSize); QFETCH(Part::Storage, storage); QFETCH(PimItem, pimItem); if (storage == Part::External) { // Create the payload file now, since don't have means to react // directly to the streaming command QFile file(ExternalPartStorage::resolveAbsolutePath(expectedPartData)); file.open(QIODevice::WriteOnly); file.write(expectedFileData); file.close(); } else if (storage == Part::Foreign) { // Create the foreign payload file QDir().mkpath(FakeAkonadiServer::basePath() + QStringLiteral("/tmp")); QFile file(QString::fromUtf8(expectedPartData)); file.open(QIODevice::WriteOnly); file.write(expectedFileData); file.close(); } FakeAkonadiServer::instance()->setScenarios(scenarios); FakeAkonadiServer::instance()->runTest(); PimItem item = PimItem::retrieveById(pimItem.id()); const QVector parts = item.parts(); QVERIFY(parts.count() == 1); const Part part = parts[0]; QCOMPARE(part.datasize(), expectedPartSize); QCOMPARE(part.storage(), storage); const QByteArray data = part.data(); if (storage == Part::External) { QCOMPARE(data, expectedPartData); QFile file(ExternalPartStorage::resolveAbsolutePath(data)); QVERIFY(file.exists()); QCOMPARE(file.size(), expectedPartSize); QVERIFY(file.open(QIODevice::ReadOnly)); const QByteArray fileData = file.readAll(); QCOMPARE(fileData, expectedFileData); // Make sure no previous versions are left behind in file_db_data const int revision = data.mid(data.indexOf("_r") + 2).toInt(); for (int i = 0; i < revision; ++i) { const QByteArray fileName = QByteArray::number(part.id()) + "_r" + QByteArray::number(i); const QString filePath = ExternalPartStorage::resolveAbsolutePath(fileName); - QVERIFY2(!QFile::exists(filePath), qPrintable(filePath)); + // TRY because the deletion happens in another thread + QTRY_VERIFY2(!QFile::exists(filePath), qPrintable(filePath)); } } else if (storage == Part::Foreign) { QCOMPARE(data, expectedPartData); QFile file(QString::fromUtf8(data)); QVERIFY(file.exists()); QCOMPARE(file.size(), expectedPartSize); QVERIFY(file.open(QIODevice::ReadOnly)); const QByteArray fileData = file.readAll(); QCOMPARE(fileData, expectedFileData); } else { QCOMPARE(data, expectedPartData); // Make sure nothing is left behind in file_db_data // TODO: we have no way of knowing what is the last revision for (int i = 0; i <= 100; ++i) { const QByteArray fileName = QByteArray::number(part.id()) + "_r" + QByteArray::number(i); const QString filePath = ExternalPartStorage::resolveAbsolutePath(fileName); - QVERIFY2(!QFile::exists(filePath), qPrintable(filePath)); + QTRY_VERIFY2(!QFile::exists(filePath), qPrintable(filePath)); } } } }; AKTEST_FAKESERVER_MAIN(PartStreamerTest) #include "partstreamertest.moc" diff --git a/cmake/modules/AkonadiMacros.cmake b/cmake/modules/AkonadiMacros.cmake index 524d815ad..e783a15a0 100644 --- a/cmake/modules/AkonadiMacros.cmake +++ b/cmake/modules/AkonadiMacros.cmake @@ -1,107 +1,108 @@ # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # Internal server macros function(akonadi_run_xsltproc) if (NOT XSLTPROC_EXECUTABLE) message(FATAL_ERROR "xsltproc executable not found but needed by AKONADI_RUN_XSLTPROC()") endif() set(options ) set(oneValueArgs XSL XML CLASSNAME BASENAME) - set(multiValueArgs ) + set(multiValueArgs DEPENDS) cmake_parse_arguments(XSLT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) if (NOT XSLT_XSL) message(FATAL_ERROR "Required argument XSL missing in AKONADI_RUN_XSLTPROC() call") endif() if (NOT XSLT_XML) message(FATAL_ERROR "Required argument XML missing in AKONADI_RUN_XSLTPROC() call") endif() if (NOT XSLT_BASENAME) message(FATAL_ERROR "Required argument BASENAME missing in AKONADI_RUN_XSLTPROC() call") endif() # Workaround xsltproc struggling with spaces in filepaths on Windows file(RELATIVE_PATH xsl_relpath ${CMAKE_CURRENT_BINARY_DIR} ${XSLT_XSL}) file(RELATIVE_PATH xml_relpath ${CMAKE_CURRENT_BINARY_DIR} ${XSLT_XML}) set(extra_params ) if (XSLT_CLASSNAME) set(extra_params --stringparam className ${XSLT_CLASSNAME}) endif() add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${XSLT_BASENAME}.h ${CMAKE_CURRENT_BINARY_DIR}/${XSLT_BASENAME}.cpp COMMAND ${XSLTPROC_EXECUTABLE} --output ${XSLT_BASENAME}.h --stringparam code header --stringparam fileName ${XSLT_BASENAME} ${extra_params} ${xsl_relpath} ${xml_relpath} COMMAND ${XSLTPROC_EXECUTABLE} --output ${XSLT_BASENAME}.cpp --stringparam code source --stringparam fileName ${XSLT_BASENAME} ${extra_params} ${xsl_relpath} ${xml_relpath} DEPENDS ${XSLT_XSL} ${XSLT_XML} + ${XSLT_DEPENDS} ) set_property(SOURCE ${CMAKE_CURRENT_BINARY_DIR}/${XSLT_BASENAME}.cpp ${CMAKE_CURRENT_BINARY_DIR}/${XSLT_BASENAME}.h PROPERTY SKIP_AUTOMOC TRUE ) endfunction() macro(akonadi_generate_schema _schemaXml _className _fileBaseName) if (NOT XSLTPROC_EXECUTABLE) message(FATAL_ERROR "xsltproc executable not found but needed by AKONADI_GENERATE_SCHEMA()") endif() akonadi_run_xsltproc( XSL ${Akonadi_SOURCE_DIR}/src/server/storage/schema.xsl XML ${_schemaXml} CLASSNAME ${_className} BASENAME ${_fileBaseName} ) endmacro() function(akonadi_add_xmllint_test) if (NOT XMLLINT_EXECUTABLE) message(FATAL_ERROR "xmllint executable not found but needed by AKONADI_ADD_XMLLINT_SCHEMA()") endif() set(options ) set(oneValueArgs XML XSD) set(multiValueArgs ) cmake_parse_arguments(TEST "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) file(RELATIVE_PATH xsd_relpath ${CMAKE_CURRENT_BINARY_DIR} ${TEST_XSD}) file(RELATIVE_PATH xml_relpath ${CMAKE_CURRENT_BINARY_DIR} ${TEST_XML}) add_test(${TEST_UNPARSED_ARGUMENTS} ${XMLLINT_EXECUTABLE} --noout --schema ${xsd_relpath} ${xml_relpath}) endfunction() diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt index db636c104..0f035e949 100644 --- a/src/server/CMakeLists.txt +++ b/src/server/CMakeLists.txt @@ -1,196 +1,198 @@ include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR}) include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}) if(MYSQLD_EXECUTABLE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DMYSQLD_EXECUTABLE=\"\\\"${MYSQLD_EXECUTABLE}\\\"\"") endif() if(POSTGRES_PATH) set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DPOSTGRES_PATH=\"\\\"${POSTGRES_PATH}\\\"\"") endif() ########### next target ############### set(AKONADI_DB_SCHEMA "${CMAKE_CURRENT_SOURCE_DIR}/storage/akonadidb.xml") akonadi_run_xsltproc( XSL ${CMAKE_CURRENT_SOURCE_DIR}/storage/entities.xsl XML ${AKONADI_DB_SCHEMA} BASENAME entities + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/storage/entities-source.xsl ${CMAKE_CURRENT_SOURCE_DIR}/storage/entities-header.xsl ) akonadi_run_xsltproc( XSL ${Akonadi_SOURCE_DIR}/src/server/storage/schema.xsl XML ${AKONADI_DB_SCHEMA} CLASSNAME AkonadiSchema BASENAME akonadischema + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/storage/schema-source.xsl ${CMAKE_CURRENT_SOURCE_DIR}/storage/schema-header.xsl ) akonadi_add_xmllint_test( akonadidb-xmllint XSD ${CMAKE_CURRENT_SOURCE_DIR}/storage/akonadidb.xsd XML ${AKONADI_DB_SCHEMA} ) akonadi_add_xmllint_test( akonadidbupdate-xmllint XSD ${CMAKE_CURRENT_SOURCE_DIR}/storage/akonadidb.xsd XML ${AKONADI_DB_SCHEMA} ) set(libakonadiserver_SRCS akonadi.cpp aggregatedfetchscope.cpp aklocalserver.cpp akthread.cpp commandcontext.cpp connection.cpp collectionscheduler.cpp dbusconnectionpool.cpp handler.cpp handlerhelper.cpp intervalcheck.cpp collectionreferencemanager.cpp handler/collectioncopyhandler.cpp handler/collectioncreatehandler.cpp handler/collectiondeletehandler.cpp handler/collectionfetchhandler.cpp handler/collectionmodifyhandler.cpp handler/collectionmovehandler.cpp handler/collectionstatsfetchhandler.cpp handler/itemcopyhandler.cpp handler/itemcreatehandler.cpp handler/itemdeletehandler.cpp handler/itemfetchhandler.cpp handler/itemfetchhelper.cpp handler/itemlinkhandler.cpp handler/itemmodifyhandler.cpp handler/itemmovehandler.cpp handler/loginhandler.cpp handler/logouthandler.cpp handler/relationfetchhandler.cpp handler/relationmodifyhandler.cpp handler/relationremovehandler.cpp handler/resourceselecthandler.cpp handler/searchhandler.cpp handler/searchhelper.cpp handler/searchcreatehandler.cpp handler/searchresulthandler.cpp handler/tagcreatehandler.cpp handler/tagdeletehandler.cpp handler/tagfetchhandler.cpp handler/tagfetchhelper.cpp handler/tagmodifyhandler.cpp handler/transactionhandler.cpp search/agentsearchengine.cpp search/agentsearchinstance.cpp search/searchtaskmanager.cpp search/searchrequest.cpp search/searchmanager.cpp storage/collectionqueryhelper.cpp storage/collectionstatistics.cpp storage/entity.cpp ${CMAKE_CURRENT_BINARY_DIR}/entities.cpp ${CMAKE_CURRENT_BINARY_DIR}/akonadischema.cpp storage/datastore.cpp storage/dbconfig.cpp storage/dbconfigmysql.cpp storage/dbconfigpostgresql.cpp storage/dbconfigsqlite.cpp storage/dbexception.cpp storage/dbinitializer.cpp storage/dbinitializer_p.cpp storage/dbintrospector.cpp storage/dbintrospector_impl.cpp storage/dbupdater.cpp storage/dbtype.cpp storage/itemqueryhelper.cpp storage/itemretriever.cpp storage/itemretrievalmanager.cpp storage/itemretrievaljob.cpp storage/notificationcollector.cpp storage/parthelper.cpp storage/parttypehelper.cpp storage/query.cpp storage/querybuilder.cpp storage/querycache.cpp storage/queryhelper.cpp storage/schematypes.cpp storage/tagqueryhelper.cpp storage/transaction.cpp storage/parthelper.cpp storage/partstreamer.cpp storage/storagedebugger.cpp tracer.cpp utils.cpp dbustracer.cpp filetracer.cpp notificationmanager.cpp notificationsubscriber.cpp resourcemanager.cpp cachecleaner.cpp debuginterface.cpp preprocessorinstance.cpp preprocessormanager.cpp storagejanitor.cpp ) set(akonadiserver_SRCS main.cpp ) ecm_qt_declare_logging_category(akonadiserver_SRCS HEADER akonadiserver_debug.h IDENTIFIER AKONADISERVER_LOG CATEGORY_NAME org.kde.pim.akonadiserver) ecm_qt_declare_logging_category(akonadiserver_SRCS HEADER akonadiserver_search_debug.h IDENTIFIER AKONADISERVER_SEARCH_LOG CATEGORY_NAME org.kde.pim.akonadiserver.search) qt5_generate_dbus_interface(debuginterface.h org.freedesktop.Akonadi.DebugInterface.xml) qt5_add_dbus_adaptor(libakonadiserver_SRCS ${Akonadi_SOURCE_DIR}/src/interfaces/org.freedesktop.Akonadi.TracerNotification.xml dbustracer.h Akonadi::Server::DBusTracer) qt5_add_dbus_adaptor(libakonadiserver_SRCS ${Akonadi_SOURCE_DIR}/src/interfaces/org.freedesktop.Akonadi.Tracer.xml tracer.h Akonadi::Server::Tracer) qt5_add_dbus_adaptor(libakonadiserver_SRCS ${Akonadi_SOURCE_DIR}/src/interfaces/org.freedesktop.Akonadi.Server.xml akonadi.h Akonadi::Server::AkonadiServer) qt5_add_dbus_adaptor(libakonadiserver_SRCS ${Akonadi_SOURCE_DIR}/src/interfaces/org.freedesktop.Akonadi.StorageDebugger.xml storage/storagedebugger.h Akonadi::Server::StorageDebugger) qt5_add_dbus_adaptor(libakonadiserver_SRCS ${CMAKE_CURRENT_BINARY_DIR}/org.freedesktop.Akonadi.DebugInterface.xml debuginterface.h Akonadi::Server::DebugInterface) qt5_add_dbus_adaptor(libakonadiserver_SRCS ${Akonadi_SOURCE_DIR}/src/interfaces/org.freedesktop.Akonadi.ResourceManager.xml resourcemanager.h Akonadi::Server::ResourceManager) qt5_add_dbus_adaptor(libakonadiserver_SRCS ${Akonadi_SOURCE_DIR}/src/interfaces/org.freedesktop.Akonadi.PreprocessorManager.xml preprocessormanager.h Akonadi::Server::PreprocessorManager) qt5_add_dbus_interface(libakonadiserver_SRCS ${Akonadi_SOURCE_DIR}/src/interfaces/org.freedesktop.Akonadi.AgentManager.xml agentmanagerinterface) qt5_add_dbus_interface(libakonadiserver_SRCS ${Akonadi_SOURCE_DIR}/src/interfaces/org.freedesktop.Akonadi.Resource.xml resourceinterface) qt5_add_dbus_interface(libakonadiserver_SRCS ${Akonadi_SOURCE_DIR}/src/interfaces/org.freedesktop.Akonadi.Preprocessor.xml preprocessorinterface) qt5_add_dbus_interface(libakonadiserver_SRCS ${Akonadi_SOURCE_DIR}/src/interfaces/org.freedesktop.Akonadi.Agent.Control.xml agentcontrolinterface) qt5_add_dbus_interface(libakonadiserver_SRCS ${Akonadi_SOURCE_DIR}/src/interfaces/org.freedesktop.Akonadi.Agent.Search.xml agentsearchinterface) qt5_add_resources(libakonadiserver_SRCS storage/akonadidb.qrc) add_library(libakonadiserver STATIC ${libakonadiserver_SRCS}) set_target_properties(libakonadiserver PROPERTIES OUTPUT_NAME akonadiserver) target_link_libraries(libakonadiserver akonadi_shared KF5AkonadiPrivate Qt5::Core Qt5::Network Qt5::Sql Qt5::DBus Qt5::Xml ) add_executable(akonadiserver ${akonadiserver_SRCS}) set_target_properties(akonadiserver PROPERTIES OUTPUT_NAME akonadiserver) target_link_libraries(akonadiserver libakonadiserver KF5::CoreAddons ) install(TARGETS akonadiserver ${KF5_INSTALL_TARGETS_DEFAULT_ARGS} ) install(FILES storage/mysql-global.conf storage/mysql-global-mobile.conf DESTINATION ${CONFIG_INSTALL_DIR}/akonadi ) install(FILES search/abstractsearchplugin.h DESTINATION ${KF5_INCLUDE_INSTALL_DIR}/akonadi ) ## DBus XML files install(FILES ${CMAKE_CURRENT_BINARY_DIR}/org.freedesktop.Akonadi.DebugInterface.xml DESTINATION ${AKONADI_DBUS_INTERFACES_INSTALL_DIR} ) diff --git a/src/server/storage/entities-source.xsl b/src/server/storage/entities-source.xsl index faf078943..05bbf4ceb 100644 --- a/src/server/storage/entities-source.xsl +++ b/src/server/storage/entities-source.xsl @@ -1,786 +1,772 @@ Table // private class class ::Private : public QSharedData { public: Private() : QSharedData() , (0) , () , () // on non-wince, QDateTime is one int , () , (0) , (false) , (0) , _changed(false) {} qint64 ; QString ; QByteArray ; // on non-wince, QDateTime is one int QDateTime ; int ; bool : 1; Tristate ; ; bool _changed : 1; static void addToCache(const &entry); // cache static QAtomicInt cacheEnabled; static QMutex cacheMutex; static QHash<qint64, > idCache; static QHash<, > nameCache; }; // static members QAtomicInt ::Private::cacheEnabled(0); QMutex ::Private::cacheMutex; QHash<qint64, > ::Private::idCache; QHash<, > ::Private::nameCache; void ::Private::addToCache(const &entry) { Q_ASSERT(cacheEnabled); Q_UNUSED(entry); QMutexLocker lock(&cacheMutex); idCache.insert(entry.id(), entry); nameCache.insert(entry.ns() + QLatin1Char(':') + entry.name(), entry); nameCache.insert(entry.name(), entry); } // constructor ::() : Entity(), d(new Private) { } ::( , ) : Entity(), d(new Private) { d-> = ; d->_changed = true; } ::( , ) : Entity(id), d(new Private) { d-> = ; d->_changed = true; } ::(const &other) : Entity(other), d(other.d) { } // destructor ::~() {} // assignment operator & ::operator=(const &other) { if (this != &other) { d = other.d; setId(other.id()); } return *this; } // comparisson operator bool ::operator==(const &other) const { return id() == other.id(); } // accessor methods ::() const { return d->; } void :: { d-> = ; d->_changed = true; } // SQL table information QString ::tableName() { static const QString tableName = QStringLiteral(""); return tableName; } QStringList ::columnNames() { static const QStringList columns = { Column() , }; return columns; } QStringList ::fullColumnNames() { static const QStringList columns = { FullColumnName() , }; return columns; } QString ::Column() { static const QString column = QStringLiteral(""); return column; } QString ::FullColumnName() { static const QString column = QStringLiteral("."); return column; } // count records int ::count(const QString &column, const QVariant &value) { return Entity::count<>(column, value); } // check existence bool ::exists(qint64 id) { if (Private::cacheEnabled) { QMutexLocker lock(&Private::cacheMutex); if (Private::idCache.contains(id)) { return true; } } return count(idColumn(), id) > 0; } bool ::exists(const &name) { if (Private::cacheEnabled) { QMutexLocker lock(&Private::cacheMutex); if (Private::nameCache.contains(name)) { return true; } } return count(nameColumn(), name) > 0; } // result extraction QVector<> ::extractResult(QSqlQuery &query) { QVector<> rv; if (query.driver()->hasFeature(QSqlDriver::QuerySize)) { rv.reserve(query.size()); } while (query.next()) { rv.append(( (query.isNull() ? () : Utils::variantToString(query.value()) static_cast<>(query.value().value<int>()) query.value().value<>() ), )); } query.finish(); return rv; } // data retrieval ::retrieveById(qint64 id) { id idCache } ::retrieveByName(const &name) { name nameCache } ::retrieveByNameOrCreate(const &name) { - static QMutex lock; - auto rv = retrieveByName(name); - if (rv.isValid()) { - return rv; - } + static QMutex lock; + QMutexLocker locker(&lock); + auto rv = retrieveByName(name); + if (rv.isValid()) { + return rv; + } - if (lock.tryLock()) { - rv.setName(name); - if (!rv.insert()) { - lock.unlock(); - return (); - } + rv.setName(name); + if (!rv.insert()) { + return (); + } - if (Private::cacheEnabled) { - Private::addToCache(rv); - } - lock.unlock(); - return rv; - } - - lock.lock(); - lock.unlock(); - return retrieveByName(name); + if (Private::cacheEnabled) { + Private::addToCache(rv); + } + return rv; } PartType PartType::retrieveByFQName(const QString &ns, const QString &name) { const QString fqname = ns + QLatin1Char(':') + name; ns name fqname nameCache } PartType PartType::retrieveByFQNameOrCreate(const QString &ns, const QString &name) { - static QMutex lock; - PartType rv = retrieveByFQName(ns, name); - if (rv.isValid()) { - return rv; - } - - if (lock.tryLock()) { - rv.setNs(ns); - rv.setName(name); - if (!rv.insert()) { - lock.unlock(); - return PartType(); - } - - if (Private::cacheEnabled) { - Private::addToCache(rv); - } - lock.unlock(); - return rv; - } - - lock.lock(); - lock.unlock(); - return retrieveByFQName(ns, name); + static QMutex lock; + QMutexLocker locker(&lock); + PartType rv = retrieveByFQName(ns, name); + if (rv.isValid()) { + return rv; + } + + rv.setNs(ns); + rv.setName(name); + if (!rv.insert()) { + return PartType(); + } + + if (Private::cacheEnabled) { + Private::addToCache(rv); + } + return rv; } QVector<> ::retrieveAll() { QSqlDatabase db = DataStore::self()->database(); if (!db.isOpen()) { return {}; } QueryBuilder qb(tableName(), QueryBuilder::Select); qb.addColumns(columnNames()); if (!qb.exec()) { qCWarning(AKONADISERVER_LOG) << "Error during selection of all records from table" << tableName() << qb.query().lastError().text() << qb.query().lastQuery(); return {}; } return extractResult(qb.query()); } QVector<> ::retrieveFiltered(const QString &key, const QVariant &value) { QSqlDatabase db = DataStore::self()->database(); if (!db.isOpen()) { return {}; } SelectQueryBuilder<> qb; if (value.isNull()) { qb.addValueCondition(key, Query::Is, QVariant()); } else { qb.addValueCondition(key, Query::Equals, value); } if (!qb.exec()) { qCWarning(AKONADISERVER_LOG) << "Error during selection of records from table" << tableName() << "filtered by" << key << "=" << value << qb.query().lastError().text(); return {}; } return qb.result(); } // data retrieval for referenced tables ::() const { return ::retrieveById(()); } void :: set (const &value) { d-> = value.id(); d->_changed = true; } // data retrieval for inverse referenced tables QVector<> ::() const { return ::retrieveFiltered(::Column(), id()); } Relation // data retrieval for n:m relations QVector<> ::s() const { QSqlDatabase db = DataStore::self()->database(); if (!db.isOpen()) { return {}; } QueryBuilder qb(::tableName(), QueryBuilder::Select); static const QStringList columns = { ::FullColumnName() , }; qb.addColumns(columns); qb.addJoin(QueryBuilder::InnerJoin, ::tableName(), ::rightFullColumnName(), ::FullColumnName()); qb.addValueCondition(::leftFullColumnName(), Query::Equals, id()); if (!qb.exec()) { qCWarning(AKONADISERVER_LOG) << "Error during selection of records from table Relation" << qb.query().lastError().text(); return {}; } return ::extractResult(qb.query()); } // manipulate n:m relations bool ::relatesTo(const &value) const { return Entity::relatesTo<>(id(), value.id()); } bool ::relatesTo(qint64 leftId, qint64 rightId) { return Entity::relatesTo<>(leftId, rightId); } bool ::add(const &value) const { return Entity::addToRelation<>(id(), value.id()); } bool ::add(qint64 leftId, qint64 rightId) { return Entity::addToRelation<>(leftId, rightId); } bool ::remove(const &value) const { return Entity::removeFromRelation<>(id(), value.id()); } bool ::remove(qint64 leftId, qint64 rightId) { return Entity::removeFromRelation<>(leftId, rightId); } bool ::clears() const { return Entity::clearRelation<>(id()); } bool ::clears(qint64 id) { return Entity::clearRelation<>(id); } #ifndef QT_NO_DEBUG_STREAM // debug stream operator QDebug &operator<<(QDebug &d, const &entity) { d << "[: " << " = " << static_cast<int>(entity.()) entity.() << ", " << "]"; return d; } #endif // inserting new data bool ::insert(qint64* insertId) { QSqlDatabase db = DataStore::self()->database(); if (!db.isOpen()) { return false; } QueryBuilder qb(tableName(), QueryBuilder::Insert); qb.setIdentificationColumn(QLatin1String("")); if (d->_changed && d-> > 0) { qb.setColumnValue( Column(), this->() ); } if (d->_changed) { qb.setColumnValue(Column(), static_cast<int>(this->())); qb.setColumnValue(Column(), this->()); } if (!qb.exec()) { qCWarning(AKONADISERVER_LOG) << "Error during insertion into table" << tableName() << qb.query().lastError().text(); return false; } setId(qb.insertId()); if (insertId) { *insertId = id(); } return true; } bool ::hasPendingChanges() const { return false || d->_changed ; } // update existing data bool ::update() { invalidateCache(); QSqlDatabase db = DataStore::self()->database(); if (!db.isOpen()) { return false; } QueryBuilder qb(tableName(), QueryBuilder::Update); if (d->_changed) { if (d-> <= 0) { qb.setColumnValue(Column(), QVariant()); } else { qb.setColumnValue(Column(), static_cast<int>(this->())); qb.setColumnValue(Column(), this->()); } } qb.addValueCondition(idColumn(), Query::Equals, id()); if (!qb.exec()) { qCWarning(AKONADISERVER_LOG) << "Error during updating record with id" << id() << " in table" << tableName() << qb.query().lastError().text(); return false; } return true; } // delete records bool ::remove(const QString &column, const QVariant &value) { invalidateCompleteCache(); return Entity::remove<>(column, value); } bool ::remove() { invalidateCache(); return Entity::remove<>(idColumn(), id()); } bool ::remove(qint64 id) { return remove(idColumn(), id); } // cache stuff void ::invalidateCache() const { if (Private::cacheEnabled) { QMutexLocker lock(&Private::cacheMutex); Private::idCache.remove(id()); Private::nameCache.remove(ns() + QLatin1Char(':') + name()); Private::nameCache.remove(name()); } } void ::invalidateCompleteCache() { if (Private::cacheEnabled) { QMutexLocker lock(&Private::cacheMutex); Private::idCache.clear(); Private::nameCache.clear(); } } void ::enableCache(bool enable) { Private::cacheEnabled = enable; } Relation Relation // SQL table information QString ::tableName() { static const QString table = QStringLiteral("" ); return table; } QString ::leftColumn() { static const QString column = QStringLiteral("_"); return column; } QString ::leftFullColumnName() { static const QString column = QStringLiteral("._"); return column; } QString ::rightColumn() { static const QString column = QStringLiteral("_"); return column; } QString ::rightFullColumnName() { static const QString column = QStringLiteral("._"); return column; }