[mapguide-commits] r10224 - in branches/4.0/MgDev: Common/Foundation/Data Server/src/UnitTesting

svn_mapguide at osgeo.org svn_mapguide at osgeo.org
Wed Oct 21 15:45:33 PDT 2026


Author: jng
Date: 2026-10-21 15:45:33 -0700 (Wed, 21 Oct 2026)
New Revision: 10224

Modified:
   branches/4.0/MgDev/Common/Foundation/Data/NamedCollection.cpp
   branches/4.0/MgDev/Server/src/UnitTesting/TestMisc.cpp
Log:
Guard against stale refs (by name) in MgNamedCollection

Fixes #1268

Modified: branches/4.0/MgDev/Common/Foundation/Data/NamedCollection.cpp
===================================================================
--- branches/4.0/MgDev/Common/Foundation/Data/NamedCollection.cpp	2026-10-21 22:40:44 UTC (rev 10223)
+++ branches/4.0/MgDev/Common/Foundation/Data/NamedCollection.cpp	2026-10-21 22:45:33 UTC (rev 10224)
@@ -159,8 +159,12 @@
                 // the requested object.
                 if (Compare(obj->GetName(), name) != 0)
                 {
-                    // The names do not match so we need to do a linear search
-                    // to find it because the collection and the map don't match.
+                    // The names do not match, so the object was renamed after it
+                    // was added to the map and the entry is keyed by a name it no
+                    // longer has. Drop it, and do a linear search to find the
+                    // requested object because the collection and the map don't
+                    // match.
+                    RemoveMap(obj);
                     obj = NULL;
                 }
             }
@@ -184,6 +188,13 @@
                 }
             }
         }
+
+        // The object was not indexed under its current name, so index it now.
+        if (m_pNameMap && obj != NULL)
+        {
+            RemoveMap(obj);
+            InsertMap(obj);
+        }
     }
 
     return obj.Detach();
@@ -332,9 +343,12 @@
 
     if (m_pNameMap)
     {
-        // If map is built, use it since it is faster.
+        // If map is built, use it since it is faster. Its entry for a name can
+        // be stale though, because an element's name can be changed after it
+        // was added, so only a hit for the element itself is conclusive.
         Ptr<MgNamedSerializable> item = GetMap( ((MgNamedSerializable*)value)->GetName() );
-        return(item  != NULL );
+        if (item.p == value)
+            return true;
     }
 
     // otherwise, linear search
@@ -551,11 +565,16 @@
 // Remove the given element from the map.
 void MgNamedCollection::RemoveMap(const MgNamedSerializable* value)
 {
-    // handle the current case sensitivity of the element name.
-    if (m_bCaseSensitive)
-        m_pNameMap->erase( ((MgNamedSerializable*)value)->GetName() );
-    else
-        m_pNameMap->erase( Lower(((MgNamedSerializable*)value)->GetName()) );
+    // The element's name may have changed since it was added to the map, so its
+    // entries have to be located by element rather than by name.
+    std::map<STRING, MgNamedSerializable* >::iterator iter = m_pNameMap->begin();
+    while (iter != m_pNameMap->end())
+    {
+        if (iter->second == value)
+            m_pNameMap->erase(iter++);
+        else
+            ++iter;
+    }
 }
 
 

Modified: branches/4.0/MgDev/Server/src/UnitTesting/TestMisc.cpp
===================================================================
--- branches/4.0/MgDev/Server/src/UnitTesting/TestMisc.cpp	2026-10-21 22:40:44 UTC (rev 10223)
+++ branches/4.0/MgDev/Server/src/UnitTesting/TestMisc.cpp	2026-10-21 22:45:33 UTC (rev 10224)
@@ -1017,6 +1017,76 @@
     }
 }
 
+TEST_CASE("TracTicket1268", "[Misc]")
+{
+    try
+    {
+        // MgNamedCollection indexes its elements by name once the collection
+        // outgrows COLL_MAP_THRESHOLD. An element can be renamed after that,
+        // and removal used to erase the map entry by the element's current
+        // name, so the entry keyed by the old name survived and kept pointing
+        // at the removed element.
+        Ptr<MgNamedCollection> coll = new MgNamedCollection(false);
+
+        Ptr<MgFeatureSchema> renamed;
+        for (INT32 i = 0; i < COLL_MAP_THRESHOLD + 2; i++)
+        {
+            STRING suffix;
+            MgUtil::Int32ToString(i, suffix);
+
+            STRING name = L"Schema" + suffix;
+            Ptr<MgFeatureSchema> schema = new MgFeatureSchema(name, L"");
+            coll->Add(schema);
+
+            if (i == 0)
+                renamed = schema;
+        }
+
+        // ========================== Begin Test ======================== //
+
+        // Renaming an element does not take it out of the collection.
+        renamed->SetName(L"RenamedSchema");
+
+        CHECK(coll->Contains(renamed));
+
+        Ptr<MgNamedSerializable> found = coll->FindItem(L"RenamedSchema");
+        CHECK(found.p == renamed.p);
+
+        // Removing the element must remove it completely: neither of the names
+        // it has had may resolve to it afterwards.
+        REQUIRE(coll->Remove(renamed));
+        CHECK(coll->FindItem(L"RenamedSchema") == nullptr);
+        CHECK(coll->FindItem(L"Schema0") == nullptr);
+
+        // An element that merely carries the vacated name is not in the
+        // collection until it is added.
+        Ptr<MgFeatureSchema> replacement = new MgFeatureSchema(L"Schema0", L"");
+        CHECK(!coll->Contains(replacement));
+
+        // Drop the last reference to the removed element. A lookup by the name
+        // it used to have must not reach the freed object.
+        found = NULL;
+        renamed = NULL;
+        CHECK(coll->FindItem(L"Schema0") == nullptr);
+
+        // The vacated name is free to be used again.
+        coll->Add(replacement);
+        found = coll->FindItem(L"Schema0");
+        REQUIRE(found.p == replacement.p);
+        CHECK(coll->Contains(replacement));
+    }
+    catch (MgException* e)
+    {
+        STRING message = e->GetDetails(TestServiceFactory::TEST_LOCALE);
+        SAFE_RELEASE(e);
+        FAIL(MG_WCHAR_TO_CHAR(message.c_str()));
+    }
+    catch (...)
+    {
+        throw;
+    }
+}
+
 TEST_CASE("TracTicket1304", "[Misc]")
 {
     try



More information about the mapguide-commits mailing list