QmlDesigner: Relink prototypes

Task-number: QDS-4552
Change-Id: Ibb0d0048c114e100c215493bc9d633d4dbe3c4e0
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-07-01 16:28:02 +02:00
parent 32fd70ce3c
commit e9407c23fa
3 changed files with 143 additions and 13 deletions

View File

@@ -472,6 +472,21 @@ private:
SourceId sourceId;
};
class Prototype
{
public:
explicit Prototype(TypeId typeId, Storage::TypeName prototypeName, SourceId sourceId)
: typeId{typeId}
, typeName{std::move(prototypeName)}
, sourceId{sourceId}
{}
public:
TypeId typeId;
Storage::TypeName typeName;
SourceId sourceId;
};
Storage::TypeName fetchTypeName(TypeNameId typeNameId)
{
Storage::TypeName typeName;
@@ -530,12 +545,27 @@ private:
updatesPropertyDeclarationPropertyTypeToNullStatement.readCallback(callback, &typeId);
}
void handlePrototypes(TypeId prototypeId, std::vector<Prototype> &relinkablePrototypes)
{
auto callback = [&](long long typeId, long long prototypeNameId, int sourceId) {
relinkablePrototypes.emplace_back(TypeId{typeId},
fetchTypeName(TypeNameId{prototypeNameId}),
SourceId{sourceId});
return Sqlite::CallbackControl::Continue;
};
updatePrototypeToNullStatement.readCallback(callback, &prototypeId);
}
void deleteType(TypeId typeId,
std::vector<AliasPropertyDeclaration> &relinkableAliasPropertyDeclarations,
std::vector<PropertyDeclaration> &relinkablePropertyDeclarations)
std::vector<PropertyDeclaration> &relinkablePropertyDeclarations,
std::vector<Prototype> &relinkablePrototypes)
{
handlePropertyDeclarationWithPropertyType(typeId, relinkablePropertyDeclarations);
handleAliasPropertyDeclarationsWithPropertyType(typeId, relinkableAliasPropertyDeclarations);
handlePrototypes(typeId, relinkablePrototypes);
deleteTypeNamesByTypeIdStatement.write(&typeId);
deleteEnumerationDeclarationByTypeIdStatement.write(&typeId);
deletePropertyDeclarationByTypeIdStatement.write(&typeId);
@@ -544,7 +574,8 @@ private:
deleteTypeStatement.write(&typeId);
}
void relinkAliasPropertyDeclaration(const std::vector<AliasPropertyDeclaration> &aliasPropertyDeclarations)
void relinkAliasPropertyDeclarations(
const std::vector<AliasPropertyDeclaration> &aliasPropertyDeclarations)
{
for (const AliasPropertyDeclaration &alias : aliasPropertyDeclarations) {
auto [typeId, aliasTypeNameId] = fetchTypeIdByNameUngarded(alias.aliasTypeName,
@@ -571,7 +602,7 @@ private:
}
}
void relinkPropertyDeclaration(const std::vector<PropertyDeclaration> &relinkablePropertyDeclaration)
void relinkPropertyDeclarations(const std::vector<PropertyDeclaration> &relinkablePropertyDeclaration)
{
for (const PropertyDeclaration &property : relinkablePropertyDeclaration) {
auto [propertyTypeId, propertyTypeNameId] = fetchTypeIdByNameUngarded(property.typeName,
@@ -593,10 +624,30 @@ private:
}
}
void relinkPrototypes(std::vector<Prototype> relinkablePrototypes)
{
for (const Prototype &prototype : relinkablePrototypes) {
auto [prototypeId, prototypeNameId] = fetchTypeIdByNameUngarded(prototype.typeName,
prototype.sourceId);
if (!prototypeId) {
auto hasTypeId = selectPropertyDeclarationIdStatement.template optionalValue<TypeId>(
&prototype.typeId);
if (hasTypeId)
throw TypeNameDoesNotExists{};
continue;
}
updateTypePrototypeStatement.write(&prototype.typeId, &prototypeId, &prototypeNameId);
}
}
void deleteNotUpdatedTypes(const TypeIds &updatedTypeIds, const SourceIds &sourceIds)
{
std::vector<AliasPropertyDeclaration> relinkableAliasPropertyDeclarations;
std::vector<PropertyDeclaration> relinkablePropertyDeclarations;
std::vector<Prototype> relinkablePrototypes;
auto updatedTypeIdValues = Utils::transform<std::vector>(updatedTypeIds, [](TypeId typeId) {
return &typeId;
@@ -609,7 +660,8 @@ private:
auto callback = [&](long long typeId) {
deleteType(TypeId{typeId},
relinkableAliasPropertyDeclarations,
relinkablePropertyDeclarations);
relinkablePropertyDeclarations,
relinkablePrototypes);
return Sqlite::CallbackControl::Continue;
};
@@ -617,17 +669,22 @@ private:
Utils::span(sourceIdValues),
Utils::span(updatedTypeIdValues));
relinkPropertyDeclaration(relinkablePropertyDeclarations);
relinkAliasPropertyDeclaration(relinkableAliasPropertyDeclarations);
relinkPropertyDeclarations(relinkablePropertyDeclarations);
relinkAliasPropertyDeclarations(relinkableAliasPropertyDeclarations);
relinkPrototypes(relinkablePrototypes);
}
void deleteTypesForImportId(ImportId importId)
{
std::vector<AliasPropertyDeclaration> aliasPropertyDeclarations;
std::vector<PropertyDeclaration> relinkablePropertyDeclarations;
std::vector<Prototype> relinkablePrototypes;
auto callback = [&](long long typeId) {
deleteType(TypeId{typeId}, aliasPropertyDeclarations, relinkablePropertyDeclarations);
deleteType(TypeId{typeId},
aliasPropertyDeclarations,
relinkablePropertyDeclarations,
relinkablePrototypes);
return Sqlite::CallbackControl::Continue;
};
@@ -1037,7 +1094,7 @@ private:
{
if (Utils::visit([](auto &&typeName) -> bool { return typeName.name.isEmpty(); },
type.prototype)) {
updatePrototypeStatement.write(&type.typeId, Sqlite::NullValue{});
updatePrototypeStatement.write(&type.typeId, Sqlite::NullValue{}, Sqlite::NullValue{});
} else {
auto [prototypeId, prototypeTypeNameId] = fetchTypeIdByNameUngarded(type.prototype,
type.sourceId);
@@ -1045,7 +1102,7 @@ private:
if (!prototypeId)
throw TypeNameDoesNotExists{};
updatePrototypeStatement.write(&type.typeId, &prototypeId);
updatePrototypeStatement.write(&type.typeId, &prototypeId, &prototypeTypeNameId);
}
}
@@ -1307,6 +1364,7 @@ private:
typesTable,
Sqlite::ForeignKeyAction::NoAction,
Sqlite::ForeignKeyAction::Restrict);
typesTable.addColumn("prototypeNameId");
typesTable.addUniqueIndex({importIdColumn, typesNameColumn});
@@ -1460,8 +1518,8 @@ public:
"CONFLICT DO UPDATE SET prototypeId=excluded.prototypeId, "
"accessSemantics=excluded.accessSemantics, sourceId=excluded.sourceId RETURNING typeId",
database};
WriteStatement updatePrototypeStatement{"UPDATE types SET prototypeId=?2 WHERE typeId=?1",
database};
WriteStatement updatePrototypeStatement{
"UPDATE types SET prototypeId=?2, prototypeNameId=?3 WHERE typeId=?1", database};
mutable ReadStatement<1> selectTypeIdByExportedNameStatement{
"SELECT typeId FROM typeNames WHERE name=?1 AND kind=1", database};
mutable ReadStatement<1> selectPrototypeIdStatement{
@@ -1753,6 +1811,13 @@ public:
ReadStatement<1> selectPropertyDeclarationIdStatement{
"SELECT propertyDeclarationId FROM propertyDeclarations WHERE propertyDeclarationId=?",
database};
ReadWriteStatement<3> updatePrototypeToNullStatement{
"UPDATE types SET prototypeId=NULL WHERE prototypeId=?1 RETURNING "
"typeId, prototypeNameId, sourceId",
database};
ReadStatement<1> selectTypeIdStatement{"SELECT typeId FROM types WHERE typeId=?", database};
WriteStatement updateTypePrototypeStatement{
"UPDATE types SET prototypeId=?2, prototypeNameId=?3 WHERE typeId=?1", database};
};
} // namespace QmlDesigner

View File

@@ -1034,7 +1034,7 @@ std::ostream &operator<<(std::ostream &out, const Type &type)
{
using Utils::operator<<;
return out << "(import: " << type.importId << ", typename: \"" << type.typeName
<< "\", prototype: \"" << type.prototype << "\", " << type.accessSemantics
<< "\", prototype: " << type.prototype << ", " << type.accessSemantics
<< ", source: " << type.sourceId << ", exports: " << type.exportedTypes
<< ", properties: " << type.propertyDeclarations
<< ", functions: " << type.functionDeclarations

View File

@@ -1191,7 +1191,7 @@ TEST_F(ProjectStorageSlowTest, BreakingPrototypeChainByDeletingBaseComponentThro
types.pop_back();
ASSERT_THROW(storage.synchronizeTypes(types, {sourceId1, sourceId2}),
Sqlite::ConstraintPreventsModification);
QmlDesigner::TypeNameDoesNotExists);
}
TEST_F(ProjectStorageSlowTest, SynchronizeTypesAddPropertyDeclarations)
@@ -3005,4 +3005,69 @@ TEST_F(ProjectStorageSlowTest, DoNotRelinkAliasPropertyTypeDoesNotExists)
ASSERT_THROW(storage.synchronizeTypes({}, {sourceId1}), QmlDesigner::TypeNameDoesNotExists);
}
TEST_F(ProjectStorageSlowTest, ChangePrototypeTypeName)
{
Storage::Types types{createTypes()};
types[0].propertyDeclarations[0].typeName = Storage::ExportedType{"Object"};
types[0].prototype = Storage::ExportedType{"Object"};
storage.synchronizeTypes(types, {sourceId1, sourceId2});
types[1].typeName = "QObject3";
storage.synchronizeTypes({types[1]}, {sourceId2});
ASSERT_THAT(storage.fetchTypes(),
Contains(IsStorageType(importId2,
"QQuickItem",
Storage::NativeType{"QObject3"},
TypeAccessSemantics::Reference,
sourceId1)));
}
TEST_F(ProjectStorageSlowTest, ChangePrototypeTypeImportId)
{
Storage::Types types{createTypes()};
storage.synchronizeTypes(types, {sourceId1, sourceId2});
types[1].importId = importId2;
storage.synchronizeTypes({types[1]}, {sourceId2});
ASSERT_THAT(storage.fetchTypes(),
Contains(IsStorageType(importId2,
"QQuickItem",
Storage::NativeType{"QObject"},
TypeAccessSemantics::Reference,
sourceId1)));
}
TEST_F(ProjectStorageSlowTest, ChangePrototypeTypeNameAndImportId)
{
Storage::Types types{createTypes()};
types[0].propertyDeclarations[0].typeName = Storage::ExportedType{"Object"};
types[0].prototype = Storage::ExportedType{"Object"};
storage.synchronizeTypes(types, {sourceId1, sourceId2});
types[1].importId = importId2;
types[1].typeName = "QObject3";
storage.synchronizeTypes({types[1]}, {sourceId2});
ASSERT_THAT(storage.fetchTypes(),
Contains(IsStorageType(importId2,
"QQuickItem",
Storage::NativeType{"QObject3"},
TypeAccessSemantics::Reference,
sourceId1)));
}
TEST_F(ProjectStorageSlowTest, ChangePrototypeTypeNameThrowsForWrongNativePrototupeTypeName)
{
Storage::Types types{createTypes()};
types[0].propertyDeclarations[0].typeName = Storage::ExportedType{"Object"};
storage.synchronizeTypes(types, {sourceId1, sourceId2});
types[1].typeName = "QObject3";
ASSERT_THROW(storage.synchronizeTypes({types[1]}, {sourceId2}),
QmlDesigner::TypeNameDoesNotExists);
}
} // namespace