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 <qt_ci_bot@qt-project.org>
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
Marco Bubke
2021-09-23 11:22:04 +02:00
parent b2a300dd60
commit 03687c1d02
7 changed files with 240 additions and 58 deletions

View File

@@ -86,7 +86,8 @@ enum class BasicIdType {
Module, Module,
ProjectPartId, ProjectPartId,
Import, Import,
ImportedTypeName ImportedTypeName,
ExportedTypeName
}; };
using TypeId = BasicId<BasicIdType::Type>; using TypeId = BasicId<BasicIdType::Type>;
@@ -122,4 +123,7 @@ using ImportIds = std::vector<ImportId>;
using ImportedTypeNameId = BasicId<BasicIdType::ImportedTypeName>; using ImportedTypeNameId = BasicId<BasicIdType::ImportedTypeName>;
using ImportedTypeNameIds = std::vector<ImportedTypeNameId>; using ImportedTypeNameIds = std::vector<ImportedTypeNameId>;
using ExportedTypeNameId = BasicId<BasicIdType::ExportedTypeName>;
using ExportedTypeNameIds = std::vector<ExportedTypeNameId>;
} // namespace QmlDesigner } // namespace QmlDesigner

View File

@@ -369,14 +369,20 @@ private:
std::vector<AliasPropertyDeclaration> &insertedAliasPropertyDeclarations, std::vector<AliasPropertyDeclaration> &insertedAliasPropertyDeclarations,
std::vector<AliasPropertyDeclaration> &updatedAliasPropertyDeclarations) std::vector<AliasPropertyDeclaration> &updatedAliasPropertyDeclarations)
{ {
Storage::ExportedTypes exportedTypes;
exportedTypes.reserve(types.size() * 3);
for (auto &&type : types) { for (auto &&type : types) {
if (!type.sourceId) if (!type.sourceId)
throw TypeHasInvalidSourceId{}; 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) for (auto &&type : types)
syncPrototypes(type); syncPrototypes(type);
@@ -745,37 +751,78 @@ private:
updateAliasPropertyDeclarationValues(updatedAliasPropertyDeclarations); updateAliasPropertyDeclarationValues(updatedAliasPropertyDeclarations);
} }
void upsertExportedType(ModuleId moduleId, void synchronizeExportedTypes(const TypeIds &typeIds, Storage::ExportedTypes &exportedTypes)
Utils::SmallStringView name,
TypeId typeId,
Storage::Version version)
{ {
if (version) { std::sort(exportedTypes.begin(), exportedTypes.end(), [](auto &&first, auto &&second) {
upsertTypeNamesWithVersionStatement.write(&moduleId, return std::tie(first.moduleId, first.name, first.version)
name, < std::tie(second.moduleId, second.name, second.version);
static_cast<long long>( });
Storage::TypeNameKind::Exported),
version.major.value,
version.minor.value,
&typeId);
} else if (version.major) { auto range = selectExportedTypesForTypeIdStatement.template range<Storage::ExportedTypeView>(
upsertTypeNamesWithMajorVersionStatement.write(&moduleId, const_cast<void *>(static_cast<const void *>(typeIds.data())),
name, static_cast<long long>(typeIds.size()));
static_cast<long long>(
Storage::TypeNameKind::Exported), auto compareKey = [](const Storage::ExportedTypeView &view,
version.major.value, const Storage::ExportedType &type) -> long long {
&typeId); auto moduleIdDifference = view.moduleId.id - type.moduleId.id;
} else { if (moduleIdDifference != 0)
upsertTypeNamesWithoutVersionStatement.write( return moduleIdDifference;
&moduleId, name, static_cast<long long>(Storage::TypeNameKind::Exported), &typeId);
} 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<long long>(
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<long long>(Storage::TypeNameKind::Exported),
type.version.major.value,
&type.typeId);
} else {
upsertExportedTypeNamesWithoutVersionStatement
.write(&type.moduleId,
type.name,
static_cast<long long>(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) void upsertNativeType(ModuleId moduleId, Utils::SmallStringView name, TypeId typeId)
{ {
upsertTypeNamesWithoutVersionStatement upsertExportedTypeNameStatement.write(&moduleId,
.write(&moduleId, name, static_cast<long long>(Storage::TypeNameKind::Native), &typeId); name,
static_cast<long long>(Storage::TypeNameKind::Native),
&typeId);
} }
void synchronizePropertyDeclarationsInsertAlias( void synchronizePropertyDeclarationsInsertAlias(
@@ -1205,30 +1252,50 @@ private:
Sqlite::insertUpdateDelete(range, enumerationDeclarations, compareKey, insert, update, remove); 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) TypeId declareType(Storage::Type &type)
{ {
if (type.module.name.isEmpty() && type.typeName.isEmpty()) { if (type.module.name.isEmpty() && type.typeName.isEmpty()) {
type.typeId = selectTypeIdBySourceIdStatement.template value<TypeId>(&type.sourceId); auto [moduleId, typeId] = selectModuleAndTypeIdBySourceIdStatement
.template value<ModuleAndTypeId>(&type.sourceId);
type.typeId = typeId;
type.moduleId = moduleId;
return type.typeId; return type.typeId;
} }
ModuleId moduleId = fetchModuleIdUnguarded(type.module); type.moduleId = fetchModuleIdUnguarded(type.module);
if (!moduleId) if (!type.moduleId)
throw ModuleDoesNotExists{}; throw ModuleDoesNotExists{};
type.typeId = upsertTypeStatement.template value<TypeId>(&moduleId, type.typeId = upsertTypeStatement.template value<TypeId>(&type.moduleId,
type.typeName, type.typeName,
static_cast<int>(type.accessSemantics), static_cast<int>(type.accessSemantics),
&type.sourceId); &type.sourceId);
if (!type.typeId) if (!type.typeId)
type.typeId = selectTypeIdByModuleIdAndNameStatement.template value<TypeId>(&moduleId, type.typeId = selectTypeIdByModuleIdAndNameStatement.template value<TypeId>(&type.moduleId,
type.typeName); type.typeName);
upsertNativeType(moduleId, type.typeName, type.typeId); upsertNativeType(type.moduleId, type.typeName, type.typeId);
for (const auto &exportedType : type.exportedTypes)
upsertExportedType(moduleId, exportedType.name, type.typeId, exportedType.version);
return type.typeId; return type.typeId;
} }
@@ -1275,6 +1342,9 @@ private:
void syncPrototypes(Storage::Type &type) void syncPrototypes(Storage::Type &type)
{ {
if (type.changeLevel == Storage::ChangeLevel::Minimal)
return;
if (Utils::visit([](auto &&typeName) -> bool { return typeName.name.isEmpty(); }, if (Utils::visit([](auto &&typeName) -> bool { return typeName.name.isEmpty(); },
type.prototype)) { type.prototype)) {
updatePrototypeStatement.write(&type.typeId, Sqlite::NullValue{}, Sqlite::NullValue{}); updatePrototypeStatement.write(&type.typeId, Sqlite::NullValue{}, Sqlite::NullValue{});
@@ -1642,7 +1712,7 @@ private:
Sqlite::Enforment::Deferred); Sqlite::Enforment::Deferred);
auto &nameColumn = table.addColumn("name"); auto &nameColumn = table.addColumn("name");
auto &kindColumn = table.addColumn("kind"); auto &kindColumn = table.addColumn("kind");
table.addColumn("typeId"); auto &typeIdColumn = table.addColumn("typeId");
auto &majorVersionColumn = table.addColumn("majorVersion"); auto &majorVersionColumn = table.addColumn("majorVersion");
auto &minorVersionColumn = table.addColumn("minorVersion"); auto &minorVersionColumn = table.addColumn("minorVersion");
@@ -1654,6 +1724,8 @@ private:
{moduleIdColumn, nameColumn, kindColumn, majorVersionColumn, minorVersionColumn}, {moduleIdColumn, nameColumn, kindColumn, majorVersionColumn, minorVersionColumn},
"majorVersion IS NOT NULL AND minorVersion IS NOT NULL"); "majorVersion IS NOT NULL AND minorVersion IS NOT NULL");
table.addIndex({typeIdColumn}, "kind=1");
table.initialize(database); table.initialize(database);
} }
@@ -1828,23 +1900,6 @@ public:
" FROM propertyDeclarations JOIN typeSelection USING(typeId) " " FROM propertyDeclarations JOIN typeSelection USING(typeId) "
" WHERE name=?2 ORDER BY level LIMIT 1", " WHERE name=?2 ORDER BY level LIMIT 1",
database}; 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{ mutable ReadStatement<1> selectPrototypeIdsStatement{
"WITH RECURSIVE " "WITH RECURSIVE "
" typeSelection(typeId, level) AS (" " typeSelection(typeId, level) AS ("
@@ -2188,8 +2243,8 @@ public:
WriteStatement deleteFileStatusStatement{"DELETE FROM fileStatuses WHERE sourceId=?1", database}; WriteStatement deleteFileStatusStatement{"DELETE FROM fileStatuses WHERE sourceId=?1", database};
WriteStatement updateFileStatusStatement{ WriteStatement updateFileStatusStatement{
"UPDATE fileStatuses SET size=?2, lastModified=?3 WHERE sourceId=?1", database}; "UPDATE fileStatuses SET size=?2, lastModified=?3 WHERE sourceId=?1", database};
ReadStatement<1> selectTypeIdBySourceIdStatement{"SELECT typeId FROM types WHERE sourceId=?", ReadStatement<2> selectModuleAndTypeIdBySourceIdStatement{
database}; "SELECT moduleId, typeId FROM types WHERE sourceId=?", database};
mutable ReadStatement<1> selectImportedTypeNameIdStatement{ mutable ReadStatement<1> selectImportedTypeNameIdStatement{
"SELECT importedTypeNameId FROM importedTypeNames WHERE kind=?1 AND importOrSourceId=?2 " "SELECT importedTypeNameId FROM importedTypeNames WHERE kind=?1 AND importOrSourceId=?2 "
"AND name=?3 LIMIT 1", "AND name=?3 LIMIT 1",
@@ -2235,6 +2290,31 @@ public:
database}; database};
WriteStatement deleteAllSourcesStatement{"DELETE FROM sources", database}; WriteStatement deleteAllSourcesStatement{"DELETE FROM sources", database};
WriteStatement deleteAllSourceContextsStatement{"DELETE FROM sourceContexts", 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 } // namespace QmlDesigner

View File

@@ -27,6 +27,7 @@
#include "projectstorageids.h" #include "projectstorageids.h"
#include <tuple>
#include <vector> #include <vector>
namespace QmlDesigner { namespace QmlDesigner {

View File

@@ -30,6 +30,7 @@
#include <utils/smallstring.h> #include <utils/smallstring.h>
#include <utils/variant.h> #include <utils/variant.h>
#include <tuple>
#include <vector> #include <vector>
namespace QmlDesigner::Storage { namespace QmlDesigner::Storage {
@@ -268,6 +269,13 @@ public:
, version{version} , 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) explicit ExportedType(Utils::SmallStringView name, int majorVersion, int minorVersion)
: name{name} : name{name}
, version{majorVersion, minorVersion} , version{majorVersion, minorVersion}
@@ -281,10 +289,37 @@ public:
public: public:
Utils::SmallString name; Utils::SmallString name;
Storage::Version version; Storage::Version version;
TypeId typeId;
ModuleId moduleId;
}; };
using ExportedTypes = std::vector<ExportedType>; using ExportedTypes = std::vector<ExportedType>;
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 class NativeType
{ {
public: public:
@@ -591,6 +626,8 @@ public:
PropertyDeclarationId aliasId; PropertyDeclarationId aliasId;
}; };
enum class ChangeLevel { Full, Minimal };
class Type class Type
{ {
public: public:
@@ -605,7 +642,7 @@ public:
FunctionDeclarations functionDeclarations = {}, FunctionDeclarations functionDeclarations = {},
SignalDeclarations signalDeclarations = {}, SignalDeclarations signalDeclarations = {},
EnumerationDeclarations enumerationDeclarations = {}, EnumerationDeclarations enumerationDeclarations = {},
TypeId typeId = TypeId{}) ChangeLevel changeLevel = ChangeLevel::Full)
: typeName{typeName} : typeName{typeName}
, prototype{std::move(prototype)} , prototype{std::move(prototype)}
, exportedTypes{std::move(exportedTypes)} , exportedTypes{std::move(exportedTypes)}
@@ -616,7 +653,7 @@ public:
, module{std::move(module)} , module{std::move(module)}
, accessSemantics{accessSemantics} , accessSemantics{accessSemantics}
, sourceId{sourceId} , sourceId{sourceId}
, typeId{typeId} , changeLevel{changeLevel}
{} {}
explicit Type(Utils::SmallStringView moduleName, explicit Type(Utils::SmallStringView moduleName,
@@ -684,6 +721,8 @@ public:
TypeAccessSemantics accessSemantics = TypeAccessSemantics::Invalid; TypeAccessSemantics accessSemantics = TypeAccessSemantics::Invalid;
SourceId sourceId; SourceId sourceId;
TypeId typeId; TypeId typeId;
ModuleId moduleId;
ChangeLevel changeLevel = ChangeLevel::Full;
}; };
using Types = std::vector<Type>; using Types = std::vector<Type>;

View File

@@ -103,6 +103,8 @@ void ProjectUpdater::update()
std::move(fileStatuses)); std::move(fileStatuses));
} }
void ProjectUpdater::pathsWithIdsChanged(const std::vector<IdPaths> &idPaths) {}
void ProjectUpdater::parseTypeInfos(const QStringList &typeInfos, void ProjectUpdater::parseTypeInfos(const QStringList &typeInfos,
SourceContextId directoryId, SourceContextId directoryId,
Storage::Imports &imports, Storage::Imports &imports,

View File

@@ -28,6 +28,7 @@
#include "filestatus.h" #include "filestatus.h"
#include "nonlockingmutex.h" #include "nonlockingmutex.h"
#include "projectstorageids.h" #include "projectstorageids.h"
#include "projectstoragepathwatchertypes.h"
#include "projectstoragetypes.h" #include "projectstoragetypes.h"
#include <qmljs/parser/qmldirparser_p.h> #include <qmljs/parser/qmldirparser_p.h>
@@ -76,6 +77,7 @@ public:
{} {}
void update(); void update();
void pathsWithIdsChanged(const std::vector<IdPaths> &idPaths);
private: private:
enum class FileState { enum class FileState {

View File

@@ -122,6 +122,19 @@ MATCHER_P(IsExportedType,
return type.name == name; 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, MATCHER_P3(IsPropertyDeclaration,
name, name,
typeName, typeName,
@@ -417,6 +430,7 @@ protected:
protected: protected:
Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory}; Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory};
//Sqlite::Database database{TESTDATA_DIR "/aaaa.db", Sqlite::JournalMode::Wal};
QmlDesigner::ProjectStorage<Sqlite::Database> storage{database, database.isInitialized()}; QmlDesigner::ProjectStorage<Sqlite::Database> storage{database, database.isInitialized()};
QmlDesigner::SourcePathCache<QmlDesigner::ProjectStorage<Sqlite::Database>> sourcePathCache{ QmlDesigner::SourcePathCache<QmlDesigner::ProjectStorage<Sqlite::Database>> sourcePathCache{
storage}; storage};
@@ -4497,4 +4511,44 @@ TEST_F(ProjectStorage, EnsureThatPrototypesForRemovedTypesAreNotAnymoreRelinked)
ASSERT_NO_THROW(storage.synchronize({}, {}, {}, {sourceId1, sourceId2}, {})); 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 } // namespace