From 1458fab82c4dab9901d81419e6b515f47ea7320f Mon Sep 17 00:00:00 2001 From: Kevin Houlihan Date: Mon, 12 Sep 2011 23:08:16 +0100 Subject: [PATCH] Reattaching a region was failing if the estate name had not changed (issue 5035). Using the RemoteAdmin API to close then recreate a region would fail if the estate name had not changed. If the estate name /was/ changed then the existing estate would be renamed rather than a new one being created. The problem really arose from a lack of distinction in the data storage layer between creating new estates and loading existing ones. --- .../RemoteController/RemoteAdminPlugin.cs | 17 +- OpenSim/Data/MSSQL/MSSQLEstateData.cs | 121 ++++++++------ OpenSim/Data/MySQL/MySQLEstateData.cs | 135 ++++++++++------ OpenSim/Data/Null/NullEstateData.cs | 5 + OpenSim/Data/SQLite/SQLiteEstateData.cs | 150 +++++++++++------- .../Interfaces/IEstateDataService.cs | 10 +- .../Framework/Interfaces/IEstateDataStore.cs | 8 + .../Simulation/EstateDataService.cs | 7 +- 8 files changed, 287 insertions(+), 166 deletions(-) diff --git a/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs b/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs index 25ae3f1933..e20b487cc6 100644 --- a/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs +++ b/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs @@ -741,21 +741,30 @@ namespace OpenSim.ApplicationPlugins.RemoteController } // Create a new estate with the name provided - region.EstateSettings = m_application.EstateDataService.LoadEstateSettings(region.RegionID, true); + region.EstateSettings = m_application.EstateDataService.CreateNewEstate(); region.EstateSettings.EstateName = (string) requestData["estate_name"]; region.EstateSettings.EstateOwner = userID; // Persistence does not seem to effect the need to save a new estate region.EstateSettings.Save(); + + if (!m_application.EstateDataService.LinkRegion(region.RegionID, (int) region.EstateSettings.EstateID)) + throw new Exception("Failed to join estate."); } else { int estateID = estateIDs[0]; - region.EstateSettings = m_application.EstateDataService.LoadEstateSettings(estateID); + region.EstateSettings = m_application.EstateDataService.LoadEstateSettings(region.RegionID, false); - if (!m_application.EstateDataService.LinkRegion(region.RegionID, estateID)) - throw new Exception("Failed to join estate."); + if (region.EstateSettings.EstateID != estateID) + { + // The region is already part of an estate, but not the one we want. + region.EstateSettings = m_application.EstateDataService.LoadEstateSettings(estateID); + + if (!m_application.EstateDataService.LinkRegion(region.RegionID, estateID)) + throw new Exception("Failed to join estate."); + } } // Create the region and perform any initial initialization diff --git a/OpenSim/Data/MSSQL/MSSQLEstateData.cs b/OpenSim/Data/MSSQL/MSSQLEstateData.cs index 9c54e77a23..1faa249aed 100644 --- a/OpenSim/Data/MSSQL/MSSQLEstateData.cs +++ b/OpenSim/Data/MSSQL/MSSQLEstateData.cs @@ -148,55 +148,10 @@ namespace OpenSim.Data.MSSQL } } - if (insertEstate && create) { - List names = new List(FieldList); - - names.Remove("EstateID"); - - sql = string.Format("insert into estate_settings ({0}) values ( @{1})", String.Join(",", names.ToArray()), String.Join(", @", names.ToArray())); - - //_Log.Debug("[DB ESTATE]: SQL: " + sql); - using (SqlConnection conn = new SqlConnection(m_connectionString)) - using (SqlCommand insertCommand = new SqlCommand(sql, conn)) - { - insertCommand.CommandText = sql + " SET @ID = SCOPE_IDENTITY()"; - - foreach (string name in names) - { - insertCommand.Parameters.Add(_Database.CreateParameter("@" + name, _FieldMap[name].GetValue(es))); - } - SqlParameter idParameter = new SqlParameter("@ID", SqlDbType.Int); - idParameter.Direction = ParameterDirection.Output; - insertCommand.Parameters.Add(idParameter); - conn.Open(); - insertCommand.ExecuteNonQuery(); - - es.EstateID = Convert.ToUInt32(idParameter.Value); - } - - sql = "INSERT INTO [estate_map] ([RegionID] ,[EstateID]) VALUES (@RegionID, @EstateID)"; - using (SqlConnection conn = new SqlConnection(m_connectionString)) - using (SqlCommand cmd = new SqlCommand(sql, conn)) - { - - cmd.Parameters.Add(_Database.CreateParameter("@RegionID", regionID)); - cmd.Parameters.Add(_Database.CreateParameter("@EstateID", es.EstateID)); - // This will throw on dupe key - try - { - conn.Open(); - cmd.ExecuteNonQuery(); - } - catch (Exception e) - { - m_log.DebugFormat("[ESTATE DB]: Error inserting regionID and EstateID in estate_map: {0}", e); - } - } - - //TODO check if this is needed?? - es.Save(); + DoCreate(es); + LinkRegion(regionID, (int)es.EstateID); } LoadBanList(es); @@ -210,6 +165,53 @@ namespace OpenSim.Data.MSSQL return es; } + public EstateSettings CreateNewEstate() + { + EstateSettings es = new EstateSettings(); + es.OnSave += StoreEstateSettings; + + DoCreate(es); + + LoadBanList(es); + + es.EstateManagers = LoadUUIDList(es.EstateID, "estate_managers"); + es.EstateAccess = LoadUUIDList(es.EstateID, "estate_users"); + es.EstateGroups = LoadUUIDList(es.EstateID, "estate_groups"); + + return es; + } + + private void DoCreate(EstateSettings es) + { + List names = new List(FieldList); + + names.Remove("EstateID"); + + string sql = string.Format("insert into estate_settings ({0}) values ( @{1})", String.Join(",", names.ToArray()), String.Join(", @", names.ToArray())); + + //_Log.Debug("[DB ESTATE]: SQL: " + sql); + using (SqlConnection conn = new SqlConnection(m_connectionString)) + using (SqlCommand insertCommand = new SqlCommand(sql, conn)) + { + insertCommand.CommandText = sql + " SET @ID = SCOPE_IDENTITY()"; + + foreach (string name in names) + { + insertCommand.Parameters.Add(_Database.CreateParameter("@" + name, _FieldMap[name].GetValue(es))); + } + SqlParameter idParameter = new SqlParameter("@ID", SqlDbType.Int); + idParameter.Direction = ParameterDirection.Output; + insertCommand.Parameters.Add(idParameter); + conn.Open(); + insertCommand.ExecuteNonQuery(); + + es.EstateID = Convert.ToUInt32(idParameter.Value); + } + + //TODO check if this is needed?? + es.Save(); + } + /// /// Stores the estate settings. /// @@ -498,24 +500,43 @@ namespace OpenSim.Data.MSSQL public bool LinkRegion(UUID regionID, int estateID) { - string sql = "insert into estate_map values (@RegionID, @EstateID)"; + string deleteSQL = "delete from estate_map where RegionID = @RegionID"; + string insertSQL = "insert into estate_map values (@RegionID, @EstateID)"; using (SqlConnection conn = new SqlConnection(m_connectionString)) { conn.Open(); + SqlTransaction transaction = conn.BeginTransaction(); + try { - using (SqlCommand cmd = new SqlCommand(sql, conn)) + using (SqlCommand cmd = new SqlCommand(deleteSQL, conn)) { - cmd.Parameters.AddWithValue("@RegionID", regionID); + cmd.Transaction = transaction; + cmd.Parameters.AddWithValue("@RegionID", regionID.Guid); + + cmd.ExecuteNonQuery(); + } + + using (SqlCommand cmd = new SqlCommand(insertSQL, conn)) + { + cmd.Transaction = transaction; + cmd.Parameters.AddWithValue("@RegionID", regionID.Guid); cmd.Parameters.AddWithValue("@EstateID", estateID); int ret = cmd.ExecuteNonQuery(); + + if (ret != 0) + transaction.Commit(); + else + transaction.Rollback(); + return (ret != 0); } } catch (Exception ex) { m_log.Error("[REGION DB]: LinkRegion failed: " + ex.Message); + transaction.Rollback(); } } return false; diff --git a/OpenSim/Data/MySQL/MySQLEstateData.cs b/OpenSim/Data/MySQL/MySQLEstateData.cs index 9dcf5e2adc..3d647ca38c 100644 --- a/OpenSim/Data/MySQL/MySQLEstateData.cs +++ b/OpenSim/Data/MySQL/MySQLEstateData.cs @@ -149,6 +149,22 @@ namespace OpenSim.Data.MySQL } } + public EstateSettings CreateNewEstate() + { + EstateSettings es = new EstateSettings(); + es.OnSave += StoreEstateSettings; + + DoCreate(es); + + LoadBanList(es); + + es.EstateManagers = LoadUUIDList(es.EstateID, "estate_managers"); + es.EstateAccess = LoadUUIDList(es.EstateID, "estate_users"); + es.EstateGroups = LoadUUIDList(es.EstateID, "estate_groups"); + + return es; + } + private EstateSettings DoLoad(MySqlCommand cmd, UUID regionID, bool create) { EstateSettings es = new EstateSettings(); @@ -188,54 +204,8 @@ namespace OpenSim.Data.MySQL if (!found && create) { - // Migration case - List names = new List(FieldList); - - names.Remove("EstateID"); - - string sql = "insert into estate_settings (" + String.Join(",", names.ToArray()) + ") values ( ?" + String.Join(", ?", names.ToArray()) + ")"; - - using (MySqlCommand cmd2 = dbcon.CreateCommand()) - { - cmd2.CommandText = sql; - cmd2.Parameters.Clear(); - - foreach (string name in FieldList) - { - if (m_FieldMap[name].GetValue(es) is bool) - { - if ((bool)m_FieldMap[name].GetValue(es)) - cmd2.Parameters.AddWithValue("?" + name, "1"); - else - cmd2.Parameters.AddWithValue("?" + name, "0"); - } - else - { - cmd2.Parameters.AddWithValue("?" + name, m_FieldMap[name].GetValue(es).ToString()); - } - } - - cmd2.ExecuteNonQuery(); - - cmd2.CommandText = "select LAST_INSERT_ID() as id"; - cmd2.Parameters.Clear(); - - using (IDataReader r = cmd2.ExecuteReader()) - { - r.Read(); - es.EstateID = Convert.ToUInt32(r["id"]); - } - - cmd2.CommandText = "insert into estate_map values (?RegionID, ?EstateID)"; - cmd2.Parameters.AddWithValue("?RegionID", regionID.ToString()); - cmd2.Parameters.AddWithValue("?EstateID", es.EstateID.ToString()); - - // This will throw on dupe key - try { cmd2.ExecuteNonQuery(); } - catch (Exception) { } - - es.Save(); - } + DoCreate(es); + LinkRegion(regionID, (int)es.EstateID); } } @@ -247,6 +217,54 @@ namespace OpenSim.Data.MySQL return es; } + private void DoCreate(EstateSettings es) + { + // Migration case + List names = new List(FieldList); + + names.Remove("EstateID"); + + string sql = "insert into estate_settings (" + String.Join(",", names.ToArray()) + ") values ( ?" + String.Join(", ?", names.ToArray()) + ")"; + + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + using (MySqlCommand cmd2 = dbcon.CreateCommand()) + { + cmd2.CommandText = sql; + cmd2.Parameters.Clear(); + + foreach (string name in FieldList) + { + if (m_FieldMap[name].GetValue(es) is bool) + { + if ((bool)m_FieldMap[name].GetValue(es)) + cmd2.Parameters.AddWithValue("?" + name, "1"); + else + cmd2.Parameters.AddWithValue("?" + name, "0"); + } + else + { + cmd2.Parameters.AddWithValue("?" + name, m_FieldMap[name].GetValue(es).ToString()); + } + } + + cmd2.ExecuteNonQuery(); + + cmd2.CommandText = "select LAST_INSERT_ID() as id"; + cmd2.Parameters.Clear(); + + using (IDataReader r = cmd2.ExecuteReader()) + { + r.Read(); + es.EstateID = Convert.ToUInt32(r["id"]); + } + + es.Save(); + } + } + } + public void StoreEstateSettings(EstateSettings es) { string sql = "replace into estate_settings (" + String.Join(",", FieldList) + ") values ( ?" + String.Join(", ?", FieldList) + ")"; @@ -477,7 +495,6 @@ namespace OpenSim.Data.MySQL } } - dbcon.Close(); } @@ -507,7 +524,6 @@ namespace OpenSim.Data.MySQL } } - dbcon.Close(); } @@ -519,16 +535,34 @@ namespace OpenSim.Data.MySQL using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) { dbcon.Open(); + MySqlTransaction transaction = dbcon.BeginTransaction(); try { + // Delete any existing association of this region with an estate. + using (MySqlCommand cmd = dbcon.CreateCommand()) + { + cmd.Transaction = transaction; + cmd.CommandText = "delete from estate_map where RegionID = ?RegionID"; + cmd.Parameters.AddWithValue("?RegionID", regionID); + + cmd.ExecuteNonQuery(); + } + using (MySqlCommand cmd = dbcon.CreateCommand()) { + cmd.Transaction = transaction; cmd.CommandText = "insert into estate_map values (?RegionID, ?EstateID)"; cmd.Parameters.AddWithValue("?RegionID", regionID); cmd.Parameters.AddWithValue("?EstateID", estateID); int ret = cmd.ExecuteNonQuery(); + + if (ret != 0) + transaction.Commit(); + else + transaction.Rollback(); + dbcon.Close(); return (ret != 0); @@ -537,6 +571,7 @@ namespace OpenSim.Data.MySQL catch (MySqlException ex) { m_log.Error("[REGION DB]: LinkRegion failed: " + ex.Message); + transaction.Rollback(); } dbcon.Close(); diff --git a/OpenSim/Data/Null/NullEstateData.cs b/OpenSim/Data/Null/NullEstateData.cs index 8db8064cbe..d64136de8d 100755 --- a/OpenSim/Data/Null/NullEstateData.cs +++ b/OpenSim/Data/Null/NullEstateData.cs @@ -84,6 +84,11 @@ namespace OpenSim.Data.Null { return new EstateSettings(); } + + public EstateSettings CreateNewEstate() + { + return new EstateSettings(); + } public List LoadEstateSettingsAll() { diff --git a/OpenSim/Data/SQLite/SQLiteEstateData.cs b/OpenSim/Data/SQLite/SQLiteEstateData.cs index 65719a63fa..8db9262e18 100644 --- a/OpenSim/Data/SQLite/SQLiteEstateData.cs +++ b/OpenSim/Data/SQLite/SQLiteEstateData.cs @@ -151,59 +151,8 @@ namespace OpenSim.Data.SQLite } else if (create) { - r.Close(); - - List names = new List(FieldList); - - names.Remove("EstateID"); - - string sql = "insert into estate_settings ("+String.Join(",", names.ToArray())+") values ( :"+String.Join(", :", names.ToArray())+")"; - - cmd.CommandText = sql; - cmd.Parameters.Clear(); - - foreach (string name in FieldList) - { - if (m_FieldMap[name].GetValue(es) is bool) - { - if ((bool)m_FieldMap[name].GetValue(es)) - cmd.Parameters.AddWithValue(":"+name, "1"); - else - cmd.Parameters.AddWithValue(":"+name, "0"); - } - else - { - cmd.Parameters.AddWithValue(":"+name, m_FieldMap[name].GetValue(es).ToString()); - } - } - - cmd.ExecuteNonQuery(); - - cmd.CommandText = "select LAST_INSERT_ROWID() as id"; - cmd.Parameters.Clear(); - - r = cmd.ExecuteReader(); - - r.Read(); - - es.EstateID = Convert.ToUInt32(r["id"]); - - r.Close(); - - cmd.CommandText = "insert into estate_map values (:RegionID, :EstateID)"; - cmd.Parameters.AddWithValue(":RegionID", regionID.ToString()); - cmd.Parameters.AddWithValue(":EstateID", es.EstateID.ToString()); - - // This will throw on dupe key - try - { - cmd.ExecuteNonQuery(); - } - catch (Exception) - { - } - - es.Save(); + DoCreate(es); + LinkRegion(regionID, (int)es.EstateID); } LoadBanList(es); @@ -214,6 +163,67 @@ namespace OpenSim.Data.SQLite return es; } + public EstateSettings CreateNewEstate() + { + EstateSettings es = new EstateSettings(); + es.OnSave += StoreEstateSettings; + + DoCreate(es); + + LoadBanList(es); + + es.EstateManagers = LoadUUIDList(es.EstateID, "estate_managers"); + es.EstateAccess = LoadUUIDList(es.EstateID, "estate_users"); + es.EstateGroups = LoadUUIDList(es.EstateID, "estate_groups"); + + return es; + } + + private void DoCreate(EstateSettings es) + { + List names = new List(FieldList); + + SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand(); + IDataReader r = null; + + names.Remove("EstateID"); + + string sql = "insert into estate_settings ("+String.Join(",", names.ToArray())+") values ( :"+String.Join(", :", names.ToArray())+")"; + + cmd.CommandText = sql; + cmd.Parameters.Clear(); + + foreach (string name in FieldList) + { + if (m_FieldMap[name].GetValue(es) is bool) + { + if ((bool)m_FieldMap[name].GetValue(es)) + cmd.Parameters.AddWithValue(":"+name, "1"); + else + cmd.Parameters.AddWithValue(":"+name, "0"); + } + else + { + cmd.Parameters.AddWithValue(":"+name, m_FieldMap[name].GetValue(es).ToString()); + } + } + + cmd.ExecuteNonQuery(); + + cmd.CommandText = "select LAST_INSERT_ROWID() as id"; + cmd.Parameters.Clear(); + + r = cmd.ExecuteReader(); + + r.Read(); + + es.EstateID = Convert.ToUInt32(r["id"]); + + r.Close(); + + es.Save(); + } + public void StoreEstateSettings(EstateSettings es) { List fields = new List(FieldList); @@ -440,16 +450,36 @@ namespace OpenSim.Data.SQLite public bool LinkRegion(UUID regionID, int estateID) { - SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand(); + SqliteTransaction transaction = m_connection.BeginTransaction(); - cmd.CommandText = "insert into estate_map values (:RegionID, :EstateID)"; - cmd.Parameters.AddWithValue(":RegionID", regionID.ToString()); - cmd.Parameters.AddWithValue(":EstateID", estateID.ToString()); + // Delete any existing estate mapping for this region. + using(SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + cmd.CommandText = "delete from estate_map where RegionID = :RegionID"; + cmd.Transaction = transaction; + cmd.Parameters.AddWithValue(":RegionID", regionID.ToString()); - if (cmd.ExecuteNonQuery() == 0) - return false; + cmd.ExecuteNonQuery(); + } - return true; + using(SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + cmd.CommandText = "insert into estate_map values (:RegionID, :EstateID)"; + cmd.Transaction = transaction; + cmd.Parameters.AddWithValue(":RegionID", regionID.ToString()); + cmd.Parameters.AddWithValue(":EstateID", estateID.ToString()); + + if (cmd.ExecuteNonQuery() == 0) + { + transaction.Rollback(); + return false; + } + else + { + transaction.Commit(); + return true; + } + } } public List GetRegions(int estateID) diff --git a/OpenSim/Region/Framework/Interfaces/IEstateDataService.cs b/OpenSim/Region/Framework/Interfaces/IEstateDataService.cs index 7066cf21e0..35cc220b17 100644 --- a/OpenSim/Region/Framework/Interfaces/IEstateDataService.cs +++ b/OpenSim/Region/Framework/Interfaces/IEstateDataService.cs @@ -1,4 +1,4 @@ -/* +/* * Copyright (c) Contributors, http://opensimulator.org/ * See CONTRIBUTORS.TXT for a full list of copyright holders. * @@ -48,6 +48,14 @@ namespace OpenSim.Region.Framework.Interfaces /// /// EstateSettings LoadEstateSettings(int estateID); + + /// + /// Create a new estate. + /// + /// + /// A + /// + EstateSettings CreateNewEstate(); /// /// Load/Get all estate settings. diff --git a/OpenSim/Region/Framework/Interfaces/IEstateDataStore.cs b/OpenSim/Region/Framework/Interfaces/IEstateDataStore.cs index d790a30b8a..8febb13d4d 100644 --- a/OpenSim/Region/Framework/Interfaces/IEstateDataStore.cs +++ b/OpenSim/Region/Framework/Interfaces/IEstateDataStore.cs @@ -54,6 +54,14 @@ namespace OpenSim.Region.Framework.Interfaces /// EstateSettings LoadEstateSettings(int estateID); + /// + /// Create a new estate. + /// + /// + /// A + /// + EstateSettings CreateNewEstate(); + /// /// Load/Get all estate settings. /// diff --git a/OpenSim/Services/Connectors/Simulation/EstateDataService.cs b/OpenSim/Services/Connectors/Simulation/EstateDataService.cs index 7184ba188e..cdcdecf9b1 100644 --- a/OpenSim/Services/Connectors/Simulation/EstateDataService.cs +++ b/OpenSim/Services/Connectors/Simulation/EstateDataService.cs @@ -1,4 +1,4 @@ -/* +/* * Copyright (c) Contributors, http://opensimulator.org/ * See CONTRIBUTORS.TXT for a full list of copyright holders. * @@ -90,6 +90,11 @@ namespace OpenSim.Services.Connectors { return m_database.LoadEstateSettings(estateID); } + + public EstateSettings CreateNewEstate() + { + return m_database.CreateNewEstate(); + } public List LoadEstateSettingsAll() {