QmlDesigner: Relink alias properties

There are still some corner cases but this will be done in follow-up
patches. The link information moved to the typenames table so only
indirections are saved in the propertyDeclarations table. Otherwise
the duplication would increase the database size too much.

Task-number: QDS-4551
Change-Id: I4aca85dd2d803b43aa9860183e500ced2d91141f
Reviewed-by: Vikas Pachdha <vikas.pachdha@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
This commit is contained in:
Marco Bubke
2021-06-15 12:32:55 +02:00
parent 8f4a06664f
commit b1fcc4b7a0
4 changed files with 650 additions and 116 deletions

View File

@@ -83,7 +83,8 @@ enum class BasicIdType {
FunctionDeclaration,
SignalDeclaration,
EnumerationDeclaration,
Import
Import,
TypeName
};
using TypeId = BasicId<BasicIdType::Type>;
@@ -110,4 +111,7 @@ using SourceIds = std::vector<SourceId>;
using ImportId = BasicId<BasicIdType::Import>;
using ImportIds = std::vector<ImportId>;
using TypeNameId = BasicId<BasicIdType::TypeName>;
using TypeNameIds = std::vector<TypeNameId>;
} // namespace QmlDesigner

View File

@@ -70,6 +70,9 @@ public:
updatedTypeIds.push_back(declareType(type));
}
for (auto &&type : types)
syncPrototypes(type);
for (auto &&type : types)
synchronizeAliasPropertyDeclarationsRemoval(type);
@@ -94,6 +97,16 @@ public:
transaction.commit();
}
void synchronizeDocuments(Storage::Documents documents)
{
Sqlite::ImmediateTransaction transaction{database};
for (auto &&document : documents)
synchronizeDocumentImports(document.sourceId, document.importIds);
transaction.commit();
}
ImportIds fetchImportIds(const Storage::Imports &imports)
{
ImportIds importIds;
@@ -108,6 +121,11 @@ public:
return importIds;
}
ImportIds fetchImportIds(SourceId sourceId)
{
return selectImportIdsForSourceIdStatement.template valeWithTransaction(16, &sourceId);
}
ImportIds fetchImportDependencyIds(ImportIds importIds) const
{
return fetchImportDependencyIdsStatement.template valuesWithTransaction<ImportId>(
@@ -417,9 +435,108 @@ private:
return selectImportIdByNameStatement.template value<ImportId>(import.name);
}
void deleteType(TypeId typeId)
class AliasPropertyDeclaration
{
deleteExportTypesByTypeIdStatement.write(&typeId);
public:
explicit AliasPropertyDeclaration(PropertyDeclarationId propertyDeclarationId,
Storage::TypeName aliasTypeName,
Utils::SmallString &&aliasPropertyName,
SourceId sourceId)
: propertyDeclarationId{propertyDeclarationId}
, aliasTypeName{std::move(aliasTypeName)}
, aliasPropertyName{std::move(aliasPropertyName)}
, sourceId{sourceId}
{}
public:
PropertyDeclarationId propertyDeclarationId;
Storage::TypeName aliasTypeName;
Utils::SmallString aliasPropertyName;
SourceId sourceId;
};
class PropertyDeclaration
{
public:
explicit PropertyDeclaration(PropertyDeclarationId propertyDeclarationId,
Storage::TypeName typeName,
SourceId sourceId)
: propertyDeclarationId{propertyDeclarationId}
, typeName{std::move(typeName)}
, sourceId{sourceId}
{}
public:
PropertyDeclarationId propertyDeclarationId;
Storage::TypeName typeName;
SourceId sourceId;
};
Storage::TypeName fetchTypeName(TypeNameId typeNameId)
{
Storage::TypeName typeName;
auto callback = [&](Utils::SmallStringView name, long long kind) {
typeName = createTypeName(kind, name);
return Sqlite::CallbackControl::Abort;
};
selectTypeNameStatement.readCallback(callback, &typeNameId);
return typeName;
}
void handleAliasPropertyDeclarationsWithPropertyType(
TypeId typeId, std::vector<AliasPropertyDeclaration> &relinkableAliasPropertyDeclarations)
{
auto callback = [&](long long propertyDeclarationId,
long long propertyTypeNameId,
long long typeIdFromProperty,
long long aliasPropertyDeclarationId) {
auto sourceId = selectSourceIdForTypeIdStatement.template value<SourceId>(
typeIdFromProperty);
auto aliasPropertyName = selectPropertyNameStatement.template value<Utils::SmallString>(
aliasPropertyDeclarationId);
relinkableAliasPropertyDeclarations
.emplace_back(PropertyDeclarationId{propertyDeclarationId},
fetchTypeName(TypeNameId{propertyTypeNameId}),
std::move(aliasPropertyName),
sourceId);
updateAliasPropertyDeclarationToNullStatement.write(propertyDeclarationId);
return Sqlite::CallbackControl::Continue;
};
selectAliasPropertiesDeclarationForPropertiesWithTypeIdStatement.readCallback(callback,
&typeId);
}
void handlePropertyDeclarationWithPropertyType(
TypeId typeId, std::vector<PropertyDeclaration> &relinkablePropertyDeclarations)
{
auto callback = [&](long long propertyDeclarationId, long long propertyTypeNameId) {
auto sourceId = selectSourceIdForTypeIdStatement.template value<SourceId>(&typeId);
relinkablePropertyDeclarations.emplace_back(PropertyDeclarationId{propertyDeclarationId},
fetchTypeName(TypeNameId{propertyTypeNameId}),
sourceId);
return Sqlite::CallbackControl::Continue;
};
updatesPropertyDeclarationPropertyTypeToNullStatement.readCallback(callback, &typeId);
}
void deleteType(TypeId typeId,
std::vector<AliasPropertyDeclaration> &relinkableAliasPropertyDeclarations,
std::vector<PropertyDeclaration> &relinkablePropertyDeclarations)
{
handlePropertyDeclarationWithPropertyType(typeId, relinkablePropertyDeclarations);
handleAliasPropertyDeclarationsWithPropertyType(typeId, relinkableAliasPropertyDeclarations);
deleteTypeNamesByTypeIdStatement.write(&typeId);
deleteEnumerationDeclarationByTypeIdStatement.write(&typeId);
deletePropertyDeclarationByTypeIdStatement.write(&typeId);
deleteFunctionDeclarationByTypeIdStatement.write(&typeId);
@@ -427,8 +544,50 @@ private:
deleteTypeStatement.write(&typeId);
}
void relinkAliasPropertyDeclaration(const std::vector<AliasPropertyDeclaration> &aliasPropertyDeclarations)
{
for (const AliasPropertyDeclaration &alias : aliasPropertyDeclarations) {
auto [typeId, aliasTypeNameId] = fetchTypeIdByNameUngarded(alias.aliasTypeName,
alias.sourceId);
auto [propertyTypeId, aliasId, propertyTraits] = fetchPropertyDeclarationByTypeIdAndNameUngarded(
typeId, alias.aliasPropertyName);
updatePropertyDeclarationWithAliasStatement.write(&alias.propertyDeclarationId,
&propertyTypeId,
propertyTraits,
&aliasTypeNameId,
&aliasId);
}
}
void relinkPropertyDeclaration(const std::vector<PropertyDeclaration> &relinkablePropertyDeclaration)
{
for (const PropertyDeclaration &property : relinkablePropertyDeclaration) {
auto [propertyTypeId, propertyTypeNameId] = fetchTypeIdByNameUngarded(property.typeName,
property.sourceId);
if (!propertyTypeId) {
auto hasPropertyDeclaration = selectPropertyDeclarationIdStatement
.template optionalValue<PropertyDeclarationId>(
&property.propertyDeclarationId);
if (hasPropertyDeclaration)
throw TypeNameDoesNotExists{};
continue;
}
updatePropertyDeclarationTypeStatement.write(&property.propertyDeclarationId,
&propertyTypeId,
&propertyTypeNameId);
}
}
void deleteNotUpdatedTypes(const TypeIds &updatedTypeIds, const SourceIds &sourceIds)
{
std::vector<AliasPropertyDeclaration> relinkableAliasPropertyDeclarations;
std::vector<PropertyDeclaration> relinkablePropertyDeclarations;
auto updatedTypeIdValues = Utils::transform<std::vector>(updatedTypeIds, [](TypeId typeId) {
return &typeId;
});
@@ -438,19 +597,27 @@ private:
});
auto callback = [&](long long typeId) {
deleteType(TypeId{typeId});
deleteType(TypeId{typeId},
relinkableAliasPropertyDeclarations,
relinkablePropertyDeclarations);
return Sqlite::CallbackControl::Continue;
};
selectNotUpdatedTypesInSourcesStatement.readCallback(callback,
Utils::span(sourceIdValues),
Utils::span(updatedTypeIdValues));
relinkPropertyDeclaration(relinkablePropertyDeclarations);
relinkAliasPropertyDeclaration(relinkableAliasPropertyDeclarations);
}
void deleteTypesForImportId(ImportId importId)
{
std::vector<AliasPropertyDeclaration> aliasPropertyDeclarations;
std::vector<PropertyDeclaration> relinkablePropertyDeclarations;
auto callback = [&](long long typeId) {
deleteType(TypeId{typeId});
deleteType(TypeId{typeId}, aliasPropertyDeclarations, relinkablePropertyDeclarations);
return Sqlite::CallbackControl::Continue;
};
@@ -459,12 +626,23 @@ private:
void upsertExportedType(ImportId importId, Utils::SmallStringView name, TypeId typeId)
{
upsertExportedTypesStatement.write(&importId, name, &typeId);
upsertTypeNamesStatement.write(&importId,
name,
&typeId,
static_cast<long long>(Storage::TypeNameKind::Exported));
}
void upsertNativeType(ImportId importId, Utils::SmallStringView name, TypeId typeId)
{
upsertTypeNamesStatement.write(&importId,
name,
&typeId,
static_cast<long long>(Storage::TypeNameKind::Native));
}
void synchronizePropertyDeclarations(TypeId typeId,
Storage::PropertyDeclarations &propertyDeclarations,
ImportIds &importIds)
SourceId sourceId)
{
std::sort(propertyDeclarations.begin(),
propertyDeclarations.end(),
@@ -481,27 +659,60 @@ private:
};
auto insert = [&](const Storage::PropertyDeclaration &value) {
auto propertyTypeId = fetchTypeIdByNameUngarded(value.typeName, importIds);
auto [propertyTypeId, propertyTypeNameId] = fetchTypeIdByNameUngarded(value.typeName,
sourceId);
insertPropertyDeclarationStatement.write(&typeId,
value.name,
&propertyTypeId,
static_cast<int>(value.traits));
if (!propertyTypeId)
throw TypeNameDoesNotExists{};
auto propertyDeclarationId = insertPropertyDeclarationStatement.template value<
PropertyDeclarationId>(&typeId,
value.name,
&propertyTypeId,
static_cast<int>(value.traits),
&propertyTypeNameId);
auto nextPropertyDeclarationId = selectPropertyDeclarationIdPrototypeChainDownStatement
.template value<PropertyDeclarationId>(&typeId,
value.name);
if (nextPropertyDeclarationId) {
updateAliasPropertyDeclarationStatement.write(&nextPropertyDeclarationId,
&propertyTypeId,
static_cast<int>(value.traits),
&propertyDeclarationId);
}
};
auto update = [&](const Storage::PropertyDeclarationView &view,
const Storage::PropertyDeclaration &value) {
auto propertyTypeId = fetchTypeIdByNameUngarded(value.typeName, importIds);
auto [propertyTypeId, propertyTypeNameId] = fetchTypeIdByNameUngarded(value.typeName,
sourceId);
if (view.traits == value.traits && propertyTypeId == view.typeId)
if (!propertyTypeId)
throw TypeNameDoesNotExists{};
if (view.traits == value.traits && propertyTypeId == view.typeId
&& propertyTypeNameId == view.typeNameId)
return;
updatePropertyDeclarationStatement.write(&view.id,
&propertyTypeId,
static_cast<int>(value.traits));
static_cast<int>(value.traits),
&propertyTypeNameId);
updatePropertyDeclarationWithAliasIdStatement.write(&view.id,
&propertyTypeId,
static_cast<int>(value.traits));
};
auto remove = [&](const Storage::PropertyDeclarationView &view) {
auto nextPropertyDeclarationId = selectPropertyDeclarationIdPrototypeChainDownStatement
.template value<PropertyDeclarationId>(&typeId,
view.name);
if (nextPropertyDeclarationId) {
updateAliasPropertyDeclarationByAliasPropertyDeclarationIdStatement
.write(&nextPropertyDeclarationId, &view.id);
}
deletePropertyDeclarationStatement.write(&view.id);
};
@@ -541,7 +752,6 @@ private:
{
auto &aliasDeclarations = type.aliasDeclarations;
TypeId typeId = type.typeId;
ImportIds &importIds = type.importIds;
std::sort(aliasDeclarations.begin(), aliasDeclarations.end(), [](auto &&first, auto &&second) {
return Sqlite::compare(first.name, second.name) < 0;
@@ -556,20 +766,31 @@ private:
};
auto insert = [&](const Storage::AliasPropertyDeclaration &value) {
auto [aliasTypeId, aliasId, propertyTraits] = fetchPropertyDeclarationByTypeNameAndName(
value.aliasTypeName, value.aliasPropertyName, importIds);
auto [propertyTypeId, aliasTypeNameId] = fetchTypeIdByNameUngarded(value.aliasTypeName,
type.sourceId);
insertPropertyDeclarationWithAliasStatement.write(&typeId,
value.name,
&aliasTypeId,
propertyTraits,
&aliasId);
if (!propertyTypeId)
throw TypeNameDoesNotExists{};
auto [aliasTypeId, aliasId, propertyTraits] = fetchPropertyDeclarationByTypeIdAndNameUngarded(
propertyTypeId, value.aliasPropertyName);
Utils::SmallStringView aliasTypeName = extractTypeName(value.aliasTypeName);
insertPropertyDeclarationWithAliasStatement.write(
&typeId, value.name, &aliasTypeId, propertyTraits, &aliasTypeNameId, &aliasId);
};
auto update = [&](const Storage::AliasPropertyDeclarationView &view,
const Storage::AliasPropertyDeclaration &value) {
auto [aliasTypeId, aliasId, propertyTraits] = fetchPropertyDeclarationByTypeNameAndName(
value.aliasTypeName, value.aliasPropertyName, importIds);
auto [propertyTypeId, aliasTypeNameId] = fetchTypeIdByNameUngarded(value.aliasTypeName,
type.sourceId);
if (!propertyTypeId)
throw TypeNameDoesNotExists{};
auto [aliasTypeId, aliasId, propertyTraits] = fetchPropertyDeclarationByTypeIdAndNameUngarded(
propertyTypeId, value.aliasPropertyName);
if (view.aliasId == aliasId)
return;
@@ -577,6 +798,7 @@ private:
updatePropertyDeclarationWithAliasStatement.write(&view.id,
&aliasTypeId,
propertyTraits,
&aliasTypeNameId,
&aliasId);
};
@@ -585,6 +807,27 @@ private:
Sqlite::insertUpdateDelete(range, aliasDeclarations, compareKey, insert, update, remove);
}
void synchronizeDocumentImports(SourceId sourceId, ImportIds &importIds)
{
std::sort(importIds.begin(), importIds.end());
auto range = selectImportIdsForSourceIdStatement.template range<ImportId>(&sourceId);
auto compareKey = [](ImportId first, ImportId second) { return first.id - second.id; };
auto insert = [&](ImportId importId) {
insertImportIdForSourceIdStatement.write(&sourceId, &importId);
};
auto update = [](ImportId, ImportId) {};
auto remove = [&](ImportId importId) {
deleteImportIdForSourceIdStatement.write(&sourceId, &importId);
};
Sqlite::insertUpdateDelete(range, importIds, compareKey, insert, update, remove);
}
Utils::PathString createJson(const Storage::ParameterDeclarations &parameters)
{
Utils::PathString json;
@@ -762,6 +1005,8 @@ private:
static_cast<int>(type.accessSemantics),
&type.sourceId);
upsertNativeType(type.importId, type.typeName, type.typeId);
for (const auto &exportedType : type.exportedTypes)
upsertExportedType(type.importId, exportedType.name, type.typeId);
@@ -772,69 +1017,114 @@ private:
{
auto typeId = type.typeId;
auto prototypeId = fetchTypeIdByNameUngarded(type.prototype, type.importIds);
updatePrototypeStatement.write(&typeId, &prototypeId);
synchronizePropertyDeclarations(typeId, type.propertyDeclarations, type.importIds);
synchronizePropertyDeclarations(typeId, type.propertyDeclarations, type.sourceId);
synchronizeFunctionDeclarations(typeId, type.functionDeclarations);
synchronizeSignalDeclarations(typeId, type.signalDeclarations);
synchronizeEnumerationDeclarations(typeId, type.enumerationDeclarations);
}
TypeId fetchTypeIdByNameUngarded(const Storage::TypeName &name, ImportIds &importIds)
void syncPrototypes(Storage::Type &type)
{
if (Utils::visit([](auto &&type) -> bool { return type.name.isEmpty(); }, name))
return TypeId{};
if (Utils::visit([](auto &&type) -> bool { return type.name.isEmpty(); }, type.prototype)) {
updatePrototypeStatement.write(&type.typeId, Sqlite::NullValue{});
} else {
auto [prototypeId, prototypeTypeNameId] = fetchTypeIdByNameUngarded(type.prototype,
type.sourceId);
if (!prototypeId)
throw TypeNameDoesNotExists{};
updatePrototypeStatement.write(&type.typeId, &prototypeId);
}
}
Utils::SmallStringView extractTypeName(const Storage::TypeName &name) const
{
return Utils::visit([](auto &&typeName) -> Utils::SmallStringView { return typeName.name; },
name);
}
Storage::TypeName createTypeName(long long typeIndex, Utils::SmallStringView name) const
{
switch (typeIndex) {
case 0:
return Utils::variant_alternative_t<0, Storage::TypeName>(name);
case 1:
return Utils::variant_alternative_t<1, Storage::TypeName>(name);
}
return {};
}
class TypeIdAndTypeNameId
{
public:
TypeIdAndTypeNameId() = default;
TypeIdAndTypeNameId(long long typeId, long long typeNameId)
: typeId{typeId}
, typeNameId{typeNameId}
{}
public:
TypeId typeId;
TypeNameId typeNameId;
};
TypeIdAndTypeNameId fetchTypeIdByNameUngarded(const Storage::TypeName &name, SourceId sourceId)
{
struct Inspect
{
TypeId operator()(const Storage::NativeType &nativeType)
auto operator()(const Storage::NativeType &nativeType)
{
return storage.selectTypeIdByImportIdsAndNameStatement
.template value<TypeId>(static_cast<void *>(importIds.data()),
static_cast<long long>(importIds.size()),
nativeType.name);
return storage.selectTypeIdByImportIdsFromSourceIdAndNameStatement
.template value<TypeIdAndTypeNameId>(&sourceId, nativeType.name);
}
TypeId operator()(const Storage::ExportedType &exportedType)
auto operator()(const Storage::ExportedType &exportedType)
{
return storage.selectTypeIdByImportIdsAndExportedNameStatement
.template value<TypeId>(static_cast<void *>(importIds.data()),
static_cast<long long>(importIds.size()),
exportedType.name);
return storage.selectTypeIdByImportIdsFromSourceIdAndExportedNameStatement
.template value<TypeIdAndTypeNameId>(&sourceId, exportedType.name);
}
TypeId operator()(const Storage::ExplicitExportedType &exportedType)
auto operator()(const Storage::ExplicitExportedType &exportedType)
{
return storage.selectTypeIdByImportIdAndExportedNameStatement
.template value<TypeId>(&exportedType.importId, exportedType.name);
.template value<TypeIdAndTypeNameId>(&exportedType.importId, exportedType.name);
}
ProjectStorage &storage;
ImportIds &importIds;
SourceId sourceId;
};
auto typeId = Utils::visit(Inspect{*this, importIds}, name);
if (typeId)
return typeId;
throw TypeNameDoesNotExists{};
return Utils::visit(Inspect{*this, sourceId}, name);
}
using PropertyDeclarationViewTuple = std::tuple<TypeId, PropertyDeclarationId, long long>;
PropertyDeclarationViewTuple fetchPropertyDeclarationByTypeNameAndName(
const Storage::TypeName &typeName, Utils::SmallStringView name, ImportIds &importIds)
class FetchPropertyDeclarationResult
{
TypeId typeId = fetchTypeIdByNameUngarded(typeName, importIds);
public:
FetchPropertyDeclarationResult(long long propertyTypeId,
long long propertyDeclarationId,
long long propertyTraits)
: propertyTypeId{propertyTypeId}
, propertyDeclarationId{propertyDeclarationId}
, propertyTraits{propertyTraits}
{}
public:
TypeId propertyTypeId;
PropertyDeclarationId propertyDeclarationId;
long long propertyTraits;
};
FetchPropertyDeclarationResult fetchPropertyDeclarationByTypeIdAndNameUngarded(
TypeId typeId, Utils::SmallStringView name)
{
auto propertyDeclaration = selectPropertyDeclarationByTypeIdAndNameStatement
.template value<PropertyDeclarationViewTuple>(&typeId, name);
.template optionalValue<FetchPropertyDeclarationResult>(&typeId,
name);
if (auto id = std::get<PropertyDeclarationId>(propertyDeclaration); id)
return propertyDeclaration;
if (propertyDeclaration)
return *propertyDeclaration;
throw PropertyNameDoesNotExists{};
}
@@ -947,10 +1237,11 @@ private:
createSourceContextsTable(database);
createSourcesTable(database);
createTypesAndePropertyDeclarationsTables(database);
createExportedTypesTable(database);
createTypeNamesTable(database);
createEnumerationsTable(database);
createFunctionsTable(database);
createSignalsTable(database);
createSourceImportsTable(database);
transaction.commit();
@@ -1024,6 +1315,7 @@ private:
Sqlite::ForeignKeyAction::NoAction,
Sqlite::ForeignKeyAction::Restrict);
propertyDeclarationTable.addColumn("propertyTraits");
propertyDeclarationTable.addColumn("propertyTypeNameId");
auto &aliasPropertyDeclarationIdColumn = propertyDeclarationTable.addForeignKeyColumn(
"aliasPropertyDeclarationId",
propertyDeclarationTable,
@@ -1038,17 +1330,18 @@ private:
}
}
void createExportedTypesTable(Database &database)
void createTypeNamesTable(Database &database)
{
Sqlite::Table table;
table.setUseIfNotExists(true);
table.setUseWithoutRowId(true);
table.setName("exportedTypes");
table.setName("typeNames");
table.addColumn("typeNameId", Sqlite::ColumnType::Integer, {Sqlite::PrimaryKey{}});
auto &importIdColumn = table.addColumn("importId");
auto &nameColumn = table.addColumn("name");
auto &kindColumn = table.addColumn("kind");
table.addColumn("typeId");
table.addPrimaryKeyContraint({importIdColumn, nameColumn});
table.addUniqueIndex({importIdColumn, nameColumn, kindColumn});
table.initialize(database);
}
@@ -1131,6 +1424,20 @@ private:
table.initialize(database);
}
void createSourceImportsTable(Database &database)
{
Sqlite::Table table;
table.setUseIfNotExists(true);
table.setUseWithoutRowId(true);
table.setName("sourceImports");
auto &sourceIdColumn = table.addColumn("sourceId");
auto &importIdColumn = table.addColumn("importId");
table.addPrimaryKeyContraint({sourceIdColumn, importIdColumn});
table.initialize(database);
}
};
public:
@@ -1142,10 +1449,10 @@ public:
"CONFLICT DO UPDATE SET prototypeId=excluded.prototypeId, "
"accessSemantics=excluded.accessSemantics, sourceId=excluded.sourceId RETURNING typeId",
database};
WriteStatement updatePrototypeStatement{
"UPDATE types SET prototypeId=nullif(?2, -1) WHERE typeId=?1", database};
WriteStatement updatePrototypeStatement{"UPDATE types SET prototypeId=?2 WHERE typeId=?1",
database};
mutable ReadStatement<1> selectTypeIdByExportedNameStatement{
"SELECT typeId FROM exportedTypes WHERE name=?1", database};
"SELECT typeId FROM typeNames WHERE name=?1 AND kind=1", database};
mutable ReadStatement<1> selectPrototypeIdStatement{
"WITH RECURSIVE "
" typeSelection(typeId) AS ("
@@ -1156,33 +1463,36 @@ public:
database};
mutable ReadStatement<1> selectPropertyDeclarationIdByTypeIdAndNameStatement{
"WITH RECURSIVE "
" typeSelection(typeId) AS ("
" VALUES(?1) "
" typeSelection(typeId, level) AS ("
" VALUES(?1, 0) "
" UNION ALL "
" SELECT prototypeId FROM types JOIN typeSelection USING(typeId)) "
" SELECT prototypeId, typeSelection.level+1 FROM types JOIN typeSelection "
" USING(typeId)) "
"SELECT propertyDeclarationId FROM propertyDeclarations JOIN typeSelection USING(typeId) "
" WHERE name=?2 LIMIT 1",
" WHERE name=?2 ORDER BY level LIMIT 1",
database};
mutable ReadStatement<3> selectPropertyDeclarationByTypeIdAndNameStatement{
"WITH RECURSIVE "
" typeSelection(typeId) AS ("
" VALUES(?1) "
" typeSelection(typeId, level) AS ("
" VALUES(?1, 0) "
" UNION ALL "
" SELECT prototypeId FROM types JOIN typeSelection USING(typeId)) "
"SELECT propertyTypeId, propertyDeclarationId, propertyTraits FROM propertyDeclarations "
" JOIN typeSelection USING(typeId) "
" WHERE name=?2 LIMIT 1",
" SELECT prototypeId, typeSelection.level+1 FROM types JOIN typeSelection "
" USING(typeId)) "
"SELECT propertyTypeId, propertyDeclarationId, propertyTraits "
" FROM propertyDeclarations JOIN typeSelection USING(typeId) "
" WHERE name=?2 ORDER BY level LIMIT 1",
database};
WriteStatement upsertExportedTypesStatement{"INSERT INTO exportedTypes(importId, name, typeId) "
"VALUES(?1, ?2, ?3) ON CONFLICT DO NOTHING",
database};
WriteStatement upsertTypeNamesStatement{"INSERT INTO typeNames(importId, name, typeId, kind) "
"VALUES(?1, ?2, ?3, ?4) ON CONFLICT DO NOTHING",
database};
mutable ReadStatement<1> selectPrototypeIdsStatement{
"WITH RECURSIVE "
" typeSelection(typeId) AS ("
" VALUES(?1) "
" typeSelection(typeId, level) AS ("
" VALUES(?1, 0) "
" UNION ALL "
" SELECT prototypeId FROM types JOIN typeSelection USING(typeId)) "
"SELECT typeId FROM typeSelection",
" SELECT prototypeId, typeSelection.level+1 FROM types JOIN typeSelection "
" USING(typeId)) "
"SELECT typeId FROM typeSelection ORDER BY level DESC",
database};
mutable ReadStatement<1> selectSourceContextIdFromSourceContextsBySourceContextPathStatement{
"SELECT sourceContextId FROM sourceContexts WHERE sourceContextPath = ?", database};
@@ -1204,12 +1514,16 @@ public:
"SELECT sourceName, sourceContextId, sourceId FROM sources", database};
mutable ReadStatement<1> selectTypeIdByImportIdsAndNameStatement{
"SELECT typeId FROM types WHERE importId IN carray(?1, ?2, 'int64') AND name=?3", database};
mutable ReadStatement<2> selectTypeIdByImportIdsFromSourceIdAndNameStatement{
"SELECT typeId, typeNameId FROM typeNames JOIN sourceImports USING(importId) WHERE name=?2 "
"AND kind=0 AND sourceImports.sourceId=?1",
database};
mutable ReadStatement<5> selectTypeByTypeIdStatement{
"SELECT importId, name, (SELECT name FROM types WHERE typeId=outerTypes.prototypeId), "
"accessSemantics, ifnull(sourceId, -1) FROM types AS outerTypes WHERE typeId=?",
database};
mutable ReadStatement<1> selectExportedTypesByTypeIdStatement{
"SELECT name FROM exportedTypes WHERE typeId=?", database};
"SELECT name FROM typeNames WHERE typeId=? AND kind=1", database};
mutable ReadStatement<6> selectTypesStatement{
"SELECT importId, name, typeId, (SELECT name FROM types WHERE "
"typeId=outerTypes.prototypeId), accessSemantics, ifnull(sourceId, -1) FROM types AS "
@@ -1218,8 +1532,7 @@ public:
ReadStatement<1> selectNotUpdatedTypesInSourcesStatement{
"SELECT typeId FROM types WHERE (sourceId IN carray(?1) AND typeId NOT IN carray(?2))",
database};
WriteStatement deleteExportTypesByTypeIdStatement{"DELETE FROM exportedTypes WHERE typeId=?",
database};
WriteStatement deleteTypeNamesByTypeIdStatement{"DELETE FROM typeNames WHERE typeId=?", database};
WriteStatement deleteEnumerationDeclarationByTypeIdStatement{
"DELETE FROM enumerationDeclarations WHERE typeId=?", database};
WriteStatement deletePropertyDeclarationByTypeIdStatement{
@@ -1233,18 +1546,23 @@ public:
"SELECT name, (SELECT name FROM types WHERE typeId=propertyDeclarations.propertyTypeId),"
"propertyTraits FROM propertyDeclarations WHERE typeId=?",
database};
ReadStatement<4> selectPropertyDeclarationsForTypeIdStatement{
"SELECT name, propertyTraits, propertyTypeId, propertyDeclarationId FROM "
"propertyDeclarations WHERE typeId=? AND aliasPropertyDeclarationId IS NULL ORDER BY "
ReadStatement<5> selectPropertyDeclarationsForTypeIdStatement{
"SELECT name, propertyTraits, propertyTypeId, propertyTypeNameId, propertyDeclarationId "
"FROM propertyDeclarations WHERE typeId=? AND aliasPropertyDeclarationId IS NULL ORDER BY "
"name",
database};
WriteStatement insertPropertyDeclarationStatement{
"INSERT INTO propertyDeclarations(typeId, name, propertyTypeId, propertyTraits) "
"VALUES(?1, ?2, ?3, ?4)",
ReadWriteStatement<1> insertPropertyDeclarationStatement{
"INSERT INTO propertyDeclarations(typeId, name, propertyTypeId, propertyTraits, "
"propertyTypeNameId, aliasPropertyDeclarationId) VALUES(?1, ?2, ?3, ?4, ?5, NULL) "
"RETURNING propertyDeclarationId",
database};
WriteStatement updatePropertyDeclarationStatement{
"UPDATE propertyDeclarations SET propertyTypeId=?2, propertyTraits=?3, "
"propertyTypeNameId=?4 WHERE propertyDeclarationId=?1",
database};
WriteStatement updatePropertyDeclarationWithAliasIdStatement{
"UPDATE propertyDeclarations SET propertyTypeId=?2, propertyTraits=?3 WHERE "
"propertyDeclarationId=?1 OR aliasPropertyDeclarationId=?1",
"aliasPropertyDeclarationId=?1",
database};
WriteStatement deletePropertyDeclarationStatement{
"DELETE FROM propertyDeclarations WHERE propertyDeclarationId=?", database};
@@ -1252,13 +1570,14 @@ public:
"SELECT name, propertyDeclarationId, aliasPropertyDeclarationId FROM propertyDeclarations "
"WHERE typeId=? AND aliasPropertyDeclarationId IS NOT NULL ORDER BY name",
database};
WriteStatement insertPropertyDeclarationWithAliasStatement{
"INSERT INTO propertyDeclarations(typeId, name, propertyTypeId, propertyTraits, "
"aliasPropertyDeclarationId) VALUES(?1, ?2, ?3, ?4, ?5) ",
database};
WriteStatement updatePropertyDeclarationWithAliasStatement{
"UPDATE propertyDeclarations SET propertyTypeId=?2, propertyTraits=?3, "
"aliasPropertyDeclarationId=?4 WHERE propertyDeclarationId=?1",
"propertyTypeNameId=?4, aliasPropertyDeclarationId=?5 WHERE propertyDeclarationId=?1",
database};
WriteStatement insertPropertyDeclarationWithAliasStatement{
"INSERT INTO propertyDeclarations(typeId, name, propertyTypeId, propertyTraits, "
"propertyTypeNameId, aliasPropertyDeclarationId) "
"VALUES(?1, ?2, ?3, ?4, ?5, ?6)",
database};
mutable ReadStatement<4> selectFunctionDeclarationsForTypeIdStatement{
"SELECT name, returnTypeName, signature, functionDeclarationId FROM "
@@ -1351,10 +1670,15 @@ public:
mutable ReadStatement<1> selectTypeIdByImportIdAndNameStatement{
"SELECT typeId FROM types WHERE importId=?1 and name=?2", database};
mutable ReadStatement<1> selectTypeIdByImportIdsAndExportedNameStatement{
"SELECT typeId FROM exportedTypes WHERE importId IN carray(?1, ?2, 'int64') AND name=?3",
"SELECT typeId FROM typeNames WHERE importId IN carray(?1, ?2, 'int64') AND name=?3 AND "
"kind=1",
database};
mutable ReadStatement<1> selectTypeIdByImportIdAndExportedNameStatement{
"SELECT typeId FROM exportedTypes WHERE importId=?1 AND name=?2", database};
mutable ReadStatement<2> selectTypeIdByImportIdsFromSourceIdAndExportedNameStatement{
"SELECT typeId, typeNameId FROM typeNames JOIN sourceImports USING(importId) WHERE name=?2 "
"AND kind=1 AND sourceImports.sourceId=?1",
database};
mutable ReadStatement<2> selectTypeIdByImportIdAndExportedNameStatement{
"SELECT typeId, typeNameId FROM typeNames WHERE importId=?1 AND name=?2 AND kind=1", database};
mutable ReadStatement<1> fetchImportDependencyIdsStatement{
"WITH RECURSIVE "
" importIds(importId) AS ("
@@ -1363,6 +1687,59 @@ public:
" SELECT parentImportId FROM importDependencies JOIN importIds USING(importId)) "
"SELECT importId FROM importIds",
database};
mutable ReadStatement<1> selectImportIdsForSourceIdStatement{
"SELECT importId FROM sourceImports WHERE sourceId=? ORDER BY importId", database};
WriteStatement insertImportIdForSourceIdStatement{
"INSERT INTO sourceImports(sourceId, importId) VALUES (?1, ?2)", database};
WriteStatement deleteImportIdForSourceIdStatement{
"DELETE FROM sourceImports WHERE sourceId=?1 AND importId=?2", database};
ReadStatement<1> selectPropertyDeclarationIdPrototypeChainDownStatement{
"WITH RECURSIVE "
" typeSelection(typeId, level) AS ("
" SELECT prototypeId, 0 FROM types WHERE typeId=?1 AND prototypeId IS NOT NULL"
" UNION ALL "
" SELECT prototypeId, typeSelection.level+1 FROM types JOIN typeSelection "
" USING(typeId) WHERE prototypeId IS NOT NULL)"
"SELECT propertyDeclarationId FROM typeSelection JOIN propertyDeclarations "
" USING(typeId) WHERE name=?2 ORDER BY level LIMIT 1",
database};
WriteStatement updateAliasPropertyDeclarationStatement{
"UPDATE propertyDeclarations SET propertyTypeId=?2, propertyTraits=?3, "
"aliasPropertyDeclarationId=?4 WHERE aliasPropertyDeclarationId=?1",
database};
WriteStatement updateAliasPropertyDeclarationByAliasPropertyDeclarationIdStatement{
"UPDATE propertyDeclarations SET propertyTypeId=new.propertyTypeId, "
"propertyTraits=new.propertyTraits, aliasPropertyDeclarationId=?1 FROM (SELECT "
"propertyTypeId, propertyTraits FROM propertyDeclarations WHERE propertyDeclarationId=?1) "
"AS new WHERE aliasPropertyDeclarationId=?2",
database};
WriteStatement updateAliasPropertyDeclarationToNullStatement{
"UPDATE propertyDeclarations SET aliasPropertyDeclarationId=NULL, propertyTypeId=NULL, "
"propertyTraits=NULL WHERE propertyDeclarationId=?",
database};
ReadStatement<4> selectAliasPropertiesDeclarationForPropertiesWithTypeIdStatement{
"SELECT alias.propertyDeclarationId, alias.propertyTypeNameId, alias.typeId, "
"target.propertyDeclarationId FROM propertyDeclarations AS alias JOIN propertyDeclarations "
"AS target ON alias.aliasPropertyDeclarationId=target.propertyDeclarationId WHERE "
"alias.propertyTypeId=?1",
database};
ReadWriteStatement<2> updatesPropertyDeclarationPropertyTypeToNullStatement{
"UPDATE propertyDeclarations SET propertyTypeId=NULL WHERE propertyTypeId=?1 AND "
"aliasPropertyDeclarationId IS NULL RETURNING propertyDeclarationId, propertyTypeNameId",
database};
ReadStatement<1> selectSourceIdForTypeIdStatement{"SELECT sourceId FROM types WHERE typeId=?",
database};
ReadStatement<2> selectTypeNameStatement{"SELECT name, kind FROM typeNames WHERE typeNameId=?",
database};
ReadStatement<1> selectPropertyNameStatement{
"SELECT name FROM propertyDeclarations WHERE propertyDeclarationId=?", database};
WriteStatement updatePropertyDeclarationTypeStatement{
"UPDATE propertyDeclarations SET propertyTypeId=?2, propertyTypeNameId=?3 WHERE "
"propertyDeclarationId=?1",
database};
ReadStatement<1> selectPropertyDeclarationIdStatement{
"SELECT propertyDeclarationId FROM propertyDeclarations WHERE propertyDeclarationId=?",
database};
};
} // namespace QmlDesigner

View File

@@ -43,6 +43,8 @@ enum class PropertyDeclarationTraits : unsigned int {
IsList = 1 << 2
};
enum class TypeNameKind { Native = 0, Exported = 1 };
constexpr PropertyDeclarationTraits operator|(PropertyDeclarationTraits first,
PropertyDeclarationTraits second)
{
@@ -372,13 +374,12 @@ using PropertyDeclarations = std::vector<PropertyDeclaration>;
class PropertyDeclarationView
{
public:
explicit PropertyDeclarationView(Utils::SmallStringView name,
int traits,
long long typeId,
long long id)
explicit PropertyDeclarationView(
Utils::SmallStringView name, int traits, long long typeId, long long typeNameId, long long id)
: name{name}
, traits{static_cast<PropertyDeclarationTraits>(traits)}
, typeId{typeId}
, typeNameId{typeNameId}
, id{id}
{}
@@ -387,6 +388,7 @@ public:
Utils::SmallStringView name;
PropertyDeclarationTraits traits = {};
TypeId typeId;
TypeNameId typeNameId;
PropertyDeclarationId id;
};
@@ -419,7 +421,7 @@ public:
{}
public:
Utils::SmallString name;
Utils::SmallStringView name;
PropertyDeclarationId id;
PropertyDeclarationId aliasId;
};
@@ -443,7 +445,6 @@ public:
TypeId typeId = TypeId{})
: typeName{typeName}
, prototype{std::move(prototype)}
, importIds{std::move(importIds)}
, exportedTypes{std::move(exportedTypes)}
, propertyDeclarations{std::move(propertyDeclarations)}
, functionDeclarations{std::move(functionDeclarations)}
@@ -487,7 +488,6 @@ public:
Utils::SmallString typeName;
TypeName prototype;
Utils::SmallString attachedType;
ImportIds importIds;
ExportedTypes exportedTypes;
PropertyDeclarations propertyDeclarations;
FunctionDeclarations functionDeclarations;
@@ -503,6 +503,22 @@ public:
using Types = std::vector<Type>;
class Document
{
public:
explicit Document() = default;
explicit Document(SourceId sourceId, ImportIds importIds)
: importIds{std::move(importIds)}
, sourceId{sourceId}
{}
public:
ImportIds importIds;
SourceId sourceId;
};
using Documents = std::vector<Document>;
class BasicImport
{
public:

View File

@@ -195,7 +195,6 @@ protected:
ProjectStorageMock storage{databaseMock, true};
ReadWriteStatement<1> &upsertTypeStatement = storage.upsertTypeStatement;
ReadStatement<1> &selectTypeIdByExportedNameStatement = storage.selectTypeIdByExportedNameStatement;
WriteStatement &upsertExportedTypesStatement = storage.upsertExportedTypesStatement;
ReadStatement<1> &selectSourceContextIdFromSourceContextsBySourceContextPathStatement
= storage.selectSourceContextIdFromSourceContextsBySourceContextPathStatement;
ReadStatement<1> &selectSourceIdFromSourcesBySourceContextIdAndSourceNameStatment
@@ -468,6 +467,11 @@ protected:
sourceId3 = sourcePathCache.sourceId(path3);
sourceId4 = sourcePathCache.sourceId(path4);
storage.synchronizeDocuments({Storage::Document{sourceId1, importIds},
Storage::Document{sourceId2, importIds},
Storage::Document{sourceId3, importIds},
Storage::Document{sourceId4, importIds}});
return Storage::Types{
Storage::Type{
importId2,
@@ -869,7 +873,7 @@ TEST_F(ProjectStorageSlowTest, SynchronizeTypesAddsNewTypesThrowsWithWrongExport
TEST_F(ProjectStorageSlowTest, SynchronizeTypesAddsNewTypesWithMissingImportAndExportedPrototypeName)
{
Storage::Types types{createTypes()};
types[0].importIds = {importId1};
storage.synchronizeDocuments({Storage::Document{sourceId1, {importId1}}});
types[1].prototype = Storage::ExportedType{"Object"};
ASSERT_THROW(storage.synchronizeTypes(types, {sourceId1, sourceId2}),
@@ -879,7 +883,7 @@ TEST_F(ProjectStorageSlowTest, SynchronizeTypesAddsNewTypesWithMissingImportAndE
TEST_F(ProjectStorageSlowTest, SynchronizeTypesAddsNewTypesWithMissingImport)
{
Storage::Types types{createTypes()};
types[0].importIds = {importId1};
storage.synchronizeDocuments({Storage::Document{sourceId1, {importId1}}});
ASSERT_THROW(storage.synchronizeTypes(types, {sourceId1, sourceId2}),
QmlDesigner::TypeNameDoesNotExists);
@@ -1154,7 +1158,7 @@ TEST_F(ProjectStorageSlowTest,
SynchronizeTypesAddPropertyDeclarationsWithMissingImportIdsForNativeTypes)
{
Storage::Types types{createTypes()};
types[0].importIds = {importId2};
storage.synchronizeDocuments({Storage::Document{sourceId1, {importId2}}});
types[0].propertyDeclarations.pop_back();
ASSERT_THROW(storage.synchronizeTypes(types, {}), QmlDesigner::TypeNameDoesNotExists);
@@ -1164,7 +1168,7 @@ TEST_F(ProjectStorageSlowTest,
SynchronizeTypesAddPropertyDeclarationsWithMissingImportIdsForExportedTypes)
{
Storage::Types types{createTypes()};
types[0].importIds = {importId1};
storage.synchronizeDocuments({Storage::Document{sourceId1, {importId1}}});
types[0].propertyDeclarations[0].typeName = Storage::ExportedType{"Obj"};
ASSERT_THROW(storage.synchronizeTypes(types, {}), QmlDesigner::TypeNameDoesNotExists);
@@ -1414,7 +1418,7 @@ TEST_F(ProjectStorageSlowTest, BreakingPropertyDeclarationTypeDependencyByDeleti
types.pop_back();
ASSERT_THROW(storage.synchronizeTypes(types, {sourceId1, sourceId2}),
Sqlite::ConstraintPreventsModification);
QmlDesigner::TypeNameDoesNotExists);
}
TEST_F(ProjectStorageSlowTest, SynchronizeTypesAddFunctionDeclarations)
@@ -2702,4 +2706,137 @@ TEST_F(ProjectStorageSlowTest, SynchronizeTypesRemoveTypeAndAliasPropertyDeclara
Storage::PropertyDeclarationTraits::IsList))))));
}
TEST_F(ProjectStorageSlowTest, UpdateAliasPropertyIfPropertyIsOverloaded)
{
Storage::Types types{createTypesWithAliases()};
storage.synchronizeTypes(types, {sourceId1, sourceId2, sourceId3, sourceId4});
types[0].propertyDeclarations.push_back(
Storage::PropertyDeclaration{"objects",
Storage::NativeType{"QQuickItem"},
Storage::PropertyDeclarationTraits::IsList
| Storage::PropertyDeclarationTraits::IsReadOnly});
storage.synchronizeTypes({types[0]}, {sourceId1});
ASSERT_THAT(
storage.fetchTypes(),
Contains(
AllOf(IsStorageType(importId2,
"QAliasItem",
Storage::NativeType{"QQuickItem"},
TypeAccessSemantics::Reference,
sourceId3),
Field(&Storage::Type::propertyDeclarations,
UnorderedElementsAre(
IsPropertyDeclaration("items",
Storage::NativeType{"QQuickItem"},
Storage::PropertyDeclarationTraits::IsList
| Storage::PropertyDeclarationTraits::IsReadOnly),
IsPropertyDeclaration("objects",
Storage::NativeType{"QQuickItem"},
Storage::PropertyDeclarationTraits::IsList
| Storage::PropertyDeclarationTraits::IsReadOnly),
IsPropertyDeclaration("data",
Storage::NativeType{"QObject"},
Storage::PropertyDeclarationTraits::IsList))))));
}
TEST_F(ProjectStorageSlowTest, AliasPropertyIsOverloaded)
{
Storage::Types types{createTypesWithAliases()};
types[0].propertyDeclarations.push_back(
Storage::PropertyDeclaration{"objects",
Storage::NativeType{"QQuickItem"},
Storage::PropertyDeclarationTraits::IsList
| Storage::PropertyDeclarationTraits::IsReadOnly});
storage.synchronizeTypes(types, {sourceId1, sourceId2, sourceId3, sourceId4});
ASSERT_THAT(
storage.fetchTypes(),
Contains(
AllOf(IsStorageType(importId2,
"QAliasItem",
Storage::NativeType{"QQuickItem"},
TypeAccessSemantics::Reference,
sourceId3),
Field(&Storage::Type::propertyDeclarations,
UnorderedElementsAre(
IsPropertyDeclaration("items",
Storage::NativeType{"QQuickItem"},
Storage::PropertyDeclarationTraits::IsList
| Storage::PropertyDeclarationTraits::IsReadOnly),
IsPropertyDeclaration("objects",
Storage::NativeType{"QQuickItem"},
Storage::PropertyDeclarationTraits::IsList
| Storage::PropertyDeclarationTraits::IsReadOnly),
IsPropertyDeclaration("data",
Storage::NativeType{"QObject"},
Storage::PropertyDeclarationTraits::IsList))))));
}
TEST_F(ProjectStorageSlowTest, UpdateAliasPropertyIfOverloadedPropertyIsRemoved)
{
Storage::Types types{createTypesWithAliases()};
types[0].propertyDeclarations.push_back(
Storage::PropertyDeclaration{"objects",
Storage::NativeType{"QQuickItem"},
Storage::PropertyDeclarationTraits::IsList
| Storage::PropertyDeclarationTraits::IsReadOnly});
storage.synchronizeTypes(types, {sourceId1, sourceId2, sourceId3, sourceId4});
types[0].propertyDeclarations.pop_back();
storage.synchronizeTypes({types[0]}, {sourceId1});
ASSERT_THAT(storage.fetchTypes(),
Contains(AllOf(
IsStorageType(importId2,
"QAliasItem",
Storage::NativeType{"QQuickItem"},
TypeAccessSemantics::Reference,
sourceId3),
Field(&Storage::Type::propertyDeclarations,
UnorderedElementsAre(
IsPropertyDeclaration("items",
Storage::NativeType{"QQuickItem"},
Storage::PropertyDeclarationTraits::IsList
| Storage::PropertyDeclarationTraits::IsReadOnly),
IsPropertyDeclaration("objects",
Storage::NativeType{"QObject"},
Storage::PropertyDeclarationTraits::IsList),
IsPropertyDeclaration("data",
Storage::NativeType{"QObject"},
Storage::PropertyDeclarationTraits::IsList))))));
}
TEST_F(ProjectStorageSlowTest, RelinkAliasProperty)
{
Storage::Types types{createTypesWithAliases()};
types[1].propertyDeclarations[0].typeName = Storage::NativeType{"QObject2"};
storage.synchronizeTypes(types, {sourceId1, sourceId2, sourceId3, sourceId4});
types[3].importId = importId2;
storage.synchronizeTypes({types[3]}, {sourceId4});
ASSERT_THAT(storage.fetchTypes(),
Contains(AllOf(
IsStorageType(importId2,
"QAliasItem",
Storage::NativeType{"QQuickItem"},
TypeAccessSemantics::Reference,
sourceId3),
Field(&Storage::Type::propertyDeclarations,
UnorderedElementsAre(
IsPropertyDeclaration("items",
Storage::NativeType{"QQuickItem"},
Storage::PropertyDeclarationTraits::IsList
| Storage::PropertyDeclarationTraits::IsReadOnly),
IsPropertyDeclaration("objects",
Storage::NativeType{"QObject2"},
Storage::PropertyDeclarationTraits::IsList),
IsPropertyDeclaration("data",
Storage::NativeType{"QObject"},
Storage::PropertyDeclarationTraits::IsList))))));
}
} // namespace