From 03687c1d0288544b12db64bc7d828d899986075c Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 23 Sep 2021 11:22:04 +0200 Subject: [PATCH] QmlDesigner: Add minimal updates Exported type names are synchronized so that old type names are removed. Task-number: QDS-5130 Task-number: QDS-5126 Change-Id: I6e6482170c8197f37f60a57bdfb7f1b450001b4b Reviewed-by: Qt CI Bot Reviewed-by: Thomas Hartmann --- .../designercore/include/projectstorageids.h | 6 +- .../projectstorage/projectstorage.h | 190 +++++++++++++----- .../projectstoragepathwatchertypes.h | 1 + .../projectstorage/projectstoragetypes.h | 43 +++- .../projectstorage/projectstorageupdater.cpp | 2 + .../projectstorage/projectstorageupdater.h | 2 + tests/unit/unittest/projectstorage-test.cpp | 54 +++++ 7 files changed, 240 insertions(+), 58 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/include/projectstorageids.h b/src/plugins/qmldesigner/designercore/include/projectstorageids.h index 5bb03b90109..76f547eeb15 100644 --- a/src/plugins/qmldesigner/designercore/include/projectstorageids.h +++ b/src/plugins/qmldesigner/designercore/include/projectstorageids.h @@ -86,7 +86,8 @@ enum class BasicIdType { Module, ProjectPartId, Import, - ImportedTypeName + ImportedTypeName, + ExportedTypeName }; using TypeId = BasicId; @@ -122,4 +123,7 @@ using ImportIds = std::vector; using ImportedTypeNameId = BasicId; using ImportedTypeNameIds = std::vector; +using ExportedTypeNameId = BasicId; +using ExportedTypeNameIds = std::vector; + } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h index a3b46b301c2..ece14cf7206 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorage.h @@ -369,14 +369,20 @@ private: std::vector &insertedAliasPropertyDeclarations, std::vector &updatedAliasPropertyDeclarations) { + Storage::ExportedTypes exportedTypes; + exportedTypes.reserve(types.size() * 3); for (auto &&type : types) { if (!type.sourceId) throw TypeHasInvalidSourceId{}; - updatedTypeIds.push_back(declareType(type)); + TypeId typeId = declareType(type); + updatedTypeIds.push_back(typeId); + extractExportedTypes(typeId, type, exportedTypes); } + synchronizeExportedTypes(updatedTypeIds, exportedTypes); + for (auto &&type : types) syncPrototypes(type); @@ -745,37 +751,78 @@ private: updateAliasPropertyDeclarationValues(updatedAliasPropertyDeclarations); } - void upsertExportedType(ModuleId moduleId, - Utils::SmallStringView name, - TypeId typeId, - Storage::Version version) + void synchronizeExportedTypes(const TypeIds &typeIds, Storage::ExportedTypes &exportedTypes) { - if (version) { - upsertTypeNamesWithVersionStatement.write(&moduleId, - name, - static_cast( - Storage::TypeNameKind::Exported), - version.major.value, - version.minor.value, - &typeId); + std::sort(exportedTypes.begin(), exportedTypes.end(), [](auto &&first, auto &&second) { + return std::tie(first.moduleId, first.name, first.version) + < std::tie(second.moduleId, second.name, second.version); + }); - } else if (version.major) { - upsertTypeNamesWithMajorVersionStatement.write(&moduleId, - name, - static_cast( - Storage::TypeNameKind::Exported), - version.major.value, - &typeId); - } else { - upsertTypeNamesWithoutVersionStatement.write( - &moduleId, name, static_cast(Storage::TypeNameKind::Exported), &typeId); - } + auto range = selectExportedTypesForTypeIdStatement.template range( + const_cast(static_cast(typeIds.data())), + static_cast(typeIds.size())); + + auto compareKey = [](const Storage::ExportedTypeView &view, + const Storage::ExportedType &type) -> long long { + auto moduleIdDifference = view.moduleId.id - type.moduleId.id; + if (moduleIdDifference != 0) + return moduleIdDifference; + + auto nameDifference = Sqlite::compare(view.name, type.name); + if (nameDifference != 0) + return nameDifference; + + auto versionDifference = view.version.major.value - type.version.major.value; + if (versionDifference != 0) + return versionDifference; + + return view.version.minor.value - type.version.minor.value; + }; + + auto insert = [&](const Storage::ExportedType &type) { + if (type.version) { + upsertExportedTypeNamesWithVersionStatement.write(&type.moduleId, + type.name, + static_cast( + Storage::TypeNameKind::Exported), + type.version.major.value, + type.version.minor.value, + &type.typeId); + + } else if (type.version.major) { + upsertExportedTypeNamesWithMajorVersionStatement + .write(&type.moduleId, + type.name, + static_cast(Storage::TypeNameKind::Exported), + type.version.major.value, + &type.typeId); + } else { + upsertExportedTypeNamesWithoutVersionStatement + .write(&type.moduleId, + type.name, + static_cast(Storage::TypeNameKind::Exported), + &type.typeId); + } + }; + + auto update = [&](const Storage::ExportedTypeView &view, const Storage::ExportedType &type) { + if (view.typeId != type.typeId) + updateExportedTypeNameTypeIdStatement.write(&view.exportedTypeNameId, &type.typeId); + }; + + auto remove = [&](const Storage::ExportedTypeView &view) { + deleteExportedTypeNameStatement.write(&view.exportedTypeNameId); + }; + + Sqlite::insertUpdateDelete(range, exportedTypes, compareKey, insert, update, remove); } void upsertNativeType(ModuleId moduleId, Utils::SmallStringView name, TypeId typeId) { - upsertTypeNamesWithoutVersionStatement - .write(&moduleId, name, static_cast(Storage::TypeNameKind::Native), &typeId); + upsertExportedTypeNameStatement.write(&moduleId, + name, + static_cast(Storage::TypeNameKind::Native), + &typeId); } void synchronizePropertyDeclarationsInsertAlias( @@ -1205,30 +1252,50 @@ private: Sqlite::insertUpdateDelete(range, enumerationDeclarations, compareKey, insert, update, remove); } + void extractExportedTypes(TypeId typeId, + const Storage::Type &type, + Storage::ExportedTypes &exportedTypes) + { + for (const auto &exportedType : type.exportedTypes) + exportedTypes.emplace_back(exportedType.name, exportedType.version, typeId, type.moduleId); + } + + struct ModuleAndTypeId + { + ModuleAndTypeId() = default; + ModuleAndTypeId(int moduleId, long long typeId) + : moduleId{moduleId} + , typeId{typeId} + {} + + ModuleId moduleId; + TypeId typeId; + }; + TypeId declareType(Storage::Type &type) { if (type.module.name.isEmpty() && type.typeName.isEmpty()) { - type.typeId = selectTypeIdBySourceIdStatement.template value(&type.sourceId); + auto [moduleId, typeId] = selectModuleAndTypeIdBySourceIdStatement + .template value(&type.sourceId); + type.typeId = typeId; + type.moduleId = moduleId; return type.typeId; } - ModuleId moduleId = fetchModuleIdUnguarded(type.module); + type.moduleId = fetchModuleIdUnguarded(type.module); - if (!moduleId) + if (!type.moduleId) throw ModuleDoesNotExists{}; - type.typeId = upsertTypeStatement.template value(&moduleId, + type.typeId = upsertTypeStatement.template value(&type.moduleId, type.typeName, static_cast(type.accessSemantics), &type.sourceId); if (!type.typeId) - type.typeId = selectTypeIdByModuleIdAndNameStatement.template value(&moduleId, + type.typeId = selectTypeIdByModuleIdAndNameStatement.template value(&type.moduleId, type.typeName); - upsertNativeType(moduleId, type.typeName, type.typeId); - - for (const auto &exportedType : type.exportedTypes) - upsertExportedType(moduleId, exportedType.name, type.typeId, exportedType.version); + upsertNativeType(type.moduleId, type.typeName, type.typeId); return type.typeId; } @@ -1275,6 +1342,9 @@ private: void syncPrototypes(Storage::Type &type) { + if (type.changeLevel == Storage::ChangeLevel::Minimal) + return; + if (Utils::visit([](auto &&typeName) -> bool { return typeName.name.isEmpty(); }, type.prototype)) { updatePrototypeStatement.write(&type.typeId, Sqlite::NullValue{}, Sqlite::NullValue{}); @@ -1642,7 +1712,7 @@ private: Sqlite::Enforment::Deferred); auto &nameColumn = table.addColumn("name"); auto &kindColumn = table.addColumn("kind"); - table.addColumn("typeId"); + auto &typeIdColumn = table.addColumn("typeId"); auto &majorVersionColumn = table.addColumn("majorVersion"); auto &minorVersionColumn = table.addColumn("minorVersion"); @@ -1654,6 +1724,8 @@ private: {moduleIdColumn, nameColumn, kindColumn, majorVersionColumn, minorVersionColumn}, "majorVersion IS NOT NULL AND minorVersion IS NOT NULL"); + table.addIndex({typeIdColumn}, "kind=1"); + table.initialize(database); } @@ -1828,23 +1900,6 @@ public: " FROM propertyDeclarations JOIN typeSelection USING(typeId) " " WHERE name=?2 ORDER BY level LIMIT 1", database}; - WriteStatement upsertTypeNamesWithVersionStatement{ - "INSERT INTO exportedTypeNames(moduleId, name, kind, majorVersion, minorVersion, typeId) " - "VALUES(?1, ?2, ?3, ?4, ?5, ?6) ON CONFLICT DO UPDATE SET typeId=excluded.typeId, " - "majorVersion=excluded.majorVersion, minorVersion=excluded.minorVersion WHERE typeId IS " - "NOT excluded.typeId OR majorVersion IS NOT excluded.majorVersion OR minorVersion IS NOT " - "excluded.minorVersion", - database}; - WriteStatement upsertTypeNamesWithMajorVersionStatement{ - "INSERT INTO exportedTypeNames(moduleId, name, kind, majorVersion, typeId) " - "VALUES(?1, ?2, ?3, ?4, ?5) ON CONFLICT DO UPDATE SET typeId=excluded.typeId, " - "majorVersion=excluded.majorVersion WHERE typeId IS NOT excluded.typeId OR majorVersion IS " - "NOT excluded.majorVersion", - database}; - WriteStatement upsertTypeNamesWithoutVersionStatement{ - "INSERT INTO exportedTypeNames(moduleId, name, kind, typeId) VALUES(?1, ?2, ?3, ?4) ON " - "CONFLICT DO UPDATE SET typeId=excluded.typeId WHERE typeId IS NOT excluded.typeId", - database}; mutable ReadStatement<1> selectPrototypeIdsStatement{ "WITH RECURSIVE " " typeSelection(typeId, level) AS (" @@ -2188,8 +2243,8 @@ public: WriteStatement deleteFileStatusStatement{"DELETE FROM fileStatuses WHERE sourceId=?1", database}; WriteStatement updateFileStatusStatement{ "UPDATE fileStatuses SET size=?2, lastModified=?3 WHERE sourceId=?1", database}; - ReadStatement<1> selectTypeIdBySourceIdStatement{"SELECT typeId FROM types WHERE sourceId=?", - database}; + ReadStatement<2> selectModuleAndTypeIdBySourceIdStatement{ + "SELECT moduleId, typeId FROM types WHERE sourceId=?", database}; mutable ReadStatement<1> selectImportedTypeNameIdStatement{ "SELECT importedTypeNameId FROM importedTypeNames WHERE kind=?1 AND importOrSourceId=?2 " "AND name=?3 LIMIT 1", @@ -2235,6 +2290,31 @@ public: database}; WriteStatement deleteAllSourcesStatement{"DELETE FROM sources", database}; WriteStatement deleteAllSourceContextsStatement{"DELETE FROM sourceContexts", database}; + mutable ReadStatement<6> selectExportedTypesForTypeIdStatement{ + "SELECT moduleId, name, ifnull(majorVersion, -1), ifnull(minorVersion, -1), typeId, " + "exportedTypeNameId FROM exportedTypeNames WHERE typeId IN carray(?1, ?2, 'int64') AND " + "kind=1 ORDER BY moduleId, name, majorVersion, minorVersion", + database}; + WriteStatement upsertExportedTypeNamesWithVersionStatement{ + "INSERT INTO exportedTypeNames(moduleId, name, kind, majorVersion, minorVersion, typeId) " + "VALUES(?1, ?2, ?3, ?4, ?5, ?6) ON CONFLICT DO UPDATE SET typeId=excluded.typeId", + database}; + WriteStatement upsertExportedTypeNamesWithMajorVersionStatement{ + "INSERT INTO exportedTypeNames(moduleId, name, kind, majorVersion, typeId) " + "VALUES(?1, ?2, ?3, ?4, ?5) ON CONFLICT DO UPDATE SET typeId=excluded.typeId", + database}; + WriteStatement upsertExportedTypeNamesWithoutVersionStatement{ + "INSERT INTO exportedTypeNames(moduleId, name, kind, typeId) VALUES(?1, ?2, ?3, ?4) ON " + "CONFLICT DO UPDATE SET typeId=excluded.typeId", + database}; + WriteStatement upsertExportedTypeNameStatement{ + "INSERT INTO exportedTypeNames(moduleId, name, kind, typeId) VALUES(?1, ?2, ?3, ?4) ON " + "CONFLICT DO UPDATE SET typeId=excluded.typeId WHERE typeId IS NOT excluded.typeId", + database}; + WriteStatement deleteExportedTypeNameStatement{ + "DELETE FROM exportedTypeNames WHERE exportedTypeNameId=?", database}; + WriteStatement updateExportedTypeNameTypeIdStatement{ + "UPDATE exportedTypeNames SET typeId=?2 WHERE exportedTypeNameId=?1", database}; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragepathwatchertypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragepathwatchertypes.h index 94470ebbb8f..b251e22a5b3 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragepathwatchertypes.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragepathwatchertypes.h @@ -27,6 +27,7 @@ #include "projectstorageids.h" +#include #include namespace QmlDesigner { diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h index 064cb6013b5..d090b16f366 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstoragetypes.h @@ -30,6 +30,7 @@ #include #include +#include #include namespace QmlDesigner::Storage { @@ -268,6 +269,13 @@ public: , version{version} {} + explicit ExportedType(Utils::SmallStringView name, Version version, TypeId typeId, ModuleId moduleId) + : name{name} + , version{version} + , typeId{typeId} + , moduleId{moduleId} + {} + explicit ExportedType(Utils::SmallStringView name, int majorVersion, int minorVersion) : name{name} , version{majorVersion, minorVersion} @@ -281,10 +289,37 @@ public: public: Utils::SmallString name; Storage::Version version; + TypeId typeId; + ModuleId moduleId; }; using ExportedTypes = std::vector; +class ExportedTypeView +{ +public: + explicit ExportedTypeView() = default; + explicit ExportedTypeView(int moduleId, + Utils::SmallStringView name, + int majorVersion, + int minorVersion, + int typeId, + long long exportedTypeNameId) + : name{name} + , version{majorVersion, minorVersion} + , typeId{typeId} + , moduleId{moduleId} + , exportedTypeNameId{exportedTypeNameId} + {} + +public: + Utils::SmallStringView name; + Storage::Version version; + TypeId typeId; + ModuleId moduleId; + ExportedTypeNameId exportedTypeNameId; +}; + class NativeType { public: @@ -591,6 +626,8 @@ public: PropertyDeclarationId aliasId; }; +enum class ChangeLevel { Full, Minimal }; + class Type { public: @@ -605,7 +642,7 @@ public: FunctionDeclarations functionDeclarations = {}, SignalDeclarations signalDeclarations = {}, EnumerationDeclarations enumerationDeclarations = {}, - TypeId typeId = TypeId{}) + ChangeLevel changeLevel = ChangeLevel::Full) : typeName{typeName} , prototype{std::move(prototype)} , exportedTypes{std::move(exportedTypes)} @@ -616,7 +653,7 @@ public: , module{std::move(module)} , accessSemantics{accessSemantics} , sourceId{sourceId} - , typeId{typeId} + , changeLevel{changeLevel} {} explicit Type(Utils::SmallStringView moduleName, @@ -684,6 +721,8 @@ public: TypeAccessSemantics accessSemantics = TypeAccessSemantics::Invalid; SourceId sourceId; TypeId typeId; + ModuleId moduleId; + ChangeLevel changeLevel = ChangeLevel::Full; }; using Types = std::vector; diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp index 672a3ece104..de16ccd2394 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.cpp @@ -103,6 +103,8 @@ void ProjectUpdater::update() std::move(fileStatuses)); } +void ProjectUpdater::pathsWithIdsChanged(const std::vector &idPaths) {} + void ProjectUpdater::parseTypeInfos(const QStringList &typeInfos, SourceContextId directoryId, Storage::Imports &imports, diff --git a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h index d60f1d297ea..29087946c71 100644 --- a/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h +++ b/src/plugins/qmldesigner/designercore/projectstorage/projectstorageupdater.h @@ -28,6 +28,7 @@ #include "filestatus.h" #include "nonlockingmutex.h" #include "projectstorageids.h" +#include "projectstoragepathwatchertypes.h" #include "projectstoragetypes.h" #include @@ -76,6 +77,7 @@ public: {} void update(); + void pathsWithIdsChanged(const std::vector &idPaths); private: enum class FileState { diff --git a/tests/unit/unittest/projectstorage-test.cpp b/tests/unit/unittest/projectstorage-test.cpp index dd570f4c4b6..a30d44889d4 100644 --- a/tests/unit/unittest/projectstorage-test.cpp +++ b/tests/unit/unittest/projectstorage-test.cpp @@ -122,6 +122,19 @@ MATCHER_P(IsExportedType, return type.name == name; } +MATCHER_P3(IsExportedType, + name, + majorVersion, + minorVersion, + std::string(negation ? "isn't " : "is ") + + PrintToString(Storage::ExportedType{name, + Storage::Version{majorVersion, minorVersion}})) +{ + const Storage::ExportedType &type = arg; + + return type.name == name && type.version == Storage::Version{majorVersion, minorVersion}; +} + MATCHER_P3(IsPropertyDeclaration, name, typeName, @@ -417,6 +430,7 @@ protected: protected: Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory}; + //Sqlite::Database database{TESTDATA_DIR "/aaaa.db", Sqlite::JournalMode::Wal}; QmlDesigner::ProjectStorage storage{database, database.isInitialized()}; QmlDesigner::SourcePathCache> sourcePathCache{ storage}; @@ -4497,4 +4511,44 @@ TEST_F(ProjectStorage, EnsureThatPrototypesForRemovedTypesAreNotAnymoreRelinked) ASSERT_NO_THROW(storage.synchronize({}, {}, {}, {sourceId1, sourceId2}, {})); } +TEST_F(ProjectStorage, MinimalUpdates) +{ + auto types = createTypes(); + storage.synchronize(modules, + imports, + types, + {sourceId1, sourceId2, moduleSourceId1, moduleSourceId2, moduleSourceId3}, + {}); + Storage::Type quickType{Storage::Module{"QtQuick"}, + "QQuickItem", + {}, + TypeAccessSemantics::Reference, + sourceId1, + {Storage::ExportedType{"Item", Storage::Version{2, 0}}}, + {}, + {}, + {}, + {}, + Storage::ChangeLevel::Minimal}; + + storage.synchronize({modules[1]}, {}, {quickType}, {moduleSourceId2}, {}); + + ASSERT_THAT(storage.fetchTypes(), + UnorderedElementsAre(AllOf(IsStorageType(Storage::Module{"Qml"}, + "QObject", + Storage::NativeType{}, + TypeAccessSemantics::Reference, + sourceId2), + Field(&Storage::Type::exportedTypes, + UnorderedElementsAre(IsExportedType("Object"), + IsExportedType("Obj")))), + AllOf(IsStorageType(Storage::Module{"QtQuick"}, + "QQuickItem", + Storage::NativeType{"QObject"}, + TypeAccessSemantics::Reference, + sourceId1), + Field(&Storage::Type::exportedTypes, + UnorderedElementsAre(IsExportedType("Item", 2, 0)))))); +} + } // namespace