diff options
-rw-r--r-- | OpenSim/Data/SQLite/Resources/InventoryStore.migrations | 39 | ||||
-rw-r--r-- | OpenSim/Data/SQLite/SQLiteInventoryStore.cs | 916 | ||||
-rw-r--r-- | OpenSim/Data/Tests/InventoryTests.cs | 5 |
3 files changed, 0 insertions, 960 deletions
diff --git a/OpenSim/Data/SQLite/Resources/InventoryStore.migrations b/OpenSim/Data/SQLite/Resources/InventoryStore.migrations deleted file mode 100644 index 2b48623..0000000 --- a/OpenSim/Data/SQLite/Resources/InventoryStore.migrations +++ /dev/null | |||
@@ -1,39 +0,0 @@ | |||
1 | :VERSION 5 | ||
2 | |||
3 | BEGIN TRANSACTION; | ||
4 | |||
5 | CREATE TABLE IF NOT EXISTS inventoryfolders( | ||
6 | folderName varchar(64), | ||
7 | type integer, | ||
8 | version integer, | ||
9 | folderID varchar(36) primary key, | ||
10 | agentID varchar(36) not null default '00000000-0000-0000-0000-000000000000', | ||
11 | parentFolderID varchar(36) not null default '00000000-0000-0000-0000-000000000000'); | ||
12 | CREATE INDEX inventoryfolders_agentid on inventoryfolders(agentID); | ||
13 | CREATE INDEX inventoryfolders_parentid on inventoryfolders(parentFolderID); | ||
14 | |||
15 | CREATE TABLE IF NOT EXISTS inventoryitems( | ||
16 | assetID varchar(36), | ||
17 | assetType integer, | ||
18 | inventoryName varchar(64), | ||
19 | inventoryDescription varchar(128), | ||
20 | inventoryNextPermissions integer, | ||
21 | inventoryCurrentPermissions integer, | ||
22 | invType integer, | ||
23 | creatorID varchar(128), | ||
24 | inventoryBasePermissions integer, | ||
25 | inventoryEveryOnePermissions integer, | ||
26 | salePrice integer default 99, | ||
27 | saleType integer default 0, | ||
28 | creationDate integer default 2000, | ||
29 | groupID varchar(36) default '00000000-0000-0000-0000-000000000000', | ||
30 | groupOwned integer default 0, | ||
31 | flags integer default 0, | ||
32 | inventoryID varchar(36) primary key, | ||
33 | parentFolderID varchar(36) not null default '00000000-0000-0000-0000-000000000000', | ||
34 | avatarID varchar(36) not null default '00000000-0000-0000-0000-000000000000', | ||
35 | inventoryGroupPermissions integer not null default 0); | ||
36 | CREATE INDEX inventoryitems_parentfolderid on inventoryitems(parentFolderID); | ||
37 | CREATE INDEX inventoryitems_avatarid on inventoryitems(avatarID); | ||
38 | |||
39 | COMMIT; | ||
diff --git a/OpenSim/Data/SQLite/SQLiteInventoryStore.cs b/OpenSim/Data/SQLite/SQLiteInventoryStore.cs deleted file mode 100644 index 7d493ca..0000000 --- a/OpenSim/Data/SQLite/SQLiteInventoryStore.cs +++ /dev/null | |||
@@ -1,916 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSimulator Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.Collections.Generic; | ||
30 | using System.Data; | ||
31 | using System.Reflection; | ||
32 | using log4net; | ||
33 | #if CSharpSqlite | ||
34 | using Community.CsharpSqlite.Sqlite; | ||
35 | #else | ||
36 | using Mono.Data.Sqlite; | ||
37 | #endif | ||
38 | using OpenMetaverse; | ||
39 | using OpenSim.Framework; | ||
40 | |||
41 | namespace OpenSim.Data.SQLite | ||
42 | { | ||
43 | /// <summary> | ||
44 | /// An Inventory Interface to the SQLite database | ||
45 | /// </summary> | ||
46 | public class SQLiteInventoryStore : SQLiteUtil, IInventoryDataPlugin | ||
47 | { | ||
48 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
49 | |||
50 | private const string invItemsSelect = "select * from inventoryitems"; | ||
51 | private const string invFoldersSelect = "select * from inventoryfolders"; | ||
52 | |||
53 | private static SqliteConnection conn; | ||
54 | private static DataSet ds; | ||
55 | private static SqliteDataAdapter invItemsDa; | ||
56 | private static SqliteDataAdapter invFoldersDa; | ||
57 | |||
58 | private static bool m_Initialized = false; | ||
59 | |||
60 | public void Initialise() | ||
61 | { | ||
62 | m_log.Info("[SQLiteInventoryData]: " + Name + " cannot be default-initialized!"); | ||
63 | throw new PluginNotInitialisedException(Name); | ||
64 | } | ||
65 | |||
66 | /// <summary> | ||
67 | /// <list type="bullet"> | ||
68 | /// <item>Initialises Inventory interface</item> | ||
69 | /// <item>Loads and initialises a new SQLite connection and maintains it.</item> | ||
70 | /// <item>use default URI if connect string string is empty.</item> | ||
71 | /// </list> | ||
72 | /// </summary> | ||
73 | /// <param name="dbconnect">connect string</param> | ||
74 | public void Initialise(string dbconnect) | ||
75 | { | ||
76 | if (!m_Initialized) | ||
77 | { | ||
78 | m_Initialized = true; | ||
79 | |||
80 | if (Util.IsWindows()) | ||
81 | Util.LoadArchSpecificWindowsDll("sqlite3.dll"); | ||
82 | |||
83 | if (dbconnect == string.Empty) | ||
84 | { | ||
85 | dbconnect = "URI=file:inventoryStore.db,version=3"; | ||
86 | } | ||
87 | m_log.Info("[INVENTORY DB]: Sqlite - connecting: " + dbconnect); | ||
88 | conn = new SqliteConnection(dbconnect); | ||
89 | |||
90 | conn.Open(); | ||
91 | |||
92 | Assembly assem = GetType().Assembly; | ||
93 | Migration m = new Migration(conn, assem, "InventoryStore"); | ||
94 | m.Update(); | ||
95 | |||
96 | SqliteCommand itemsSelectCmd = new SqliteCommand(invItemsSelect, conn); | ||
97 | invItemsDa = new SqliteDataAdapter(itemsSelectCmd); | ||
98 | // SqliteCommandBuilder primCb = new SqliteCommandBuilder(primDa); | ||
99 | |||
100 | SqliteCommand foldersSelectCmd = new SqliteCommand(invFoldersSelect, conn); | ||
101 | invFoldersDa = new SqliteDataAdapter(foldersSelectCmd); | ||
102 | |||
103 | ds = new DataSet(); | ||
104 | |||
105 | ds.Tables.Add(createInventoryFoldersTable()); | ||
106 | invFoldersDa.Fill(ds.Tables["inventoryfolders"]); | ||
107 | setupFoldersCommands(invFoldersDa, conn); | ||
108 | CreateDataSetMapping(invFoldersDa, "inventoryfolders"); | ||
109 | m_log.Info("[INVENTORY DB]: Populated Inventory Folders Definitions"); | ||
110 | |||
111 | ds.Tables.Add(createInventoryItemsTable()); | ||
112 | invItemsDa.Fill(ds.Tables["inventoryitems"]); | ||
113 | setupItemsCommands(invItemsDa, conn); | ||
114 | CreateDataSetMapping(invItemsDa, "inventoryitems"); | ||
115 | m_log.Info("[INVENTORY DB]: Populated Inventory Items Definitions"); | ||
116 | |||
117 | ds.AcceptChanges(); | ||
118 | } | ||
119 | } | ||
120 | |||
121 | /// <summary> | ||
122 | /// Closes the inventory interface | ||
123 | /// </summary> | ||
124 | public void Dispose() | ||
125 | { | ||
126 | if (conn != null) | ||
127 | { | ||
128 | conn.Close(); | ||
129 | conn = null; | ||
130 | } | ||
131 | if (invItemsDa != null) | ||
132 | { | ||
133 | invItemsDa.Dispose(); | ||
134 | invItemsDa = null; | ||
135 | } | ||
136 | if (invFoldersDa != null) | ||
137 | { | ||
138 | invFoldersDa.Dispose(); | ||
139 | invFoldersDa = null; | ||
140 | } | ||
141 | if (ds != null) | ||
142 | { | ||
143 | ds.Dispose(); | ||
144 | ds = null; | ||
145 | } | ||
146 | } | ||
147 | |||
148 | /// <summary> | ||
149 | /// | ||
150 | /// </summary> | ||
151 | /// <param name="row"></param> | ||
152 | /// <returns></returns> | ||
153 | public InventoryItemBase buildItem(DataRow row) | ||
154 | { | ||
155 | InventoryItemBase item = new InventoryItemBase(); | ||
156 | item.ID = new UUID((string) row["UUID"]); | ||
157 | item.AssetID = new UUID((string) row["assetID"]); | ||
158 | item.AssetType = Convert.ToInt32(row["assetType"]); | ||
159 | item.InvType = Convert.ToInt32(row["invType"]); | ||
160 | item.Folder = new UUID((string) row["parentFolderID"]); | ||
161 | item.Owner = new UUID((string) row["avatarID"]); | ||
162 | item.CreatorIdentification = (string)row["creatorsID"]; | ||
163 | item.Name = (string) row["inventoryName"]; | ||
164 | item.Description = (string) row["inventoryDescription"]; | ||
165 | |||
166 | item.NextPermissions = Convert.ToUInt32(row["inventoryNextPermissions"]); | ||
167 | item.CurrentPermissions = Convert.ToUInt32(row["inventoryCurrentPermissions"]); | ||
168 | item.BasePermissions = Convert.ToUInt32(row["inventoryBasePermissions"]); | ||
169 | item.EveryOnePermissions = Convert.ToUInt32(row["inventoryEveryOnePermissions"]); | ||
170 | item.GroupPermissions = Convert.ToUInt32(row["inventoryGroupPermissions"]); | ||
171 | |||
172 | // new fields | ||
173 | if (!Convert.IsDBNull(row["salePrice"])) | ||
174 | item.SalePrice = Convert.ToInt32(row["salePrice"]); | ||
175 | |||
176 | if (!Convert.IsDBNull(row["saleType"])) | ||
177 | item.SaleType = Convert.ToByte(row["saleType"]); | ||
178 | |||
179 | if (!Convert.IsDBNull(row["creationDate"])) | ||
180 | item.CreationDate = Convert.ToInt32(row["creationDate"]); | ||
181 | |||
182 | if (!Convert.IsDBNull(row["groupID"])) | ||
183 | item.GroupID = new UUID((string)row["groupID"]); | ||
184 | |||
185 | if (!Convert.IsDBNull(row["groupOwned"])) | ||
186 | item.GroupOwned = Convert.ToBoolean(row["groupOwned"]); | ||
187 | |||
188 | if (!Convert.IsDBNull(row["Flags"])) | ||
189 | item.Flags = Convert.ToUInt32(row["Flags"]); | ||
190 | |||
191 | return item; | ||
192 | } | ||
193 | |||
194 | /// <summary> | ||
195 | /// Fill a database row with item data | ||
196 | /// </summary> | ||
197 | /// <param name="row"></param> | ||
198 | /// <param name="item"></param> | ||
199 | private static void fillItemRow(DataRow row, InventoryItemBase item) | ||
200 | { | ||
201 | row["UUID"] = item.ID.ToString(); | ||
202 | row["assetID"] = item.AssetID.ToString(); | ||
203 | row["assetType"] = item.AssetType; | ||
204 | row["invType"] = item.InvType; | ||
205 | row["parentFolderID"] = item.Folder.ToString(); | ||
206 | row["avatarID"] = item.Owner.ToString(); | ||
207 | row["creatorsID"] = item.CreatorIdentification.ToString(); | ||
208 | row["inventoryName"] = item.Name; | ||
209 | row["inventoryDescription"] = item.Description; | ||
210 | |||
211 | row["inventoryNextPermissions"] = item.NextPermissions; | ||
212 | row["inventoryCurrentPermissions"] = item.CurrentPermissions; | ||
213 | row["inventoryBasePermissions"] = item.BasePermissions; | ||
214 | row["inventoryEveryOnePermissions"] = item.EveryOnePermissions; | ||
215 | row["inventoryGroupPermissions"] = item.GroupPermissions; | ||
216 | |||
217 | // new fields | ||
218 | row["salePrice"] = item.SalePrice; | ||
219 | row["saleType"] = item.SaleType; | ||
220 | row["creationDate"] = item.CreationDate; | ||
221 | row["groupID"] = item.GroupID.ToString(); | ||
222 | row["groupOwned"] = item.GroupOwned; | ||
223 | row["flags"] = item.Flags; | ||
224 | } | ||
225 | |||
226 | /// <summary> | ||
227 | /// Add inventory folder | ||
228 | /// </summary> | ||
229 | /// <param name="folder">Folder base</param> | ||
230 | /// <param name="add">true=create folder. false=update existing folder</param> | ||
231 | /// <remarks>nasty</remarks> | ||
232 | private void addFolder(InventoryFolderBase folder, bool add) | ||
233 | { | ||
234 | lock (ds) | ||
235 | { | ||
236 | DataTable inventoryFolderTable = ds.Tables["inventoryfolders"]; | ||
237 | |||
238 | DataRow inventoryRow = inventoryFolderTable.Rows.Find(folder.ID.ToString()); | ||
239 | if (inventoryRow == null) | ||
240 | { | ||
241 | if (! add) | ||
242 | m_log.ErrorFormat("Interface Misuse: Attempting to Update non-existent inventory folder: {0}", folder.ID); | ||
243 | |||
244 | inventoryRow = inventoryFolderTable.NewRow(); | ||
245 | fillFolderRow(inventoryRow, folder); | ||
246 | inventoryFolderTable.Rows.Add(inventoryRow); | ||
247 | } | ||
248 | else | ||
249 | { | ||
250 | if (add) | ||
251 | m_log.ErrorFormat("Interface Misuse: Attempting to Add inventory folder that already exists: {0}", folder.ID); | ||
252 | |||
253 | fillFolderRow(inventoryRow, folder); | ||
254 | } | ||
255 | |||
256 | invFoldersDa.Update(ds, "inventoryfolders"); | ||
257 | } | ||
258 | } | ||
259 | |||
260 | /// <summary> | ||
261 | /// Move an inventory folder | ||
262 | /// </summary> | ||
263 | /// <param name="folder">folder base</param> | ||
264 | private void moveFolder(InventoryFolderBase folder) | ||
265 | { | ||
266 | lock (ds) | ||
267 | { | ||
268 | DataTable inventoryFolderTable = ds.Tables["inventoryfolders"]; | ||
269 | |||
270 | DataRow inventoryRow = inventoryFolderTable.Rows.Find(folder.ID.ToString()); | ||
271 | if (inventoryRow == null) | ||
272 | { | ||
273 | inventoryRow = inventoryFolderTable.NewRow(); | ||
274 | fillFolderRow(inventoryRow, folder); | ||
275 | inventoryFolderTable.Rows.Add(inventoryRow); | ||
276 | } | ||
277 | else | ||
278 | { | ||
279 | moveFolderRow(inventoryRow, folder); | ||
280 | } | ||
281 | |||
282 | invFoldersDa.Update(ds, "inventoryfolders"); | ||
283 | } | ||
284 | } | ||
285 | |||
286 | /// <summary> | ||
287 | /// add an item in inventory | ||
288 | /// </summary> | ||
289 | /// <param name="item">the item</param> | ||
290 | /// <param name="add">true=add item ; false=update existing item</param> | ||
291 | private void addItem(InventoryItemBase item, bool add) | ||
292 | { | ||
293 | lock (ds) | ||
294 | { | ||
295 | DataTable inventoryItemTable = ds.Tables["inventoryitems"]; | ||
296 | |||
297 | DataRow inventoryRow = inventoryItemTable.Rows.Find(item.ID.ToString()); | ||
298 | if (inventoryRow == null) | ||
299 | { | ||
300 | if (!add) | ||
301 | m_log.ErrorFormat("[INVENTORY DB]: Interface Misuse: Attempting to Update non-existent inventory item: {0}", item.ID); | ||
302 | |||
303 | inventoryRow = inventoryItemTable.NewRow(); | ||
304 | fillItemRow(inventoryRow, item); | ||
305 | inventoryItemTable.Rows.Add(inventoryRow); | ||
306 | } | ||
307 | else | ||
308 | { | ||
309 | if (add) | ||
310 | m_log.ErrorFormat("[INVENTORY DB]: Interface Misuse: Attempting to Add inventory item that already exists: {0}", item.ID); | ||
311 | |||
312 | fillItemRow(inventoryRow, item); | ||
313 | } | ||
314 | |||
315 | invItemsDa.Update(ds, "inventoryitems"); | ||
316 | |||
317 | DataTable inventoryFolderTable = ds.Tables["inventoryfolders"]; | ||
318 | |||
319 | inventoryRow = inventoryFolderTable.Rows.Find(item.Folder.ToString()); | ||
320 | if (inventoryRow != null) //MySQL doesn't throw an exception here, so sqlite shouldn't either. | ||
321 | inventoryRow["version"] = (int)inventoryRow["version"] + 1; | ||
322 | |||
323 | invFoldersDa.Update(ds, "inventoryfolders"); | ||
324 | } | ||
325 | } | ||
326 | |||
327 | /// <summary> | ||
328 | /// TODO : DataSet commit | ||
329 | /// </summary> | ||
330 | public void Shutdown() | ||
331 | { | ||
332 | // TODO: DataSet commit | ||
333 | } | ||
334 | |||
335 | /// <summary> | ||
336 | /// The name of this DB provider | ||
337 | /// </summary> | ||
338 | /// <returns>Name of DB provider</returns> | ||
339 | public string Name | ||
340 | { | ||
341 | get { return "SQLite Inventory Data Interface"; } | ||
342 | } | ||
343 | |||
344 | /// <summary> | ||
345 | /// Returns the version of this DB provider | ||
346 | /// </summary> | ||
347 | /// <returns>A string containing the DB provider version</returns> | ||
348 | public string Version | ||
349 | { | ||
350 | get | ||
351 | { | ||
352 | Module module = GetType().Module; | ||
353 | // string dllName = module.Assembly.ManifestModule.Name; | ||
354 | Version dllVersion = module.Assembly.GetName().Version; | ||
355 | |||
356 | |||
357 | return | ||
358 | string.Format("{0}.{1}.{2}.{3}", dllVersion.Major, dllVersion.Minor, dllVersion.Build, | ||
359 | dllVersion.Revision); | ||
360 | } | ||
361 | } | ||
362 | |||
363 | /// <summary> | ||
364 | /// Returns a list of inventory items contained within the specified folder | ||
365 | /// </summary> | ||
366 | /// <param name="folderID">The UUID of the target folder</param> | ||
367 | /// <returns>A List of InventoryItemBase items</returns> | ||
368 | public List<InventoryItemBase> getInventoryInFolder(UUID folderID) | ||
369 | { | ||
370 | lock (ds) | ||
371 | { | ||
372 | List<InventoryItemBase> retval = new List<InventoryItemBase>(); | ||
373 | DataTable inventoryItemTable = ds.Tables["inventoryitems"]; | ||
374 | string selectExp = "parentFolderID = '" + folderID + "'"; | ||
375 | DataRow[] rows = inventoryItemTable.Select(selectExp); | ||
376 | foreach (DataRow row in rows) | ||
377 | { | ||
378 | retval.Add(buildItem(row)); | ||
379 | } | ||
380 | |||
381 | return retval; | ||
382 | } | ||
383 | } | ||
384 | |||
385 | /// <summary> | ||
386 | /// Returns a list of the root folders within a users inventory | ||
387 | /// </summary> | ||
388 | /// <param name="user">The user whos inventory is to be searched</param> | ||
389 | /// <returns>A list of folder objects</returns> | ||
390 | public List<InventoryFolderBase> getUserRootFolders(UUID user) | ||
391 | { | ||
392 | return new List<InventoryFolderBase>(); | ||
393 | } | ||
394 | |||
395 | // see InventoryItemBase.getUserRootFolder | ||
396 | public InventoryFolderBase getUserRootFolder(UUID user) | ||
397 | { | ||
398 | lock (ds) | ||
399 | { | ||
400 | List<InventoryFolderBase> folders = new List<InventoryFolderBase>(); | ||
401 | DataTable inventoryFolderTable = ds.Tables["inventoryfolders"]; | ||
402 | string selectExp = "agentID = '" + user + "' AND parentID = '" + UUID.Zero + "'"; | ||
403 | DataRow[] rows = inventoryFolderTable.Select(selectExp); | ||
404 | foreach (DataRow row in rows) | ||
405 | { | ||
406 | folders.Add(buildFolder(row)); | ||
407 | } | ||
408 | |||
409 | // There should only ever be one root folder for a user. However, if there's more | ||
410 | // than one we'll simply use the first one rather than failing. It would be even | ||
411 | // nicer to print some message to this effect, but this feels like it's too low a | ||
412 | // to put such a message out, and it's too minor right now to spare the time to | ||
413 | // suitably refactor. | ||
414 | if (folders.Count > 0) | ||
415 | { | ||
416 | return folders[0]; | ||
417 | } | ||
418 | |||
419 | return null; | ||
420 | } | ||
421 | } | ||
422 | |||
423 | /// <summary> | ||
424 | /// Append a list of all the child folders of a parent folder | ||
425 | /// </summary> | ||
426 | /// <param name="folders">list where folders will be appended</param> | ||
427 | /// <param name="parentID">ID of parent</param> | ||
428 | protected void getInventoryFolders(ref List<InventoryFolderBase> folders, UUID parentID) | ||
429 | { | ||
430 | lock (ds) | ||
431 | { | ||
432 | DataTable inventoryFolderTable = ds.Tables["inventoryfolders"]; | ||
433 | string selectExp = "parentID = '" + parentID + "'"; | ||
434 | DataRow[] rows = inventoryFolderTable.Select(selectExp); | ||
435 | foreach (DataRow row in rows) | ||
436 | { | ||
437 | folders.Add(buildFolder(row)); | ||
438 | } | ||
439 | |||
440 | } | ||
441 | } | ||
442 | |||
443 | /// <summary> | ||
444 | /// Returns a list of inventory folders contained in the folder 'parentID' | ||
445 | /// </summary> | ||
446 | /// <param name="parentID">The folder to get subfolders for</param> | ||
447 | /// <returns>A list of inventory folders</returns> | ||
448 | public List<InventoryFolderBase> getInventoryFolders(UUID parentID) | ||
449 | { | ||
450 | List<InventoryFolderBase> folders = new List<InventoryFolderBase>(); | ||
451 | getInventoryFolders(ref folders, parentID); | ||
452 | return folders; | ||
453 | } | ||
454 | |||
455 | /// <summary> | ||
456 | /// See IInventoryDataPlugin | ||
457 | /// </summary> | ||
458 | /// <param name="parentID"></param> | ||
459 | /// <returns></returns> | ||
460 | public List<InventoryFolderBase> getFolderHierarchy(UUID parentID) | ||
461 | { | ||
462 | /* Note: There are subtle changes between this implementation of getFolderHierarchy and the previous one | ||
463 | * - We will only need to hit the database twice instead of n times. | ||
464 | * - We assume the database is well-formed - no stranded/dangling folders, all folders in heirarchy owned | ||
465 | * by the same person, each user only has 1 inventory heirarchy | ||
466 | * - The returned list is not ordered, instead of breadth-first ordered | ||
467 | There are basically 2 usage cases for getFolderHeirarchy: | ||
468 | 1) Getting the user's entire inventory heirarchy when they log in | ||
469 | 2) Finding a subfolder heirarchy to delete when emptying the trash. | ||
470 | This implementation will pull all inventory folders from the database, and then prune away any folder that | ||
471 | is not part of the requested sub-heirarchy. The theory is that it is cheaper to make 1 request from the | ||
472 | database than to make n requests. This pays off only if requested heirarchy is large. | ||
473 | By making this choice, we are making the worst case better at the cost of making the best case worse | ||
474 | - Francis | ||
475 | */ | ||
476 | |||
477 | List<InventoryFolderBase> folders = new List<InventoryFolderBase>(); | ||
478 | DataRow[] folderRows = null, parentRow; | ||
479 | InventoryFolderBase parentFolder = null; | ||
480 | lock (ds) | ||
481 | { | ||
482 | /* Fetch the parent folder from the database to determine the agent ID. | ||
483 | * Then fetch all inventory folders for that agent from the agent ID. | ||
484 | */ | ||
485 | DataTable inventoryFolderTable = ds.Tables["inventoryfolders"]; | ||
486 | string selectExp = "UUID = '" + parentID + "'"; | ||
487 | parentRow = inventoryFolderTable.Select(selectExp); // Assume at most 1 result | ||
488 | if (parentRow.GetLength(0) >= 1) // No result means parent folder does not exist | ||
489 | { | ||
490 | parentFolder = buildFolder(parentRow[0]); | ||
491 | UUID agentID = parentFolder.Owner; | ||
492 | selectExp = "agentID = '" + agentID + "'"; | ||
493 | folderRows = inventoryFolderTable.Select(selectExp); | ||
494 | } | ||
495 | |||
496 | if (folderRows != null && folderRows.GetLength(0) >= 1) // No result means parent folder does not exist | ||
497 | { // or has no children | ||
498 | /* if we're querying the root folder, just return an unordered list of all folders in the user's | ||
499 | * inventory | ||
500 | */ | ||
501 | if (parentFolder.ParentID == UUID.Zero) | ||
502 | { | ||
503 | foreach (DataRow row in folderRows) | ||
504 | { | ||
505 | InventoryFolderBase curFolder = buildFolder(row); | ||
506 | if (curFolder.ID != parentID) // Return all folders except the parent folder of heirarchy | ||
507 | folders.Add(buildFolder(row)); | ||
508 | } | ||
509 | } // If requesting root folder | ||
510 | /* else we are querying a non-root folder. We currently have a list of all of the user's folders, | ||
511 | * we must construct a list of all folders in the heirarchy below parentID. | ||
512 | * Our first step will be to construct a hash table of all folders, indexed by parent ID. | ||
513 | * Once we have constructed the hash table, we will do a breadth-first traversal on the tree using the | ||
514 | * hash table to find child folders. | ||
515 | */ | ||
516 | else | ||
517 | { // Querying a non-root folder | ||
518 | |||
519 | // Build a hash table of all user's inventory folders, indexed by each folder's parent ID | ||
520 | Dictionary<UUID, List<InventoryFolderBase>> hashtable = | ||
521 | new Dictionary<UUID, List<InventoryFolderBase>>(folderRows.GetLength(0)); | ||
522 | |||
523 | foreach (DataRow row in folderRows) | ||
524 | { | ||
525 | InventoryFolderBase curFolder = buildFolder(row); | ||
526 | if (curFolder.ParentID != UUID.Zero) // Discard root of tree - not needed | ||
527 | { | ||
528 | if (hashtable.ContainsKey(curFolder.ParentID)) | ||
529 | { | ||
530 | // Current folder already has a sibling - append to sibling list | ||
531 | hashtable[curFolder.ParentID].Add(curFolder); | ||
532 | } | ||
533 | else | ||
534 | { | ||
535 | List<InventoryFolderBase> siblingList = new List<InventoryFolderBase>(); | ||
536 | siblingList.Add(curFolder); | ||
537 | // Current folder has no known (yet) siblings | ||
538 | hashtable.Add(curFolder.ParentID, siblingList); | ||
539 | } | ||
540 | } | ||
541 | } // For all inventory folders | ||
542 | |||
543 | // Note: Could release the ds lock here - we don't access folderRows or the database anymore. | ||
544 | // This is somewhat of a moot point as the callers of this function usually lock db anyways. | ||
545 | |||
546 | if (hashtable.ContainsKey(parentID)) // if requested folder does have children | ||
547 | folders.AddRange(hashtable[parentID]); | ||
548 | |||
549 | // BreadthFirstSearch build inventory tree **Note: folders.Count is *not* static | ||
550 | for (int i = 0; i < folders.Count; i++) | ||
551 | if (hashtable.ContainsKey(folders[i].ID)) | ||
552 | folders.AddRange(hashtable[folders[i].ID]); | ||
553 | |||
554 | } // if requesting a subfolder heirarchy | ||
555 | } // if folder parentID exists and has children | ||
556 | } // lock ds | ||
557 | return folders; | ||
558 | } | ||
559 | |||
560 | /// <summary> | ||
561 | /// Returns an inventory item by its UUID | ||
562 | /// </summary> | ||
563 | /// <param name="item">The UUID of the item to be returned</param> | ||
564 | /// <returns>A class containing item information</returns> | ||
565 | public InventoryItemBase getInventoryItem(UUID item) | ||
566 | { | ||
567 | lock (ds) | ||
568 | { | ||
569 | DataRow row = ds.Tables["inventoryitems"].Rows.Find(item.ToString()); | ||
570 | if (row != null) | ||
571 | { | ||
572 | return buildItem(row); | ||
573 | } | ||
574 | else | ||
575 | { | ||
576 | return null; | ||
577 | } | ||
578 | } | ||
579 | } | ||
580 | |||
581 | /// <summary> | ||
582 | /// Returns a specified inventory folder by its UUID | ||
583 | /// </summary> | ||
584 | /// <param name="folder">The UUID of the folder to be returned</param> | ||
585 | /// <returns>A class containing folder information</returns> | ||
586 | public InventoryFolderBase getInventoryFolder(UUID folder) | ||
587 | { | ||
588 | // TODO: Deep voodoo here. If you enable this code then | ||
589 | // multi region breaks. No idea why, but I figured it was | ||
590 | // better to leave multi region at this point. It does mean | ||
591 | // that you don't get to see system textures why creating | ||
592 | // clothes and the like. :( | ||
593 | lock (ds) | ||
594 | { | ||
595 | DataRow row = ds.Tables["inventoryfolders"].Rows.Find(folder.ToString()); | ||
596 | if (row != null) | ||
597 | { | ||
598 | return buildFolder(row); | ||
599 | } | ||
600 | else | ||
601 | { | ||
602 | return null; | ||
603 | } | ||
604 | } | ||
605 | } | ||
606 | |||
607 | /// <summary> | ||
608 | /// Creates a new inventory item based on item | ||
609 | /// </summary> | ||
610 | /// <param name="item">The item to be created</param> | ||
611 | public void addInventoryItem(InventoryItemBase item) | ||
612 | { | ||
613 | addItem(item, true); | ||
614 | } | ||
615 | |||
616 | /// <summary> | ||
617 | /// Updates an inventory item with item (updates based on ID) | ||
618 | /// </summary> | ||
619 | /// <param name="item">The updated item</param> | ||
620 | public void updateInventoryItem(InventoryItemBase item) | ||
621 | { | ||
622 | addItem(item, false); | ||
623 | } | ||
624 | |||
625 | /// <summary> | ||
626 | /// Delete an inventory item | ||
627 | /// </summary> | ||
628 | /// <param name="item">The item UUID</param> | ||
629 | public void deleteInventoryItem(UUID itemID) | ||
630 | { | ||
631 | lock (ds) | ||
632 | { | ||
633 | DataTable inventoryItemTable = ds.Tables["inventoryitems"]; | ||
634 | |||
635 | DataRow inventoryRow = inventoryItemTable.Rows.Find(itemID.ToString()); | ||
636 | if (inventoryRow != null) | ||
637 | { | ||
638 | inventoryRow.Delete(); | ||
639 | } | ||
640 | |||
641 | invItemsDa.Update(ds, "inventoryitems"); | ||
642 | } | ||
643 | } | ||
644 | |||
645 | public InventoryItemBase queryInventoryItem(UUID itemID) | ||
646 | { | ||
647 | return getInventoryItem(itemID); | ||
648 | } | ||
649 | |||
650 | public InventoryFolderBase queryInventoryFolder(UUID folderID) | ||
651 | { | ||
652 | return getInventoryFolder(folderID); | ||
653 | } | ||
654 | |||
655 | /// <summary> | ||
656 | /// Delete all items in the specified folder | ||
657 | /// </summary> | ||
658 | /// <param name="folderId">id of the folder, whose item content should be deleted</param> | ||
659 | /// <todo>this is horribly inefficient, but I don't want to ruin the overall structure of this implementation</todo> | ||
660 | private void deleteItemsInFolder(UUID folderId) | ||
661 | { | ||
662 | List<InventoryItemBase> items = getInventoryInFolder(folderId); | ||
663 | |||
664 | foreach (InventoryItemBase i in items) | ||
665 | deleteInventoryItem(i.ID); | ||
666 | } | ||
667 | |||
668 | /// <summary> | ||
669 | /// Adds a new folder specified by folder | ||
670 | /// </summary> | ||
671 | /// <param name="folder">The inventory folder</param> | ||
672 | public void addInventoryFolder(InventoryFolderBase folder) | ||
673 | { | ||
674 | addFolder(folder, true); | ||
675 | } | ||
676 | |||
677 | /// <summary> | ||
678 | /// Updates a folder based on its ID with folder | ||
679 | /// </summary> | ||
680 | /// <param name="folder">The inventory folder</param> | ||
681 | public void updateInventoryFolder(InventoryFolderBase folder) | ||
682 | { | ||
683 | addFolder(folder, false); | ||
684 | } | ||
685 | |||
686 | /// <summary> | ||
687 | /// Moves a folder based on its ID with folder | ||
688 | /// </summary> | ||
689 | /// <param name="folder">The inventory folder</param> | ||
690 | public void moveInventoryFolder(InventoryFolderBase folder) | ||
691 | { | ||
692 | moveFolder(folder); | ||
693 | } | ||
694 | |||
695 | /// <summary> | ||
696 | /// Delete a folder | ||
697 | /// </summary> | ||
698 | /// <remarks> | ||
699 | /// This will clean-up any child folders and child items as well | ||
700 | /// </remarks> | ||
701 | /// <param name="folderID">the folder UUID</param> | ||
702 | public void deleteInventoryFolder(UUID folderID) | ||
703 | { | ||
704 | lock (ds) | ||
705 | { | ||
706 | List<InventoryFolderBase> subFolders = getFolderHierarchy(folderID); | ||
707 | |||
708 | DataTable inventoryFolderTable = ds.Tables["inventoryfolders"]; | ||
709 | DataRow inventoryRow; | ||
710 | |||
711 | //Delete all sub-folders | ||
712 | foreach (InventoryFolderBase f in subFolders) | ||
713 | { | ||
714 | inventoryRow = inventoryFolderTable.Rows.Find(f.ID.ToString()); | ||
715 | if (inventoryRow != null) | ||
716 | { | ||
717 | deleteItemsInFolder(f.ID); | ||
718 | inventoryRow.Delete(); | ||
719 | } | ||
720 | } | ||
721 | |||
722 | //Delete the actual row | ||
723 | inventoryRow = inventoryFolderTable.Rows.Find(folderID.ToString()); | ||
724 | if (inventoryRow != null) | ||
725 | { | ||
726 | deleteItemsInFolder(folderID); | ||
727 | inventoryRow.Delete(); | ||
728 | } | ||
729 | |||
730 | invFoldersDa.Update(ds, "inventoryfolders"); | ||
731 | } | ||
732 | } | ||
733 | |||
734 | /*********************************************************************** | ||
735 | * | ||
736 | * Data Table definitions | ||
737 | * | ||
738 | **********************************************************************/ | ||
739 | |||
740 | protected void CreateDataSetMapping(IDataAdapter da, string tableName) | ||
741 | { | ||
742 | ITableMapping dbMapping = da.TableMappings.Add(tableName, tableName); | ||
743 | foreach (DataColumn col in ds.Tables[tableName].Columns) | ||
744 | { | ||
745 | dbMapping.ColumnMappings.Add(col.ColumnName, col.ColumnName); | ||
746 | } | ||
747 | } | ||
748 | |||
749 | /// <summary> | ||
750 | /// Create the "inventoryitems" table | ||
751 | /// </summary> | ||
752 | private static DataTable createInventoryItemsTable() | ||
753 | { | ||
754 | DataTable inv = new DataTable("inventoryitems"); | ||
755 | |||
756 | createCol(inv, "UUID", typeof (String)); //inventoryID | ||
757 | createCol(inv, "assetID", typeof (String)); | ||
758 | createCol(inv, "assetType", typeof (Int32)); | ||
759 | createCol(inv, "invType", typeof (Int32)); | ||
760 | createCol(inv, "parentFolderID", typeof (String)); | ||
761 | createCol(inv, "avatarID", typeof (String)); | ||
762 | createCol(inv, "creatorsID", typeof (String)); | ||
763 | |||
764 | createCol(inv, "inventoryName", typeof (String)); | ||
765 | createCol(inv, "inventoryDescription", typeof (String)); | ||
766 | // permissions | ||
767 | createCol(inv, "inventoryNextPermissions", typeof (Int32)); | ||
768 | createCol(inv, "inventoryCurrentPermissions", typeof (Int32)); | ||
769 | createCol(inv, "inventoryBasePermissions", typeof (Int32)); | ||
770 | createCol(inv, "inventoryEveryOnePermissions", typeof (Int32)); | ||
771 | createCol(inv, "inventoryGroupPermissions", typeof (Int32)); | ||
772 | |||
773 | // sale info | ||
774 | createCol(inv, "salePrice", typeof(Int32)); | ||
775 | createCol(inv, "saleType", typeof(Byte)); | ||
776 | |||
777 | // creation date | ||
778 | createCol(inv, "creationDate", typeof(Int32)); | ||
779 | |||
780 | // group info | ||
781 | createCol(inv, "groupID", typeof(String)); | ||
782 | createCol(inv, "groupOwned", typeof(Boolean)); | ||
783 | |||
784 | // Flags | ||
785 | createCol(inv, "flags", typeof(UInt32)); | ||
786 | |||
787 | inv.PrimaryKey = new DataColumn[] { inv.Columns["UUID"] }; | ||
788 | return inv; | ||
789 | } | ||
790 | |||
791 | /// <summary> | ||
792 | /// Creates the "inventoryfolders" table | ||
793 | /// </summary> | ||
794 | /// <returns></returns> | ||
795 | private static DataTable createInventoryFoldersTable() | ||
796 | { | ||
797 | DataTable fol = new DataTable("inventoryfolders"); | ||
798 | |||
799 | createCol(fol, "UUID", typeof (String)); //folderID | ||
800 | createCol(fol, "name", typeof (String)); | ||
801 | createCol(fol, "agentID", typeof (String)); | ||
802 | createCol(fol, "parentID", typeof (String)); | ||
803 | createCol(fol, "type", typeof (Int32)); | ||
804 | createCol(fol, "version", typeof (Int32)); | ||
805 | |||
806 | fol.PrimaryKey = new DataColumn[] {fol.Columns["UUID"]}; | ||
807 | return fol; | ||
808 | } | ||
809 | |||
810 | /// <summary> | ||
811 | /// | ||
812 | /// </summary> | ||
813 | /// <param name="da"></param> | ||
814 | /// <param name="conn"></param> | ||
815 | private void setupItemsCommands(SqliteDataAdapter da, SqliteConnection conn) | ||
816 | { | ||
817 | lock (ds) | ||
818 | { | ||
819 | da.InsertCommand = createInsertCommand("inventoryitems", ds.Tables["inventoryitems"]); | ||
820 | da.InsertCommand.Connection = conn; | ||
821 | |||
822 | da.UpdateCommand = createUpdateCommand("inventoryitems", "UUID=:UUID", ds.Tables["inventoryitems"]); | ||
823 | da.UpdateCommand.Connection = conn; | ||
824 | |||
825 | SqliteCommand delete = new SqliteCommand("delete from inventoryitems where UUID = :UUID"); | ||
826 | delete.Parameters.Add(createSqliteParameter("UUID", typeof(String))); | ||
827 | delete.Connection = conn; | ||
828 | da.DeleteCommand = delete; | ||
829 | } | ||
830 | } | ||
831 | |||
832 | /// <summary> | ||
833 | /// | ||
834 | /// </summary> | ||
835 | /// <param name="da"></param> | ||
836 | /// <param name="conn"></param> | ||
837 | private void setupFoldersCommands(SqliteDataAdapter da, SqliteConnection conn) | ||
838 | { | ||
839 | lock (ds) | ||
840 | { | ||
841 | da.InsertCommand = createInsertCommand("inventoryfolders", ds.Tables["inventoryfolders"]); | ||
842 | da.InsertCommand.Connection = conn; | ||
843 | |||
844 | da.UpdateCommand = createUpdateCommand("inventoryfolders", "UUID=:UUID", ds.Tables["inventoryfolders"]); | ||
845 | da.UpdateCommand.Connection = conn; | ||
846 | |||
847 | SqliteCommand delete = new SqliteCommand("delete from inventoryfolders where UUID = :UUID"); | ||
848 | delete.Parameters.Add(createSqliteParameter("UUID", typeof(String))); | ||
849 | delete.Connection = conn; | ||
850 | da.DeleteCommand = delete; | ||
851 | } | ||
852 | } | ||
853 | |||
854 | /// <summary> | ||
855 | /// | ||
856 | /// </summary> | ||
857 | /// <param name="row"></param> | ||
858 | /// <returns></returns> | ||
859 | private static InventoryFolderBase buildFolder(DataRow row) | ||
860 | { | ||
861 | InventoryFolderBase folder = new InventoryFolderBase(); | ||
862 | folder.ID = new UUID((string) row["UUID"]); | ||
863 | folder.Name = (string) row["name"]; | ||
864 | folder.Owner = new UUID((string) row["agentID"]); | ||
865 | folder.ParentID = new UUID((string) row["parentID"]); | ||
866 | folder.Type = Convert.ToInt16(row["type"]); | ||
867 | folder.Version = Convert.ToUInt16(row["version"]); | ||
868 | return folder; | ||
869 | } | ||
870 | |||
871 | /// <summary> | ||
872 | /// | ||
873 | /// </summary> | ||
874 | /// <param name="row"></param> | ||
875 | /// <param name="folder"></param> | ||
876 | private static void fillFolderRow(DataRow row, InventoryFolderBase folder) | ||
877 | { | ||
878 | row["UUID"] = folder.ID.ToString(); | ||
879 | row["name"] = folder.Name; | ||
880 | row["agentID"] = folder.Owner.ToString(); | ||
881 | row["parentID"] = folder.ParentID.ToString(); | ||
882 | row["type"] = folder.Type; | ||
883 | row["version"] = folder.Version; | ||
884 | } | ||
885 | |||
886 | /// <summary> | ||
887 | /// | ||
888 | /// </summary> | ||
889 | /// <param name="row"></param> | ||
890 | /// <param name="folder"></param> | ||
891 | private static void moveFolderRow(DataRow row, InventoryFolderBase folder) | ||
892 | { | ||
893 | row["UUID"] = folder.ID.ToString(); | ||
894 | row["parentID"] = folder.ParentID.ToString(); | ||
895 | } | ||
896 | |||
897 | public List<InventoryItemBase> fetchActiveGestures (UUID avatarID) | ||
898 | { | ||
899 | lock (ds) | ||
900 | { | ||
901 | List<InventoryItemBase> items = new List<InventoryItemBase>(); | ||
902 | |||
903 | DataTable inventoryItemTable = ds.Tables["inventoryitems"]; | ||
904 | string selectExp | ||
905 | = "avatarID = '" + avatarID + "' AND assetType = " + (int)AssetType.Gesture + " AND flags = 1"; | ||
906 | //m_log.DebugFormat("[SQL]: sql = " + selectExp); | ||
907 | DataRow[] rows = inventoryItemTable.Select(selectExp); | ||
908 | foreach (DataRow row in rows) | ||
909 | { | ||
910 | items.Add(buildItem(row)); | ||
911 | } | ||
912 | return items; | ||
913 | } | ||
914 | } | ||
915 | } | ||
916 | } | ||
diff --git a/OpenSim/Data/Tests/InventoryTests.cs b/OpenSim/Data/Tests/InventoryTests.cs index 3edf89d..e4ec5c1 100644 --- a/OpenSim/Data/Tests/InventoryTests.cs +++ b/OpenSim/Data/Tests/InventoryTests.cs | |||
@@ -44,11 +44,6 @@ using OpenSim.Data.SQLite; | |||
44 | 44 | ||
45 | namespace OpenSim.Data.Tests | 45 | namespace OpenSim.Data.Tests |
46 | { | 46 | { |
47 | [TestFixture(Description = "Inventory store tests (SQLite)")] | ||
48 | public class SQLiteInventoryTests : InventoryTests<SqliteConnection, SQLiteInventoryStore> | ||
49 | { | ||
50 | } | ||
51 | |||
52 | [TestFixture(Description = "Inventory store tests (MySQL)")] | 47 | [TestFixture(Description = "Inventory store tests (MySQL)")] |
53 | public class MySqlInventoryTests : InventoryTests<MySqlConnection, MySQLInventoryData> | 48 | public class MySqlInventoryTests : InventoryTests<MySqlConnection, MySQLInventoryData> |
54 | { | 49 | { |