diff options
Diffstat (limited to 'OpenSim/Data/PGSQL/PGSQLRegionData.cs')
-rw-r--r-- | OpenSim/Data/PGSQL/PGSQLRegionData.cs | 432 |
1 files changed, 432 insertions, 0 deletions
diff --git a/OpenSim/Data/PGSQL/PGSQLRegionData.cs b/OpenSim/Data/PGSQL/PGSQLRegionData.cs new file mode 100644 index 0000000..1272e37 --- /dev/null +++ b/OpenSim/Data/PGSQL/PGSQLRegionData.cs | |||
@@ -0,0 +1,432 @@ | |||
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; | ||
30 | using System.Collections.Generic; | ||
31 | using System.Data; | ||
32 | using System.Reflection; | ||
33 | using log4net; | ||
34 | using OpenMetaverse; | ||
35 | using OpenSim.Framework; | ||
36 | using OpenSim.Data; | ||
37 | using RegionFlags = OpenSim.Framework.RegionFlags; | ||
38 | using Npgsql; | ||
39 | |||
40 | namespace OpenSim.Data.PGSQL | ||
41 | { | ||
42 | /// <summary> | ||
43 | /// A PGSQL Interface for the Region Server. | ||
44 | /// </summary> | ||
45 | public class PGSQLRegionData : IRegionData | ||
46 | { | ||
47 | private string m_Realm; | ||
48 | private List<string> m_ColumnNames = null; | ||
49 | private string m_ConnectionString; | ||
50 | private PGSQLManager m_database; | ||
51 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
52 | |||
53 | protected Dictionary<string, string> m_FieldTypes = new Dictionary<string, string>(); | ||
54 | |||
55 | protected virtual Assembly Assembly | ||
56 | { | ||
57 | get { return GetType().Assembly; } | ||
58 | } | ||
59 | |||
60 | public PGSQLRegionData(string connectionString, string realm) | ||
61 | { | ||
62 | m_Realm = realm; | ||
63 | m_ConnectionString = connectionString; | ||
64 | m_database = new PGSQLManager(connectionString); | ||
65 | |||
66 | using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) | ||
67 | { | ||
68 | conn.Open(); | ||
69 | Migration m = new Migration(conn, GetType().Assembly, "GridStore"); | ||
70 | m.Update(); | ||
71 | } | ||
72 | LoadFieldTypes(); | ||
73 | } | ||
74 | |||
75 | private void LoadFieldTypes() | ||
76 | { | ||
77 | m_FieldTypes = new Dictionary<string, string>(); | ||
78 | |||
79 | string query = string.Format(@"select column_name,data_type | ||
80 | from INFORMATION_SCHEMA.COLUMNS | ||
81 | where table_name = lower('{0}'); | ||
82 | |||
83 | ", m_Realm); | ||
84 | using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) | ||
85 | using (NpgsqlCommand cmd = new NpgsqlCommand(query, conn)) | ||
86 | { | ||
87 | conn.Open(); | ||
88 | using (NpgsqlDataReader rdr = cmd.ExecuteReader()) | ||
89 | { | ||
90 | while (rdr.Read()) | ||
91 | { | ||
92 | // query produces 0 to many rows of single column, so always add the first item in each row | ||
93 | m_FieldTypes.Add((string)rdr[0], (string)rdr[1]); | ||
94 | } | ||
95 | } | ||
96 | } | ||
97 | } | ||
98 | |||
99 | public List<RegionData> Get(string regionName, UUID scopeID) | ||
100 | { | ||
101 | string sql = "select * from "+m_Realm+" where lower(\"regionName\") like lower(:regionName) "; | ||
102 | if (scopeID != UUID.Zero) | ||
103 | sql += " and \"ScopeID\" = :scopeID"; | ||
104 | sql += " order by lower(\"regionName\")"; | ||
105 | |||
106 | using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) | ||
107 | using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) | ||
108 | { | ||
109 | cmd.Parameters.Add(m_database.CreateParameter("regionName", regionName)); | ||
110 | if (scopeID != UUID.Zero) | ||
111 | cmd.Parameters.Add(m_database.CreateParameter("scopeID", scopeID)); | ||
112 | conn.Open(); | ||
113 | return RunCommand(cmd); | ||
114 | } | ||
115 | } | ||
116 | |||
117 | public RegionData Get(int posX, int posY, UUID scopeID) | ||
118 | { | ||
119 | // extend database search for maximum region size area | ||
120 | string sql = "select * from "+m_Realm+" where \"locX\" between :startX and :endX and \"locY\" between :startY and :endY"; | ||
121 | if (scopeID != UUID.Zero) | ||
122 | sql += " and \"ScopeID\" = :scopeID"; | ||
123 | |||
124 | int startX = posX - (int)Constants.MaximumRegionSize; | ||
125 | int startY = posY - (int)Constants.MaximumRegionSize; | ||
126 | int endX = posX; | ||
127 | int endY = posY; | ||
128 | |||
129 | List<RegionData> ret; | ||
130 | using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) | ||
131 | using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) | ||
132 | { | ||
133 | cmd.Parameters.Add(m_database.CreateParameter("startX", startX)); | ||
134 | cmd.Parameters.Add(m_database.CreateParameter("startY", startY)); | ||
135 | cmd.Parameters.Add(m_database.CreateParameter("endX", endX)); | ||
136 | cmd.Parameters.Add(m_database.CreateParameter("endY", endY)); | ||
137 | if (scopeID != UUID.Zero) | ||
138 | cmd.Parameters.Add(m_database.CreateParameter("scopeID", scopeID)); | ||
139 | conn.Open(); | ||
140 | ret = RunCommand(cmd); | ||
141 | } | ||
142 | |||
143 | if (ret.Count == 0) | ||
144 | return null; | ||
145 | |||
146 | // Find the first that contains pos | ||
147 | RegionData rg = null; | ||
148 | foreach (RegionData r in ret) | ||
149 | { | ||
150 | if (posX >= r.posX && posX < r.posX + r.sizeX | ||
151 | && posY >= r.posY && posY < r.posY + r.sizeY) | ||
152 | { | ||
153 | rg = r; | ||
154 | break; | ||
155 | } | ||
156 | } | ||
157 | |||
158 | return rg; | ||
159 | } | ||
160 | |||
161 | public RegionData Get(UUID regionID, UUID scopeID) | ||
162 | { | ||
163 | string sql = "select * from "+m_Realm+" where uuid = :regionID"; | ||
164 | if (scopeID != UUID.Zero) | ||
165 | sql += " and \"ScopeID\" = :scopeID"; | ||
166 | using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) | ||
167 | using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) | ||
168 | { | ||
169 | cmd.Parameters.Add(m_database.CreateParameter("regionID", regionID)); | ||
170 | if (scopeID != UUID.Zero) | ||
171 | cmd.Parameters.Add(m_database.CreateParameter("scopeID", scopeID)); | ||
172 | conn.Open(); | ||
173 | List<RegionData> ret = RunCommand(cmd); | ||
174 | if (ret.Count == 0) | ||
175 | return null; | ||
176 | |||
177 | return ret[0]; | ||
178 | } | ||
179 | } | ||
180 | |||
181 | public List<RegionData> Get(int startX, int startY, int endX, int endY, UUID scopeID) | ||
182 | { | ||
183 | // extend database search for maximum region size area | ||
184 | string sql = "select * from "+m_Realm+" where \"locX\" between :startX and :endX and \"locY\" between :startY and :endY"; | ||
185 | if (scopeID != UUID.Zero) | ||
186 | sql += " and \"ScopeID\" = :scopeID"; | ||
187 | |||
188 | int qstartX = startX - (int)Constants.MaximumRegionSize; | ||
189 | int qstartY = startY - (int)Constants.MaximumRegionSize; | ||
190 | |||
191 | List<RegionData> dbret; | ||
192 | using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) | ||
193 | using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) | ||
194 | { | ||
195 | cmd.Parameters.Add(m_database.CreateParameter("startX", qstartX)); | ||
196 | cmd.Parameters.Add(m_database.CreateParameter("startY", qstartY)); | ||
197 | cmd.Parameters.Add(m_database.CreateParameter("endX", endX)); | ||
198 | cmd.Parameters.Add(m_database.CreateParameter("endY", endY)); | ||
199 | if (scopeID != UUID.Zero) | ||
200 | cmd.Parameters.Add(m_database.CreateParameter("scopeID", scopeID)); | ||
201 | conn.Open(); | ||
202 | |||
203 | dbret = RunCommand(cmd); | ||
204 | } | ||
205 | |||
206 | List<RegionData> ret = new List<RegionData>(); | ||
207 | |||
208 | if(dbret.Count == 0) | ||
209 | return ret; | ||
210 | |||
211 | foreach (RegionData r in dbret) | ||
212 | { | ||
213 | if (r.posX + r.sizeX > startX && r.posX <= endX | ||
214 | && r.posY + r.sizeY > startY && r.posY <= endY) | ||
215 | ret.Add(r); | ||
216 | } | ||
217 | return ret; | ||
218 | } | ||
219 | |||
220 | public List<RegionData> RunCommand(NpgsqlCommand cmd) | ||
221 | { | ||
222 | List<RegionData> retList = new List<RegionData>(); | ||
223 | |||
224 | NpgsqlDataReader result = cmd.ExecuteReader(); | ||
225 | |||
226 | while (result.Read()) | ||
227 | { | ||
228 | RegionData ret = new RegionData(); | ||
229 | ret.Data = new Dictionary<string, object>(); | ||
230 | |||
231 | UUID regionID; | ||
232 | UUID.TryParse(result["uuid"].ToString(), out regionID); | ||
233 | ret.RegionID = regionID; | ||
234 | UUID scope; | ||
235 | UUID.TryParse(result["ScopeID"].ToString(), out scope); | ||
236 | ret.ScopeID = scope; | ||
237 | ret.RegionName = result["regionName"].ToString(); | ||
238 | ret.posX = Convert.ToInt32(result["locX"]); | ||
239 | ret.posY = Convert.ToInt32(result["locY"]); | ||
240 | ret.sizeX = Convert.ToInt32(result["sizeX"]); | ||
241 | ret.sizeY = Convert.ToInt32(result["sizeY"]); | ||
242 | |||
243 | if (m_ColumnNames == null) | ||
244 | { | ||
245 | m_ColumnNames = new List<string>(); | ||
246 | |||
247 | DataTable schemaTable = result.GetSchemaTable(); | ||
248 | foreach (DataRow row in schemaTable.Rows) | ||
249 | m_ColumnNames.Add(row["ColumnName"].ToString()); | ||
250 | } | ||
251 | |||
252 | foreach (string s in m_ColumnNames) | ||
253 | { | ||
254 | if (s == "uuid") | ||
255 | continue; | ||
256 | if (s == "ScopeID") | ||
257 | continue; | ||
258 | if (s == "regionName") | ||
259 | continue; | ||
260 | if (s == "locX") | ||
261 | continue; | ||
262 | if (s == "locY") | ||
263 | continue; | ||
264 | |||
265 | ret.Data[s] = result[s].ToString(); | ||
266 | } | ||
267 | |||
268 | retList.Add(ret); | ||
269 | } | ||
270 | return retList; | ||
271 | } | ||
272 | |||
273 | public bool Store(RegionData data) | ||
274 | { | ||
275 | if (data.Data.ContainsKey("uuid")) | ||
276 | data.Data.Remove("uuid"); | ||
277 | if (data.Data.ContainsKey("ScopeID")) | ||
278 | data.Data.Remove("ScopeID"); | ||
279 | if (data.Data.ContainsKey("regionName")) | ||
280 | data.Data.Remove("regionName"); | ||
281 | if (data.Data.ContainsKey("posX")) | ||
282 | data.Data.Remove("posX"); | ||
283 | if (data.Data.ContainsKey("posY")) | ||
284 | data.Data.Remove("posY"); | ||
285 | if (data.Data.ContainsKey("sizeX")) | ||
286 | data.Data.Remove("sizeX"); | ||
287 | if (data.Data.ContainsKey("sizeY")) | ||
288 | data.Data.Remove("sizeY"); | ||
289 | if (data.Data.ContainsKey("locX")) | ||
290 | data.Data.Remove("locX"); | ||
291 | if (data.Data.ContainsKey("locY")) | ||
292 | data.Data.Remove("locY"); | ||
293 | |||
294 | string[] fields = new List<string>(data.Data.Keys).ToArray(); | ||
295 | |||
296 | using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) | ||
297 | using (NpgsqlCommand cmd = new NpgsqlCommand()) | ||
298 | { | ||
299 | |||
300 | string update = "update " + m_Realm + " set \"locX\"=:posX, \"locY\"=:posY, \"sizeX\"=:sizeX, \"sizeY\"=:sizeY "; | ||
301 | |||
302 | foreach (string field in fields) | ||
303 | { | ||
304 | |||
305 | update += ", "; | ||
306 | update += " \"" + field + "\" = :" + field; | ||
307 | |||
308 | if (m_FieldTypes.ContainsKey(field)) | ||
309 | cmd.Parameters.Add(m_database.CreateParameter(field, data.Data[field], m_FieldTypes[field])); | ||
310 | else | ||
311 | cmd.Parameters.Add(m_database.CreateParameter(field, data.Data[field])); | ||
312 | } | ||
313 | |||
314 | update += " where uuid = :regionID"; | ||
315 | |||
316 | if (data.ScopeID != UUID.Zero) | ||
317 | update += " and \"ScopeID\" = :scopeID"; | ||
318 | |||
319 | cmd.CommandText = update; | ||
320 | cmd.Connection = conn; | ||
321 | cmd.Parameters.Add(m_database.CreateParameter("regionID", data.RegionID)); | ||
322 | cmd.Parameters.Add(m_database.CreateParameter("regionName", data.RegionName)); | ||
323 | cmd.Parameters.Add(m_database.CreateParameter("scopeID", data.ScopeID)); | ||
324 | cmd.Parameters.Add(m_database.CreateParameter("posX", data.posX)); | ||
325 | cmd.Parameters.Add(m_database.CreateParameter("posY", data.posY)); | ||
326 | cmd.Parameters.Add(m_database.CreateParameter("sizeX", data.sizeX)); | ||
327 | cmd.Parameters.Add(m_database.CreateParameter("sizeY", data.sizeY)); | ||
328 | conn.Open(); | ||
329 | try | ||
330 | { | ||
331 | if (cmd.ExecuteNonQuery() < 1) | ||
332 | { | ||
333 | string insert = "insert into " + m_Realm + " (uuid, \"ScopeID\", \"locX\", \"locY\", \"sizeX\", \"sizeY\", \"regionName\", \"" + | ||
334 | String.Join("\", \"", fields) + | ||
335 | "\") values (:regionID, :scopeID, :posX, :posY, :sizeX, :sizeY, :regionName, :" + String.Join(", :", fields) + ")"; | ||
336 | |||
337 | cmd.CommandText = insert; | ||
338 | |||
339 | try | ||
340 | { | ||
341 | if (cmd.ExecuteNonQuery() < 1) | ||
342 | { | ||
343 | return false; | ||
344 | } | ||
345 | } | ||
346 | catch (Exception ex) | ||
347 | { | ||
348 | m_log.Warn("[PGSQL Grid]: Error inserting into Regions table: " + ex.Message + ", INSERT sql: " + insert); | ||
349 | } | ||
350 | } | ||
351 | } | ||
352 | catch (Exception ex) | ||
353 | { | ||
354 | m_log.Warn("[PGSQL Grid]: Error updating Regions table: " + ex.Message + ", UPDATE sql: " + update); | ||
355 | } | ||
356 | } | ||
357 | |||
358 | return true; | ||
359 | } | ||
360 | |||
361 | public bool SetDataItem(UUID regionID, string item, string value) | ||
362 | { | ||
363 | string sql = "update " + m_Realm + | ||
364 | " set \"" + item + "\" = :" + item + " where uuid = :UUID"; | ||
365 | |||
366 | using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) | ||
367 | using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) | ||
368 | { | ||
369 | cmd.Parameters.Add(m_database.CreateParameter("" + item, value)); | ||
370 | cmd.Parameters.Add(m_database.CreateParameter("UUID", regionID)); | ||
371 | conn.Open(); | ||
372 | if (cmd.ExecuteNonQuery() > 0) | ||
373 | return true; | ||
374 | } | ||
375 | return false; | ||
376 | } | ||
377 | |||
378 | public bool Delete(UUID regionID) | ||
379 | { | ||
380 | string sql = "delete from " + m_Realm + | ||
381 | " where uuid = :UUID"; | ||
382 | using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) | ||
383 | using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) | ||
384 | { | ||
385 | cmd.Parameters.Add(m_database.CreateParameter("UUID", regionID)); | ||
386 | conn.Open(); | ||
387 | if (cmd.ExecuteNonQuery() > 0) | ||
388 | return true; | ||
389 | } | ||
390 | return false; | ||
391 | } | ||
392 | |||
393 | public List<RegionData> GetDefaultRegions(UUID scopeID) | ||
394 | { | ||
395 | return Get((int)RegionFlags.DefaultRegion, scopeID); | ||
396 | } | ||
397 | |||
398 | public List<RegionData> GetDefaultHypergridRegions(UUID scopeID) | ||
399 | { | ||
400 | return Get((int)RegionFlags.DefaultHGRegion, scopeID); | ||
401 | } | ||
402 | |||
403 | public List<RegionData> GetFallbackRegions(UUID scopeID, int x, int y) | ||
404 | { | ||
405 | List<RegionData> regions = Get((int)RegionFlags.FallbackRegion, scopeID); | ||
406 | RegionDataDistanceCompare distanceComparer = new RegionDataDistanceCompare(x, y); | ||
407 | regions.Sort(distanceComparer); | ||
408 | |||
409 | return regions; | ||
410 | } | ||
411 | |||
412 | public List<RegionData> GetHyperlinks(UUID scopeID) | ||
413 | { | ||
414 | return Get((int)RegionFlags.Hyperlink, scopeID); | ||
415 | } | ||
416 | |||
417 | private List<RegionData> Get(int regionFlags, UUID scopeID) | ||
418 | { | ||
419 | string sql = "SELECT * FROM " + m_Realm + " WHERE (\"flags\" & " + regionFlags.ToString() + ") <> 0"; | ||
420 | if (scopeID != UUID.Zero) | ||
421 | sql += " AND \"ScopeID\" = :scopeID"; | ||
422 | |||
423 | using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) | ||
424 | using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) | ||
425 | { | ||
426 | cmd.Parameters.Add(m_database.CreateParameter("scopeID", scopeID)); | ||
427 | conn.Open(); | ||
428 | return RunCommand(cmd); | ||
429 | } | ||
430 | } | ||
431 | } | ||
432 | } | ||