Skip to content
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
a559cdc
First set of changes for a bulk element deletion API
RohitPtnkr1996 Feb 26, 2026
cc121f6
Added basic api path to delete definition elements
RohitPtnkr1996 Feb 27, 2026
b30fe30
Reverted accidental commit
RohitPtnkr1996 Feb 27, 2026
c138bca
Updated bulk element deletion APIs
RohitPtnkr1996 Mar 3, 2026
780ec74
Moved tests over to itwinjs repo
RohitPtnkr1996 Mar 3, 2026
bd01a7e
Updated native library
RohitPtnkr1996 Mar 3, 2026
2f4af23
Re-worked the code scope violation detection logic, reverted partfile…
RohitPtnkr1996 Mar 3, 2026
1535fd8
Updated function arg, bugfix, reworked the logic a bit
RohitPtnkr1996 Mar 5, 2026
b0b3d30
Removed experimental flag
RohitPtnkr1996 Mar 5, 2026
26641e0
Merge branch 'main' into rohitptnkr/bulk-element-deletion
RohitPtnkr1996 Mar 5, 2026
c00783b
Cleaned up inconsistencies after removing the temporary flag
RohitPtnkr1996 Mar 5, 2026
a96c3c0
Fixed bug
RohitPtnkr1996 Mar 5, 2026
16042ed
Addressed co-pilot review comments
RohitPtnkr1996 Mar 5, 2026
bd1bec5
Merge branch 'main' into rohitptnkr/bulk-element-deletion
RohitPtnkr1996 Mar 6, 2026
e319f59
Made the function comments clearer
RohitPtnkr1996 Mar 6, 2026
78e5941
Merge branch 'rohitptnkr/bulk-element-deletion' of https://github.com…
RohitPtnkr1996 Mar 6, 2026
5a09584
Merge branch 'main' into rohitptnkr/bulk-element-deletion
RohitPtnkr1996 Mar 9, 2026
4f680c4
Merge branch 'main' into rohitptnkr/bulk-element-deletion
RohitPtnkr1996 Mar 10, 2026
c155d2e
Updated API documentation
RohitPtnkr1996 Mar 10, 2026
76b0f7f
Extended API to handle sub model hierarchies
RohitPtnkr1996 Mar 13, 2026
b52aeb9
Cleaned up API
RohitPtnkr1996 Mar 13, 2026
407f7af
Fixed a bug
RohitPtnkr1996 Mar 13, 2026
47cce72
Some bug fixes
RohitPtnkr1996 Mar 16, 2026
a25f388
Renamed functions to be clear of intent
RohitPtnkr1996 Mar 16, 2026
e0ecb43
Merge branch 'main' into rohitptnkr/bulk-element-deletion
RohitPtnkr1996 Mar 16, 2026
e8cb310
Merge branch 'main' into rohitptnkr/bulk-element-deletion
RohitPtnkr1996 Mar 20, 2026
01b7288
Merge branch 'main' into rohitptnkr/bulk-element-deletion
RohitPtnkr1996 Mar 26, 2026
b0b6749
Updated the code to use direct sqls instead of element id sets
RohitPtnkr1996 Mar 28, 2026
df09cf1
Merge branch 'rohitptnkr/bulk-element-deletion' of https://github.com…
RohitPtnkr1996 Mar 28, 2026
3c0b66b
Final minor changes
RohitPtnkr1996 Mar 30, 2026
d3d9bca
Merge branch 'main' into rohitptnkr/bulk-element-deletion
RohitPtnkr1996 Mar 30, 2026
8b53689
Added log messages
RohitPtnkr1996 Mar 30, 2026
6226c1f
Merge branch 'rohitptnkr/bulk-element-deletion' of https://github.com…
RohitPtnkr1996 Mar 30, 2026
1fa1bf0
Minor optimization for geometric elements
RohitPtnkr1996 Apr 1, 2026
dea889b
Merge branch 'main' into rohitptnkr/bulk-element-deletion
RohitPtnkr1996 Apr 1, 2026
e511b57
Merge branch 'main' into rohitptnkr/bulk-element-deletion
RohitPtnkr1996 Apr 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 88 additions & 29 deletions iModelCore/iModelPlatform/DgnCore/DefinitionElementUsageInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@
/*---------------------------------------------------------------------------------**//**
* @bsimethod
+---------------+---------------+---------------+---------------+---------------+------*/
DefinitionElementUsageInfoPtr DefinitionElementUsageInfo::Create(DgnDbR db, BeSQLite::IdSet<DgnElementId> const& elementIds)
DefinitionElementUsageInfoPtr DefinitionElementUsageInfo::Create(DgnDbR db, BeSQLite::IdSet<DgnElementId> const& elementIds, std::shared_ptr<BeSQLite::IdSet<BeInt64Id>> excludeIds)
{
DefinitionElementUsageInfoPtr context = new DefinitionElementUsageInfo(db);
if (!context.IsValid())
return nullptr;

context->m_excludeIds = excludeIds;
context->Initialize(elementIds);
context->QueryUsage();
return context;
Expand Down Expand Up @@ -215,9 +216,14 @@ void DefinitionElementUsageInfo::QueryUsage()
+---------------+---------------+---------------+---------------+---------------+------*/
bool DefinitionElementUsageInfo::IsSpatialCategoryUsed(DgnCategoryId categoryId) const
{
Utf8CP sql = "SELECT ECInstanceId FROM " BIS_SCHEMA(BIS_CLASS_GeometricElement3d) " WHERE Category.Id=? LIMIT 1";
CachedECSqlStatementPtr statement = m_db.GetPreparedECSqlStatement(sql);
Utf8String sql = "SELECT ECInstanceId FROM " BIS_SCHEMA(BIS_CLASS_GeometricElement3d) " WHERE Category.Id=?";
if (m_excludeIds)
sql.append(" AND NOT InVirtualSet(?, ECInstanceId)");
sql.append(" LIMIT 1");
CachedECSqlStatementPtr statement = m_db.GetPreparedECSqlStatement(sql.c_str());
statement->BindId(1, categoryId);
if (m_excludeIds)
statement->BindVirtualSet(2, m_excludeIds);
return BE_SQLITE_ROW == statement->Step();
}

Expand All @@ -226,9 +232,14 @@ bool DefinitionElementUsageInfo::IsSpatialCategoryUsed(DgnCategoryId categoryId)
+---------------+---------------+---------------+---------------+---------------+------*/
bool DefinitionElementUsageInfo::IsDrawingCategoryUsed(DgnCategoryId categoryId) const
{
Utf8CP sql = "SELECT ECInstanceId FROM " BIS_SCHEMA(BIS_CLASS_GeometricElement2d) " WHERE Category.Id=? LIMIT 1";
CachedECSqlStatementPtr statement = m_db.GetPreparedECSqlStatement(sql);
Utf8String sql = "SELECT ECInstanceId FROM " BIS_SCHEMA(BIS_CLASS_GeometricElement2d) " WHERE Category.Id=?";
if (m_excludeIds)
sql.append(" AND NOT InVirtualSet(?, ECInstanceId)");
sql.append(" LIMIT 1");
CachedECSqlStatementPtr statement = m_db.GetPreparedECSqlStatement(sql.c_str());
statement->BindId(1, categoryId);
if (m_excludeIds)
statement->BindVirtualSet(2, m_excludeIds);
return BE_SQLITE_ROW == statement->Step();
}

Expand All @@ -237,9 +248,14 @@ bool DefinitionElementUsageInfo::IsDrawingCategoryUsed(DgnCategoryId categoryId)
+---------------+---------------+---------------+---------------+---------------+------*/
bool DefinitionElementUsageInfo::IsDisplayStyleUsed(DgnElementId displayStyleId) const
{
Utf8CP sql = "SELECT ECInstanceId FROM " BIS_SCHEMA(BIS_CLASS_ViewDefinition) " WHERE DisplayStyle.Id=? LIMIT 1";
CachedECSqlStatementPtr statement = m_db.GetPreparedECSqlStatement(sql);
Utf8String sql = "SELECT ECInstanceId FROM " BIS_SCHEMA(BIS_CLASS_ViewDefinition) " WHERE DisplayStyle.Id=?";
if (m_excludeIds)
sql.append(" AND NOT InVirtualSet(?, ECInstanceId)");
sql.append(" LIMIT 1");
CachedECSqlStatementPtr statement = m_db.GetPreparedECSqlStatement(sql.c_str());
statement->BindId(1, displayStyleId);
if (m_excludeIds)
statement->BindVirtualSet(2, m_excludeIds);
return BE_SQLITE_ROW == statement->Step();
}

Expand All @@ -248,9 +264,14 @@ bool DefinitionElementUsageInfo::IsDisplayStyleUsed(DgnElementId displayStyleId)
+---------------+---------------+---------------+---------------+---------------+------*/
bool DefinitionElementUsageInfo::IsCategorySelectorUsed(DgnElementId categorySelectorId) const
{
Utf8CP sql = "SELECT ECInstanceId FROM " BIS_SCHEMA(BIS_CLASS_ViewDefinition) " WHERE CategorySelector.Id=? LIMIT 1";
CachedECSqlStatementPtr statement = m_db.GetPreparedECSqlStatement(sql);
Utf8String sql = "SELECT ECInstanceId FROM " BIS_SCHEMA(BIS_CLASS_ViewDefinition) " WHERE CategorySelector.Id=?";
if (m_excludeIds)
sql.append(" AND NOT InVirtualSet(?, ECInstanceId)");
sql.append(" LIMIT 1");
CachedECSqlStatementPtr statement = m_db.GetPreparedECSqlStatement(sql.c_str());
statement->BindId(1, categorySelectorId);
if (m_excludeIds)
statement->BindVirtualSet(2, m_excludeIds);
return BE_SQLITE_ROW == statement->Step();
}

Expand All @@ -259,9 +280,14 @@ bool DefinitionElementUsageInfo::IsCategorySelectorUsed(DgnElementId categorySel
+---------------+---------------+---------------+---------------+---------------+------*/
bool DefinitionElementUsageInfo::IsModelSelectorUsed(DgnElementId modelSelectorId) const
{
Utf8CP sql = "SELECT ECInstanceId FROM " BIS_SCHEMA(BIS_CLASS_SpatialViewDefinition) " WHERE ModelSelector.Id=? LIMIT 1";
CachedECSqlStatementPtr statement = m_db.GetPreparedECSqlStatement(sql);
Utf8String sql = "SELECT ECInstanceId FROM " BIS_SCHEMA(BIS_CLASS_SpatialViewDefinition) " WHERE ModelSelector.Id=?";
if (m_excludeIds)
sql.append(" AND NOT InVirtualSet(?, ECInstanceId)");
sql.append(" LIMIT 1");
CachedECSqlStatementPtr statement = m_db.GetPreparedECSqlStatement(sql.c_str());
statement->BindId(1, modelSelectorId);
if (m_excludeIds)
statement->BindVirtualSet(2, m_excludeIds);
return BE_SQLITE_ROW == statement->Step();
}

Expand All @@ -272,24 +298,39 @@ bool DefinitionElementUsageInfo::IsViewDefinitionUsed(DgnViewId viewDefinitionId
{
if (m_db.Schemas().GetClass(BIS_ECSCHEMA_NAME, BIS_CLASS_SectionDrawing)->GetPropertyP("SpatialView") != nullptr)
{
Utf8CP sectionDrawingSql = "SELECT ECInstanceId FROM " BIS_SCHEMA(BIS_CLASS_SectionDrawing) " WHERE SpatialView.Id=? LIMIT 1";
CachedECSqlStatementPtr sectionDrawingStatement = m_db.GetPreparedECSqlStatement(sectionDrawingSql);
Utf8String sectionDrawingSql = "SELECT ECInstanceId FROM " BIS_SCHEMA(BIS_CLASS_SectionDrawing) " WHERE SpatialView.Id=?";
if (m_excludeIds)
sectionDrawingSql.append(" AND NOT InVirtualSet(?, ECInstanceId)");
sectionDrawingSql.append(" LIMIT 1");
CachedECSqlStatementPtr sectionDrawingStatement = m_db.GetPreparedECSqlStatement(sectionDrawingSql.c_str());
sectionDrawingStatement->BindId(1, viewDefinitionId);
if (m_excludeIds)
sectionDrawingStatement->BindVirtualSet(2, m_excludeIds);
if (BE_SQLITE_ROW == sectionDrawingStatement->Step())
return true;
}

Utf8CP viewAttachmentSql = "SELECT ECInstanceId FROM " BIS_SCHEMA(BIS_CLASS_ViewAttachment) " WHERE View.Id=? LIMIT 1";
CachedECSqlStatementPtr viewAttachmentStatement = m_db.GetPreparedECSqlStatement(viewAttachmentSql);
Utf8String viewAttachmentSql = "SELECT ECInstanceId FROM " BIS_SCHEMA(BIS_CLASS_ViewAttachment) " WHERE View.Id=?";
if (m_excludeIds)
viewAttachmentSql.append(" AND NOT InVirtualSet(?, ECInstanceId)");
viewAttachmentSql.append(" LIMIT 1");
CachedECSqlStatementPtr viewAttachmentStatement = m_db.GetPreparedECSqlStatement(viewAttachmentSql.c_str());
viewAttachmentStatement->BindId(1, viewDefinitionId);
if (m_excludeIds)
viewAttachmentStatement->BindVirtualSet(2, m_excludeIds);
if (BE_SQLITE_ROW == viewAttachmentStatement->Step())
return true;

if (m_db.Schemas().GetClassId(BIS_ECSCHEMA_NAME, BIS_CLASS_SectionDrawingLocation).IsValid())
{
Utf8CP sectionDrawingLocationSql = "SELECT ECInstanceId FROM " BIS_SCHEMA(BIS_CLASS_SectionDrawingLocation) " WHERE SectionView.Id=? LIMIT 1";
CachedECSqlStatementPtr sectionDrawingLocationStatement = m_db.GetPreparedECSqlStatement(sectionDrawingLocationSql);
Utf8String sectionDrawingLocationSql = "SELECT ECInstanceId FROM " BIS_SCHEMA(BIS_CLASS_SectionDrawingLocation) " WHERE SectionView.Id=?";
if (m_excludeIds)
sectionDrawingLocationSql.append(" AND NOT InVirtualSet(?, ECInstanceId)");
sectionDrawingLocationSql.append(" LIMIT 1");
CachedECSqlStatementPtr sectionDrawingLocationStatement = m_db.GetPreparedECSqlStatement(sectionDrawingLocationSql.c_str());
sectionDrawingLocationStatement->BindId(1, viewDefinitionId);
if (m_excludeIds)
sectionDrawingLocationStatement->BindVirtualSet(2, m_excludeIds);
if (BE_SQLITE_ROW == sectionDrawingLocationStatement->Step())
return true;
}
Expand All @@ -315,12 +356,15 @@ void DefinitionElementUsageInfo::ScanGeometricElement3ds(std::shared_ptr<BeSQLit
Utf8String sql = "SELECT ECInstanceId FROM " BIS_SCHEMA(BIS_CLASS_GeometricElement3d) " WHERE GeometryStream IS NOT NULL";
if (nullptr != categoriesToScan)
sql.append(" AND (InVirtualSet(?, Category.Id))");
if (m_excludeIds)
sql.append(" AND NOT InVirtualSet(?, ECInstanceId)");

CachedECSqlStatementPtr statement = m_db.GetPreparedECSqlStatement(sql.c_str());
int bindIdx = 1;
if (nullptr != categoriesToScan)
{
statement->BindVirtualSet(1, categoriesToScan);
}
statement->BindVirtualSet(bindIdx++, categoriesToScan);
if (m_excludeIds)
statement->BindVirtualSet(bindIdx, m_excludeIds);

while (BE_SQLITE_ROW == statement->Step())
{
Expand All @@ -337,12 +381,15 @@ void DefinitionElementUsageInfo::ScanGeometricElement2ds(std::shared_ptr<BeSQLit
Utf8String sql = "SELECT ECInstanceId FROM " BIS_SCHEMA(BIS_CLASS_GeometricElement2d) " WHERE GeometryStream IS NOT NULL";
if (nullptr != categoriesToScan)
sql.append(" AND (InVirtualSet(?, Category.Id))");
if (m_excludeIds)
sql.append(" AND NOT InVirtualSet(?, ECInstanceId)");

CachedECSqlStatementPtr statement = m_db.GetPreparedECSqlStatement(sql.c_str());
int bindIdx = 1;
if (nullptr != categoriesToScan)
{
statement->BindVirtualSet(1, categoriesToScan);
}
statement->BindVirtualSet(bindIdx++, categoriesToScan);
if (m_excludeIds)
statement->BindVirtualSet(bindIdx, m_excludeIds);

while (BE_SQLITE_ROW == statement->Step())
{
Expand All @@ -367,8 +414,12 @@ void DefinitionElementUsageInfo::ScanGeometricElement(DgnElementId elementId)
+---------------+---------------+---------------+---------------+---------------+------*/
void DefinitionElementUsageInfo::ScanGeometryParts()
{
Utf8CP sql = "SELECT ECInstanceId FROM " BIS_SCHEMA(BIS_CLASS_GeometryPart) " WHERE GeometryStream IS NOT NULL";
CachedECSqlStatementPtr statement = m_db.GetPreparedECSqlStatement(sql);
Utf8String sql = "SELECT ECInstanceId FROM " BIS_SCHEMA(BIS_CLASS_GeometryPart) " WHERE GeometryStream IS NOT NULL";
if (m_excludeIds)
sql.append(" AND NOT InVirtualSet(?, ECInstanceId)");
CachedECSqlStatementPtr statement = m_db.GetPreparedECSqlStatement(sql.c_str());
if (m_excludeIds)
statement->BindVirtualSet(1, m_excludeIds);
while (BE_SQLITE_ROW == statement->Step())
{
DgnElementId elementId = statement->GetValueId<DgnElementId>(0);
Expand All @@ -383,8 +434,12 @@ void DefinitionElementUsageInfo::ScanGeometryParts()
+---------------+---------------+---------------+---------------+---------------+------*/
void DefinitionElementUsageInfo::ScanLineStyles()
{
Utf8CP sql = "SELECT ECInstanceId FROM " BIS_SCHEMA(BIS_CLASS_LineStyle);
CachedECSqlStatementPtr statement = m_db.GetPreparedECSqlStatement(sql);
Utf8String sql = "SELECT ECInstanceId FROM " BIS_SCHEMA(BIS_CLASS_LineStyle);
if (m_excludeIds)
sql.append(" WHERE NOT InVirtualSet(?, ECInstanceId)");
CachedECSqlStatementPtr statement = m_db.GetPreparedECSqlStatement(sql.c_str());
if (m_excludeIds)
statement->BindVirtualSet(1, m_excludeIds);
while (BE_SQLITE_ROW == statement->Step())
{
DgnElementId lineStyleElementId = statement->GetValueId<DgnElementId>(0);
Expand Down Expand Up @@ -460,8 +515,12 @@ void DefinitionElementUsageInfo::ScanLineStyleComponent(LsComponentCP lineStyleC
+---------------+---------------+---------------+---------------+---------------+------*/
void DefinitionElementUsageInfo::ScanDisplayStyles()
{
Utf8CP sql = "SELECT ECInstanceId FROM " BIS_SCHEMA(BIS_CLASS_DisplayStyle3d);
CachedECSqlStatementPtr statement = m_db.GetPreparedECSqlStatement(sql);
Utf8String sql = "SELECT ECInstanceId FROM " BIS_SCHEMA(BIS_CLASS_DisplayStyle3d);
if (m_excludeIds)
sql.append(" WHERE NOT InVirtualSet(?, ECInstanceId)");
CachedECSqlStatementPtr statement = m_db.GetPreparedECSqlStatement(sql.c_str());
if (m_excludeIds)
statement->BindVirtualSet(1, m_excludeIds);
while (BE_SQLITE_ROW == statement->Step())
{
DgnElementId displayStyleId = statement->GetValueId<DgnElementId>(0);
Expand Down
6 changes: 5 additions & 1 deletion iModelCore/iModelPlatform/DgnCore/DgnElement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1046,7 +1046,11 @@ void DgnElement::_OnDeleted() const
CallJsPostHandler("onDeleted");
CallAppData(OnDeletedCaller());
GetDgnDb().Elements().DropFromPool(*this);
deleteLinkTableRelationships(GetDgnDb(), GetElementId());

// For a bulk delete operation, the relationship classes will be handled separately
if (!GetDgnDb().Elements().IsBulkOperation())
deleteLinkTableRelationships(GetDgnDb(), GetElementId());

DgnModelPtr model = GetModel();
if (model.IsValid())
model->_OnDeletedElement(m_elementId);
Expand Down
Loading