From 54af3b4f4dd8a2885dcc57904f04d603bb444f24 Mon Sep 17 00:00:00 2001
From: Charles Krinke
Date: Sun, 10 Aug 2008 16:44:25 +0000
Subject: Mantis#1903. Thank you kindly, CMickeyb for a patch that: patch
attached replaces the tree walk algorithm used to build the folder hierarchy
with a single database query. That is, we replace 1 database query per folder
with 1 query for the root folder's properties and 1 query to retrieve the
entire collection of folders for a user.
---
OpenSim/Data/SQLite/SQLiteInventoryStore.cs | 97 +++++++++++++++++++++++++++--
1 file changed, 93 insertions(+), 4 deletions(-)
(limited to 'OpenSim/Data/SQLite')
diff --git a/OpenSim/Data/SQLite/SQLiteInventoryStore.cs b/OpenSim/Data/SQLite/SQLiteInventoryStore.cs
index ef4ef99..e29b99c 100644
--- a/OpenSim/Data/SQLite/SQLiteInventoryStore.cs
+++ b/OpenSim/Data/SQLite/SQLiteInventoryStore.cs
@@ -392,6 +392,7 @@ namespace OpenSim.Data.SQLite
{
folders.Add(buildFolder(row));
}
+
}
}
@@ -414,12 +415,100 @@ namespace OpenSim.Data.SQLite
///
public List getFolderHierarchy(LLUUID parentID)
{
- List folders = new List();
- getInventoryFolders(ref folders, Util.ToRawUuidString(parentID));
+ /* Note: There are subtle changes between this implementation of getFolderHierarchy and the previous one
+ * - We will only need to hit the database twice instead of n times.
+ * - We assume the database is well-formed - no stranded/dangling folders, all folders in heirarchy owned
+ * by the same person, each user only has 1 inventory heirarchy
+ * - The returned list is not ordered, instead of breadth-first ordered
+ There are basically 2 usage cases for getFolderHeirarchy:
+ 1) Getting the user's entire inventory heirarchy when they log in
+ 2) Finding a subfolder heirarchy to delete when emptying the trash.
+ This implementation will pull all inventory folders from the database, and then prune away any folder that
+ is not part of the requested sub-heirarchy. The theory is that it is cheaper to make 1 request from the
+ database than to make n requests. This pays off only if requested heirarchy is large.
+ By making this choice, we are making the worst case better at the cost of making the best case worse
+ - Francis
+ */
- for (int i = 0; i < folders.Count; i++)
- getInventoryFolders(ref folders, Util.ToRawUuidString(folders[i].ID));
+ List folders = new List();
+ DataRow[] folderRows = null, parentRow;
+ InventoryFolderBase parentFolder = null;
+ lock (ds)
+ {
+ /* Fetch the parent folder from the database to determine the agent ID.
+ * Then fetch all inventory folders for that agent from the agent ID.
+ */
+ DataTable inventoryFolderTable = ds.Tables["inventoryfolders"];
+ string selectExp = "UUID = '" + Util.ToRawUuidString(parentID) + "'";
+ parentRow = inventoryFolderTable.Select(selectExp); // Assume at most 1 result
+ if (parentRow.GetLength(0) >= 1) // No result means parent folder does not exist
+ {
+ parentFolder = buildFolder(parentRow[0]);
+ LLUUID agentID = parentFolder.Owner;
+ selectExp = "agentID = '" + Util.ToRawUuidString(agentID) + "'";
+ folderRows = inventoryFolderTable.Select(selectExp);
+ }
+ if ( folderRows!=null && folderRows.GetLength(0)>=1 ) // No result means parent folder does not exist
+ { // or has no children
+ /* if we're querying the root folder, just return an unordered list of all folders in the user's
+ * inventory
+ */
+ if (parentFolder.ParentID == LLUUID.Zero)
+ {
+ foreach (DataRow row in folderRows)
+ {
+ InventoryFolderBase curFolder = buildFolder(row);
+ if (curFolder.ID != parentID) // Return all folders except the parent folder of heirarchy
+ folders.Add(buildFolder(row));
+ }
+ } // If requesting root folder
+ /* else we are querying a non-root folder. We currently have a list of all of the user's folders,
+ * we must construct a list of all folders in the heirarchy below parentID.
+ * Our first step will be to construct a hash table of all folders, indexed by parent ID.
+ * Once we have constructed the hash table, we will do a breadth-first traversal on the tree using the
+ * hash table to find child folders.
+ */
+ else
+ { // Querying a non-root folder
+
+ // Build a hash table of all user's inventory folders, indexed by each folder's parent ID
+ Dictionary> hashtable =
+ new Dictionary>(folderRows.GetLength(0));
+
+ foreach (DataRow row in folderRows)
+ {
+ InventoryFolderBase curFolder = buildFolder(row);
+ if (curFolder.ParentID != LLUUID.Zero) // Discard root of tree - not needed
+ {
+ if ( hashtable.ContainsKey(curFolder.ParentID ) )
+ {
+ // Current folder already has a sibling - append to sibling list
+ hashtable[curFolder.ParentID].Add( curFolder );
+ }
+ else {
+ List siblingList = new List();
+ siblingList.Add( curFolder );
+ // Current folder has no known (yet) siblings
+ hashtable.Add( curFolder.ParentID, siblingList );
+ }
+ }
+ } // For all inventory folders
+
+ // Note: Could release the ds lock here - we don't access folderRows or the database anymore.
+ // This is somewhat of a moot point as the callers of this function usually lock db anyways.
+
+ if ( hashtable.ContainsKey( parentID ) ) // if requested folder does have children
+ folders.AddRange( hashtable[parentID] );
+
+ // BreadthFirstSearch build inventory tree **Note: folders.Count is *not* static
+ for ( int i = 0; i < folders.Count; i++ )
+ if (hashtable.ContainsKey(folders[i].ID))
+ folders.AddRange(hashtable[folders[i].ID]);
+
+ } // if requesting a subfolder heirarchy
+ } // if folder parentID exists and has children
+ } // lock ds
return folders;
}
--
cgit v1.1