/* * Copyright (c) Contributors, http://opensimulator.org/ * See CONTRIBUTORS.TXT for a full list of copyright holders. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the OpenSim Project nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ using System; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; using System.Reflection; using libsecondlife; using log4net; using OpenSim.Framework; namespace OpenSim.Data.MSSQL { /// /// A MSSQL interface for the inventory server /// public class MSSQLInventoryData : IInventoryData { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); #region Helper converters to preserve unsigned bitfield-type data in DB roundtrips via signed int32s private static int ConvertUint32BitFieldToInt32(uint bitField) { return BitConverter.ToInt32(BitConverter.GetBytes(bitField), 0); } private static uint ConvertInt32BitFieldToUint32(int bitField) { return BitConverter.ToUInt32(BitConverter.GetBytes(bitField), 0); } #endregion /// /// The database manager /// private MSSQLManager database; /// /// Loads and initialises the MSSQL inventory storage interface /// /// connect string /// use mssql_connection.ini public void Initialise(string connect) { // TODO: actually use the provided connect string IniFile GridDataMySqlFile = new IniFile("mssql_connection.ini"); string settingDataSource = GridDataMySqlFile.ParseFileReadValue("data_source"); string settingInitialCatalog = GridDataMySqlFile.ParseFileReadValue("initial_catalog"); string settingPersistSecurityInfo = GridDataMySqlFile.ParseFileReadValue("persist_security_info"); string settingUserId = GridDataMySqlFile.ParseFileReadValue("user_id"); string settingPassword = GridDataMySqlFile.ParseFileReadValue("password"); database = new MSSQLManager(settingDataSource, settingInitialCatalog, settingPersistSecurityInfo, settingUserId, settingPassword); TestTables(); } #region Test and initialization code /// /// Execute "CreateFoldersTable.sql" if tableName == null /// /// the table name private void UpgradeFoldersTable(string tableName) { // null as the version, indicates that the table didn't exist if (tableName == null) { database.ExecuteResourceSql("CreateFoldersTable.sql"); //database.ExecuteResourceSql("UpgradeFoldersTableToVersion2.sql"); return; } } /// /// Execute "CreateItemsTable.sql" if tableName = null /// /// the table name private void UpgradeItemsTable(string tableName) { // null as the version, indicates that the table didn't exist if (tableName == null) { database.ExecuteResourceSql("CreateItemsTable.sql"); //database.ExecuteResourceSql("UpgradeItemsTableToVersion2.sql"); return; } } /// /// /// private void TestTables() { Dictionary tableList = new Dictionary(); tableList["inventoryfolders"] = null; tableList["inventoryitems"] = null; database.GetTableVersion(tableList); UpgradeFoldersTable(tableList["inventoryfolders"]); UpgradeItemsTable(tableList["inventoryitems"]); } #endregion /// /// The name of this DB provider /// /// A string containing the name of the DB provider public string getName() { return "MSSQL Inventory Data Interface"; } /// /// Closes this DB provider /// public void Close() { // Do nothing. } /// /// Returns the version of this DB provider /// /// A string containing the DB provider public string getVersion() { return database.getVersion(); } /// /// Returns a list of items in a specified folder /// /// The folder to search /// A list containing inventory items public List getInventoryInFolder(LLUUID folderID) { try { lock (database) { List items = new List(); Dictionary param = new Dictionary(); param["parentFolderID"] = folderID.ToString(); IDbCommand result = database.Query("SELECT * FROM inventoryitems WHERE parentFolderID = @parentFolderID", param); IDataReader reader = result.ExecuteReader(); while (reader.Read()) items.Add(readInventoryItem(reader)); reader.Close(); result.Dispose(); return items; } } catch (Exception e) { database.Reconnect(); m_log.Error(e.ToString()); return null; } } /// /// Returns a list of the root folders within a users inventory /// /// The user whos inventory is to be searched /// A list of folder objects public List getUserRootFolders(LLUUID user) { try { lock (database) { Dictionary param = new Dictionary(); param["uuid"] = user.ToString(); param["zero"] = LLUUID.Zero.ToString(); IDbCommand result = database.Query( "SELECT * FROM inventoryfolders WHERE parentFolderID = @zero AND agentID = @uuid", param); IDataReader reader = result.ExecuteReader(); List items = new List(); while (reader.Read()) items.Add(readInventoryFolder(reader)); reader.Close(); result.Dispose(); return items; } } catch (Exception e) { database.Reconnect(); m_log.Error(e.ToString()); return null; } } /// /// see InventoryItemBase.getUserRootFolder /// /// the User UUID /// public InventoryFolderBase getUserRootFolder(LLUUID user) { try { lock (database) { Dictionary param = new Dictionary(); param["uuid"] = user.ToString(); param["zero"] = LLUUID.Zero.ToString(); IDbCommand result = database.Query( "SELECT * FROM inventoryfolders WHERE parentFolderID = @zero AND agentID = @uuid", param); IDataReader reader = result.ExecuteReader(); List items = new List(); while (reader.Read()) items.Add(readInventoryFolder(reader)); InventoryFolderBase rootFolder = null; // There should only ever be one root folder for a user. However, if there's more // than one we'll simply use the first one rather than failing. It would be even // nicer to print some message to this effect, but this feels like it's too low a // to put such a message out, and it's too minor right now to spare the time to // suitably refactor. if (items.Count > 0) { rootFolder = items[0]; } reader.Close(); result.Dispose(); return rootFolder; } } catch (Exception e) { database.Reconnect(); m_log.Error(e.ToString()); return null; } } /// /// Returns a list of folders in a users inventory contained within the specified folder /// /// The folder to search /// A list of inventory folders public List getInventoryFolders(LLUUID parentID) { try { lock (database) { Dictionary param = new Dictionary(); param["parentFolderID"] = parentID.ToString(); IDbCommand result = database.Query("SELECT * FROM inventoryfolders WHERE parentFolderID = @parentFolderID", param); IDataReader reader = result.ExecuteReader(); List items = new List(); while (reader.Read()) items.Add(readInventoryFolder(reader)); reader.Close(); result.Dispose(); return items; } } catch (Exception e) { database.Reconnect(); m_log.Error(e.ToString()); return null; } } /// /// Reads a one item from an SQL result /// /// The SQL Result /// the item read private static InventoryItemBase readInventoryItem(IDataReader reader) { try { InventoryItemBase item = new InventoryItemBase(); item.ID = new LLUUID((string) reader["inventoryID"]); item.AssetID = new LLUUID((string) reader["assetID"]); item.AssetType = (int) reader["assetType"]; item.Folder = new LLUUID((string) reader["parentFolderID"]); item.Owner = new LLUUID((string) reader["avatarID"]); item.Name = (string) reader["inventoryName"]; item.Description = (string) reader["inventoryDescription"]; item.NextPermissions = ConvertInt32BitFieldToUint32((int)reader["inventoryNextPermissions"]); item.CurrentPermissions = ConvertInt32BitFieldToUint32((int)reader["inventoryCurrentPermissions"]); item.InvType = (int) reader["invType"]; item.Creator = new LLUUID((string) reader["creatorID"]); item.BasePermissions = ConvertInt32BitFieldToUint32((int)reader["inventoryBasePermissions"]); item.EveryOnePermissions = ConvertInt32BitFieldToUint32((int)reader["inventoryEveryOnePermissions"]); item.SalePrice = (int) reader["salePrice"]; item.SaleType = Convert.ToByte(reader["saleType"]); item.CreationDate = (int) reader["creationDate"]; item.GroupID = new LLUUID(reader["groupID"].ToString()); item.GroupOwned = Convert.ToBoolean(reader["groupOwned"]); item.Flags = ConvertInt32BitFieldToUint32((int)reader["flags"]); return item; } catch (SqlException e) { m_log.Error(e.ToString()); } return null; } /// /// Returns a specified inventory item /// /// The item to return /// An inventory item public InventoryItemBase getInventoryItem(LLUUID itemID) { try { lock (database) { Dictionary param = new Dictionary(); param["inventoryID"] = itemID.ToString(); IDbCommand result = database.Query("SELECT * FROM inventoryitems WHERE inventoryID = @inventoryID", param); IDataReader reader = result.ExecuteReader(); InventoryItemBase item = null; if (reader.Read()) item = readInventoryItem(reader); reader.Close(); result.Dispose(); return item; } } catch (Exception e) { database.Reconnect(); m_log.Error(e.ToString()); } return null; } /// /// Reads a list of inventory folders returned by a query. /// /// A MSSQL Data Reader /// A List containing inventory folders protected static InventoryFolderBase readInventoryFolder(IDataReader reader) { try { InventoryFolderBase folder = new InventoryFolderBase(); folder.Owner = new LLUUID((string) reader["agentID"]); folder.ParentID = new LLUUID((string) reader["parentFolderID"]); folder.ID = new LLUUID((string) reader["folderID"]); folder.Name = (string) reader["folderName"]; folder.Type = (short) reader["type"]; folder.Version = Convert.ToUInt16(reader["version"]); return folder; } catch (Exception e) { m_log.Error(e.ToString()); } return null; } /// /// Returns a specified inventory folder /// /// The folder to return /// A folder class public InventoryFolderBase getInventoryFolder(LLUUID folderID) { try { lock (database) { Dictionary param = new Dictionary(); param["uuid"] = folderID.ToString(); IDbCommand result = database.Query("SELECT * FROM inventoryfolders WHERE folderID = @uuid", param); IDataReader reader = result.ExecuteReader(); reader.Read(); InventoryFolderBase folder = readInventoryFolder(reader); reader.Close(); result.Dispose(); return folder; } } catch (Exception e) { database.Reconnect(); m_log.Error(e.ToString()); return null; } } /// /// Adds a specified item to the database /// /// The inventory item public void addInventoryItem(InventoryItemBase item) { if (getInventoryItem(item.ID) != null) { updateInventoryItem(item); return; } string sql = "INSERT INTO inventoryitems"; sql += "([inventoryID], [assetID], [assetType], [parentFolderID], [avatarID], [inventoryName]" + ", [inventoryDescription], [inventoryNextPermissions], [inventoryCurrentPermissions]" + ", [invType], [creatorID], [inventoryBasePermissions], [inventoryEveryOnePermissions]" + ", [salePrice], [saleType], [creationDate], [groupID], [groupOwned], [flags]) VALUES "; sql += "(@inventoryID, @assetID, @assetType, @parentFolderID, @avatarID, @inventoryName, @inventoryDescription" + ", @inventoryNextPermissions, @inventoryCurrentPermissions, @invType, @creatorID" + ", @inventoryBasePermissions, @inventoryEveryOnePermissions, @salePrice, @saleType" + ", @creationDate, @groupID, @groupOwned, @flags);"; try { SqlCommand command = new SqlCommand(sql, database.getConnection()); command.Parameters.AddWithValue("inventoryID", item.ID.ToString()); command.Parameters.AddWithValue("assetID", item.AssetID.ToString()); command.Parameters.AddWithValue("assetType", item.AssetType.ToString()); command.Parameters.AddWithValue("parentFolderID", item.Folder.ToString()); command.Parameters.AddWithValue("avatarID", item.Owner.ToString()); command.Parameters.AddWithValue("inventoryName", item.Name); command.Parameters.AddWithValue("inventoryDescription", item.Description); command.Parameters.AddWithValue("inventoryNextPermissions", ConvertUint32BitFieldToInt32(item.NextPermissions)); command.Parameters.AddWithValue("inventoryCurrentPermissions", ConvertUint32BitFieldToInt32(item.CurrentPermissions)); command.Parameters.AddWithValue("invType", item.InvType); command.Parameters.AddWithValue("creatorID", item.Creator.ToString()); command.Parameters.AddWithValue("inventoryBasePermissions", ConvertUint32BitFieldToInt32(item.BasePermissions)); command.Parameters.AddWithValue("inventoryEveryOnePermissions", ConvertUint32BitFieldToInt32(item.EveryOnePermissions)); command.Parameters.AddWithValue("salePrice", item.SalePrice); command.Parameters.AddWithValue("saleType", item.SaleType); command.Parameters.AddWithValue("creationDate", item.CreationDate); command.Parameters.AddWithValue("groupID", item.GroupID.ToString()); command.Parameters.AddWithValue("groupOwned", item.GroupOwned); command.Parameters.AddWithValue("flags", ConvertUint32BitFieldToInt32(item.Flags)); command.ExecuteNonQuery(); command.Dispose(); } catch (SqlException e) { m_log.Error(e.ToString()); } } /// /// Updates the specified inventory item /// /// Inventory item to update public void updateInventoryItem(InventoryItemBase item) { SqlCommand command = new SqlCommand("UPDATE inventoryitems set inventoryID = @inventoryID, " + "assetID = @assetID, " + "assetType = @assetType," + "parentFolderID = @parentFolderID," + "avatarID = @avatarID," + "inventoryName = @inventoryName," + "inventoryDescription = @inventoryDescription," + "inventoryNextPermissions = @inventoryNextPermissions," + "inventoryCurrentPermissions = @inventoryCurrentPermissions," + "invType = @invType," + "creatorID = @creatorID," + "inventoryBasePermissions = @inventoryBasePermissions," + "inventoryEveryOnePermissions = @inventoryEveryOnePermissions," + "salePrice = @salePrice," + "saleType = @saleType," + "creationDate = @creationDate," + "groupID = @groupID," + "groupOwned = @groupOwned," + "flags = @flags where " + "inventoryID = @keyInventoryID;", database.getConnection()); command.Parameters.AddWithValue("inventoryID", item.ID.ToString()); command.Parameters.AddWithValue("assetID", item.AssetID.ToString()); command.Parameters.AddWithValue("assetType", item.AssetType.ToString()); command.Parameters.AddWithValue("parentFolderID", item.Folder.ToString()); command.Parameters.AddWithValue("avatarID", item.Owner.ToString()); command.Parameters.AddWithValue("inventoryName", item.Name); command.Parameters.AddWithValue("inventoryDescription", item.Description); command.Parameters.AddWithValue("inventoryNextPermissions", ConvertUint32BitFieldToInt32(item.NextPermissions)); command.Parameters.AddWithValue("inventoryCurrentPermissions", ConvertUint32BitFieldToInt32(item.CurrentPermissions)); command.Parameters.AddWithValue("invType", item.InvType); command.Parameters.AddWithValue("creatorID", item.Creator.ToString()); command.Parameters.AddWithValue("inventoryBasePermissions", ConvertUint32BitFieldToInt32(item.BasePermissions)); command.Parameters.AddWithValue("inventoryEveryOnePermissions", ConvertUint32BitFieldToInt32(item.EveryOnePermissions)); command.Parameters.AddWithValue("salePrice", item.SalePrice); command.Parameters.AddWithValue("saleType", item.SaleType); command.Parameters.AddWithValue("creationDate", item.CreationDate); command.Parameters.AddWithValue("groupID", item.GroupID.ToString()); command.Parameters.AddWithValue("groupOwned", item.GroupOwned); command.Parameters.AddWithValue("flags", ConvertUint32BitFieldToInt32(item.Flags)); command.Parameters.AddWithValue("@keyInventoryID", item.ID.ToString()); try { command.ExecuteNonQuery(); } catch (Exception e) { m_log.Error(e.ToString()); } } /// /// Delete an item in inventory database /// /// the item UUID public void deleteInventoryItem(LLUUID itemID) { try { Dictionary param = new Dictionary(); param["uuid"] = itemID.ToString(); IDbCommand cmd = database.Query("DELETE FROM inventoryitems WHERE inventoryID=@uuid", param); cmd.ExecuteNonQuery(); cmd.Dispose(); } catch (SqlException e) { database.Reconnect(); m_log.Error(e.ToString()); } } /// /// Creates a new inventory folder /// /// Folder to create public void addInventoryFolder(InventoryFolderBase folder) { string sql = "INSERT INTO inventoryfolders ([folderID], [agentID], [parentFolderID], [folderName], [type], [version]) VALUES "; sql += "(@folderID, @agentID, @parentFolderID, @folderName, @type, @version);"; SqlCommand command = new SqlCommand(sql, database.getConnection()); command.Parameters.AddWithValue("folderID", folder.ID.ToString()); command.Parameters.AddWithValue("agentID", folder.Owner.ToString()); command.Parameters.AddWithValue("parentFolderID", folder.ParentID.ToString()); command.Parameters.AddWithValue("folderName", folder.Name); command.Parameters.AddWithValue("type", folder.Type); command.Parameters.AddWithValue("version", Convert.ToInt32(folder.Version)); try { //IDbCommand result = database.Query(sql, param); command.ExecuteNonQuery(); command.Dispose(); } catch (Exception e) { m_log.Error(e.ToString()); } } /// /// Updates an inventory folder /// /// Folder to update public void updateInventoryFolder(InventoryFolderBase folder) { SqlCommand command = new SqlCommand("UPDATE inventoryfolders set folderID = @folderID, " + "agentID = @agentID, " + "parentFolderID = @parentFolderID," + "folderName = @folderName," + "type = @type," + "version = @version where " + "folderID = @keyFolderID;", database.getConnection()); SqlParameter param1 = new SqlParameter("@folderID", folder.ID.ToString()); SqlParameter param2 = new SqlParameter("@agentID", folder.Owner.ToString()); SqlParameter param3 = new SqlParameter("@parentFolderID", folder.ParentID.ToString()); SqlParameter param4 = new SqlParameter("@folderName", folder.Name); SqlParameter param5 = new SqlParameter("@type", folder.Type); SqlParameter param6 = new SqlParameter("@version", Convert.ToInt32(folder.Version)); SqlParameter param7 = new SqlParameter("@keyFolderID", folder.ID.ToString()); command.Parameters.Add(param1); command.Parameters.Add(param2); command.Parameters.Add(param3); command.Parameters.Add(param4); command.Parameters.Add(param5); command.Parameters.Add(param6); command.Parameters.Add(param7); try { command.ExecuteNonQuery(); } catch (Exception e) { m_log.Error(e.ToString()); } } /// /// Updates an inventory folder /// /// Folder to update public void moveInventoryFolder(InventoryFolderBase folder) { SqlCommand command = new SqlCommand("UPDATE inventoryfolders set folderID = @folderID, " + "parentFolderID = @parentFolderID," + "folderID = @keyFolderID;", database.getConnection()); SqlParameter param1 = new SqlParameter("@folderID", folder.ID.ToString()); SqlParameter param2 = new SqlParameter("@parentFolderID", folder.ParentID.ToString()); SqlParameter param3 = new SqlParameter("@keyFolderID", folder.ID.ToString()); command.Parameters.Add(param1); command.Parameters.Add(param2); command.Parameters.Add(param3); try { command.ExecuteNonQuery(); } catch (Exception e) { m_log.Error(e.ToString()); } } /// /// Append a list of all the child folders of a parent folder /// /// list where folders will be appended /// ID of parent protected void getInventoryFolders(ref List folders, LLUUID parentID) { List subfolderList = getInventoryFolders(parentID); foreach (InventoryFolderBase f in subfolderList) folders.Add(f); } // See IInventoryData public List getFolderHierarchy(LLUUID parentID) { List folders = new List(); getInventoryFolders(ref folders, parentID); for (int i = 0; i < folders.Count; i++) getInventoryFolders(ref folders, folders[i].ID); return folders; } /// /// Delete a folder in inventory databasae /// /// the folder UUID protected void deleteOneFolder(LLUUID folderID) { try { Dictionary param = new Dictionary(); param["folderID"] = folderID.ToString(); IDbCommand cmd = database.Query("DELETE FROM inventoryfolders WHERE folderID=@folderID", param); cmd.ExecuteNonQuery(); cmd.Dispose(); } catch (SqlException e) { database.Reconnect(); m_log.Error(e.ToString()); } } /// /// Delete an item in inventory database /// /// the item ID protected void deleteItemsInFolder(LLUUID folderID) { try { Dictionary param = new Dictionary(); param["parentFolderID"] = folderID.ToString(); IDbCommand cmd = database.Query("DELETE FROM inventoryitems WHERE parentFolderID=@parentFolderID", param); cmd.ExecuteNonQuery(); cmd.Dispose(); } catch (SqlException e) { database.Reconnect(); m_log.Error(e.ToString()); } } /// /// Delete an inventory folder /// /// Id of folder to delete public void deleteInventoryFolder(LLUUID folderID) { lock (database) { List subFolders = getFolderHierarchy(folderID); //Delete all sub-folders foreach (InventoryFolderBase f in subFolders) { deleteOneFolder(f.ID); deleteItemsInFolder(f.ID); } //Delete the actual row deleteOneFolder(folderID); deleteItemsInFolder(folderID); } } } }