aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Data/MySQL/MySQLDataStore.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Data/MySQL/MySQLDataStore.cs')
-rw-r--r--OpenSim/Data/MySQL/MySQLDataStore.cs1722
1 files changed, 1722 insertions, 0 deletions
diff --git a/OpenSim/Data/MySQL/MySQLDataStore.cs b/OpenSim/Data/MySQL/MySQLDataStore.cs
new file mode 100644
index 0000000..eaa7f14
--- /dev/null
+++ b/OpenSim/Data/MySQL/MySQLDataStore.cs
@@ -0,0 +1,1722 @@
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 OpenSim 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
28using System;
29using System.Collections.Generic;
30using System.Data;
31using System.Diagnostics;
32using System.IO;
33using libsecondlife;
34using MySql.Data.MySqlClient;
35using OpenSim.Framework.Console;
36using OpenSim.Region.Environment.Interfaces;
37using OpenSim.Region.Environment.Scenes;
38
39namespace OpenSim.Framework.Data.MySQL
40{
41 public class MySQLDataStore : IRegionDataStore
42 {
43 private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
44
45 private const string m_primSelect = "select * from prims";
46 private const string m_shapeSelect = "select * from primshapes";
47 private const string m_itemsSelect = "select * from primitems";
48 private const string m_terrainSelect = "select * from terrain limit 1";
49 private const string m_landSelect = "select * from land";
50 private const string m_landAccessListSelect = "select * from landaccesslist";
51
52 private DataSet m_dataSet;
53 private MySqlDataAdapter m_primDataAdapter;
54 private MySqlDataAdapter m_shapeDataAdapter;
55 private MySqlDataAdapter m_itemsDataAdapter;
56 private MySqlConnection m_connection;
57 private MySqlDataAdapter m_terrainDataAdapter;
58 private MySqlDataAdapter m_landDataAdapter;
59 private MySqlDataAdapter m_landAccessListDataAdapter;
60
61 private DataTable m_primTable;
62 private DataTable m_shapeTable;
63 private DataTable m_itemsTable;
64 private DataTable m_terrainTable;
65 private DataTable m_landTable;
66 private DataTable m_landAccessListTable;
67
68 // Temporary attribute while this is experimental
69 private bool persistPrimInventories;
70
71 /***********************************************************************
72 *
73 * Public Interface Functions
74 *
75 **********************************************************************/
76
77 // see IRegionDataStore
78 public void Initialise(string connectionstring, bool persistPrimInventories)
79 {
80 m_dataSet = new DataSet();
81 this.persistPrimInventories = persistPrimInventories;
82
83 m_log.Info("[DATASTORE]: MySql - connecting: " + connectionstring);
84 m_connection = new MySqlConnection(connectionstring);
85
86 MySqlCommand primSelectCmd = new MySqlCommand(m_primSelect, m_connection);
87 m_primDataAdapter = new MySqlDataAdapter(primSelectCmd);
88
89 MySqlCommand shapeSelectCmd = new MySqlCommand(m_shapeSelect, m_connection);
90 m_shapeDataAdapter = new MySqlDataAdapter(shapeSelectCmd);
91
92 MySqlCommand itemsSelectCmd = new MySqlCommand(m_itemsSelect, m_connection);
93 m_itemsDataAdapter = new MySqlDataAdapter(itemsSelectCmd);
94
95 MySqlCommand terrainSelectCmd = new MySqlCommand(m_terrainSelect, m_connection);
96 m_terrainDataAdapter = new MySqlDataAdapter(terrainSelectCmd);
97
98 MySqlCommand landSelectCmd = new MySqlCommand(m_landSelect, m_connection);
99 m_landDataAdapter = new MySqlDataAdapter(landSelectCmd);
100
101 MySqlCommand landAccessListSelectCmd = new MySqlCommand(m_landAccessListSelect, m_connection);
102 m_landAccessListDataAdapter = new MySqlDataAdapter(landAccessListSelectCmd);
103
104 TestTables(m_connection);
105
106 lock (m_dataSet)
107 {
108 m_primTable = createPrimTable();
109 m_dataSet.Tables.Add(m_primTable);
110 SetupPrimCommands(m_primDataAdapter, m_connection);
111 m_primDataAdapter.Fill(m_primTable);
112
113 m_shapeTable = createShapeTable();
114 m_dataSet.Tables.Add(m_shapeTable);
115 SetupShapeCommands(m_shapeDataAdapter, m_connection);
116 m_shapeDataAdapter.Fill(m_shapeTable);
117
118 if (persistPrimInventories)
119 {
120 m_itemsTable = createItemsTable();
121 m_dataSet.Tables.Add(m_itemsTable);
122 SetupItemsCommands(m_itemsDataAdapter, m_connection);
123 m_itemsDataAdapter.Fill(m_itemsTable);
124 }
125
126 m_terrainTable = createTerrainTable();
127 m_dataSet.Tables.Add(m_terrainTable);
128 SetupTerrainCommands(m_terrainDataAdapter, m_connection);
129 m_terrainDataAdapter.Fill(m_terrainTable);
130
131 m_landTable = createLandTable();
132 m_dataSet.Tables.Add(m_landTable);
133 setupLandCommands(m_landDataAdapter, m_connection);
134 m_landDataAdapter.Fill(m_landTable);
135
136 m_landAccessListTable = createLandAccessListTable();
137 m_dataSet.Tables.Add(m_landAccessListTable);
138 setupLandAccessCommands(m_landAccessListDataAdapter, m_connection);
139 m_landAccessListDataAdapter.Fill(m_landAccessListTable);
140 }
141 }
142
143 public void StoreObject(SceneObjectGroup obj, LLUUID regionUUID)
144 {
145 lock (m_dataSet)
146 {
147 foreach (SceneObjectPart prim in obj.Children.Values)
148 {
149 if ((prim.ObjectFlags & (uint) LLObject.ObjectFlags.Physics) == 0)
150 {
151 m_log.Info("[DATASTORE]: Adding obj: " + obj.UUID + " to region: " + regionUUID);
152 addPrim(prim, obj.UUID, regionUUID);
153 }
154 else
155 {
156 // m_log.Info("[DATASTORE]: Ignoring Physical obj: " + obj.UUID + " in region: " + regionUUID);
157 }
158 }
159 Commit();
160 }
161 }
162
163 public void RemoveObject(LLUUID obj, LLUUID regionUUID)
164 {
165 m_log.InfoFormat("[DATASTORE]: Removing obj: {0} from region: {1}", obj.UUID, regionUUID);
166
167 DataTable prims = m_primTable;
168 DataTable shapes = m_shapeTable;
169
170 string selectExp = "SceneGroupID = '" + Util.ToRawUuidString(obj) + "'";
171 lock (m_dataSet)
172 {
173 DataRow[] primRows = prims.Select(selectExp);
174 foreach (DataRow row in primRows)
175 {
176 // Remove shapes row
177 LLUUID uuid = new LLUUID((string) row["UUID"]);
178 DataRow shapeRow = shapes.Rows.Find(Util.ToRawUuidString(uuid));
179 if (shapeRow != null)
180 {
181 shapeRow.Delete();
182 }
183
184 if (persistPrimInventories)
185 {
186 RemoveItems(uuid);
187 }
188
189 // Remove prim row
190 row.Delete();
191 }
192 Commit();
193 }
194 }
195
196 /// <summary>
197 /// Remove all persisted items of the given prim.
198 /// The caller must acquire the necessrary synchronization locks and commit or rollback changes.
199 /// </summary>
200 private void RemoveItems(LLUUID uuid)
201 {
202 String sql = String.Format("primID = '{0}'", uuid);
203 DataRow[] itemRows = m_itemsTable.Select(sql);
204
205 foreach (DataRow itemRow in itemRows)
206 {
207 itemRow.Delete();
208 }
209 }
210
211 /// <summary>
212 /// Load persisted objects from region storage.
213 /// </summary>
214 public List<SceneObjectGroup> LoadObjects(LLUUID regionUUID)
215 {
216 Dictionary<LLUUID, SceneObjectGroup> createdObjects = new Dictionary<LLUUID, SceneObjectGroup>();
217
218 List<SceneObjectGroup> retvals = new List<SceneObjectGroup>();
219
220 DataTable prims = m_primTable;
221 DataTable shapes = m_shapeTable;
222
223 string byRegion = "RegionUUID = '" + Util.ToRawUuidString(regionUUID) + "'";
224 string orderByParent = "ParentID ASC";
225
226 lock (m_dataSet)
227 {
228 DataRow[] primsForRegion = prims.Select(byRegion, orderByParent);
229 m_log.Info("[DATASTORE]: " +
230 "Loaded " + primsForRegion.Length + " prims for region: " + regionUUID);
231
232 foreach (DataRow primRow in primsForRegion)
233 {
234 try
235 {
236 string uuid = (string) primRow["UUID"];
237 string objID = (string) primRow["SceneGroupID"];
238
239 SceneObjectPart prim = buildPrim(primRow);
240
241 if (uuid == objID) //is new SceneObjectGroup ?
242 {
243 SceneObjectGroup group = new SceneObjectGroup();
244
245 DataRow shapeRow = shapes.Rows.Find(Util.ToRawUuidString(prim.UUID));
246 if (shapeRow != null)
247 {
248 prim.Shape = buildShape(shapeRow);
249 }
250 else
251 {
252 m_log.Info(
253 "No shape found for prim in storage, so setting default box shape");
254 prim.Shape = PrimitiveBaseShape.Default;
255 }
256 group.AddPart(prim);
257 group.RootPart = prim;
258
259 createdObjects.Add(group.UUID, group);
260 retvals.Add(group);
261 }
262 else
263 {
264 DataRow shapeRow = shapes.Rows.Find(Util.ToRawUuidString(prim.UUID));
265 if (shapeRow != null)
266 {
267 prim.Shape = buildShape(shapeRow);
268 }
269 else
270 {
271 m_log.Info(
272 "No shape found for prim in storage, so setting default box shape");
273 prim.Shape = PrimitiveBaseShape.Default;
274 }
275 createdObjects[new LLUUID(objID)].AddPart(prim);
276 }
277
278 if (persistPrimInventories)
279 {
280 LoadItems(prim);
281 }
282 }
283 catch (Exception e)
284 {
285 m_log.Error("[DATASTORE]: Failed create prim object, exception and data follows");
286 m_log.Info("[DATASTORE]: " + e.ToString());
287 foreach (DataColumn col in prims.Columns)
288 {
289 m_log.Info("[DATASTORE]: Col: " + col.ColumnName + " => " + primRow[col]);
290 }
291 }
292 }
293 }
294 return retvals;
295 }
296
297 /// <summary>
298 /// Load in a prim's persisted inventory.
299 /// </summary>
300 /// <param name="prim"></param>
301 private void LoadItems(SceneObjectPart prim)
302 {
303 //m_log.InfoFormat("[DATASTORE]: Loading inventory for {0}, {1}", prim.Name, prim.UUID);
304
305 DataTable dbItems = m_itemsTable;
306
307 String sql = String.Format("primID = '{0}'", prim.UUID.ToString());
308 DataRow[] dbItemRows = dbItems.Select(sql);
309
310 IList<TaskInventoryItem> inventory = new List<TaskInventoryItem>();
311
312 foreach (DataRow row in dbItemRows)
313 {
314 TaskInventoryItem item = buildItem(row);
315 inventory.Add(item);
316
317 //m_log.DebugFormat("[DATASTORE]: Restored item {0}, {1}", item.Name, item.ItemID);
318 }
319
320 prim.RestoreInventoryItems(inventory);
321
322 // XXX A nasty little hack to recover the folder id for the prim (which is currently stored in
323 // every item). This data should really be stored in the prim table itself.
324 if (dbItemRows.Length > 0)
325 {
326 prim.FolderID = inventory[0].ParentID;
327 }
328 }
329
330 public void StoreTerrain(double[,] ter, LLUUID regionID)
331 {
332 int revision = Util.UnixTimeSinceEpoch();
333 m_log.Info("[DATASTORE]: Storing terrain revision r" + revision.ToString());
334
335 DataTable terrain = m_dataSet.Tables["terrain"];
336 lock (m_dataSet)
337 {
338 MySqlCommand cmd = new MySqlCommand("insert into terrain(RegionUUID, Revision, Heightfield)" +
339 " values(?RegionUUID, ?Revision, ?Heightfield)", m_connection);
340 using (cmd)
341 {
342 cmd.Parameters.Add(new MySqlParameter("?RegionUUID", Util.ToRawUuidString(regionID)));
343 cmd.Parameters.Add(new MySqlParameter("?Revision", revision));
344 cmd.Parameters.Add(new MySqlParameter("?Heightfield", serializeTerrain(ter)));
345 cmd.ExecuteNonQuery();
346 }
347 }
348 }
349
350 public double[,] LoadTerrain(LLUUID regionID)
351 {
352 double[,] terret = new double[256,256];
353 terret.Initialize();
354
355 MySqlCommand cmd = new MySqlCommand(
356 @"select RegionUUID, Revision, Heightfield from terrain
357 where RegionUUID=?RegionUUID order by Revision desc limit 1"
358 , m_connection);
359
360 MySqlParameter param = new MySqlParameter();
361 cmd.Parameters.Add(new MySqlParameter("?RegionUUID", Util.ToRawUuidString(regionID)));
362
363 if (m_connection.State != ConnectionState.Open)
364 {
365 m_connection.Open();
366 }
367
368 lock (m_dataSet)
369 {
370 using (MySqlDataReader row = cmd.ExecuteReader())
371 {
372 int rev = 0;
373 if (row.Read())
374 {
375 MemoryStream str = new MemoryStream((byte[]) row["Heightfield"]);
376 BinaryReader br = new BinaryReader(str);
377 for (int x = 0; x < 256; x++)
378 {
379 for (int y = 0; y < 256; y++)
380 {
381 terret[x, y] = br.ReadDouble();
382 }
383 }
384 rev = (int) row["Revision"];
385 }
386 else
387 {
388 m_log.Info("[DATASTORE]: No terrain found for region");
389 return null;
390 }
391
392 m_log.Info("[DATASTORE]: Loaded terrain revision r" + rev.ToString());
393 }
394 }
395 return terret;
396 }
397
398 public void RemoveLandObject(LLUUID globalID)
399 {
400 lock (m_dataSet)
401 {
402 using (MySqlCommand cmd = new MySqlCommand("delete from land where UUID=?UUID", m_connection))
403 {
404 cmd.Parameters.Add(new MySqlParameter("?UUID", Util.ToRawUuidString(globalID)));
405 cmd.ExecuteNonQuery();
406 }
407
408 using (
409 MySqlCommand cmd = new MySqlCommand("delete from landaccesslist where LandUUID=?UUID", m_connection)
410 )
411 {
412 cmd.Parameters.Add(new MySqlParameter("?UUID", Util.ToRawUuidString(globalID)));
413 cmd.ExecuteNonQuery();
414 }
415 }
416 }
417
418 public void StoreLandObject(ILandObject parcel)
419 {
420 lock (m_dataSet)
421 {
422 DataTable land = m_landTable;
423 DataTable landaccesslist = m_landAccessListTable;
424
425 DataRow landRow = land.Rows.Find(Util.ToRawUuidString(parcel.landData.globalID));
426 if (landRow == null)
427 {
428 landRow = land.NewRow();
429 fillLandRow(landRow, parcel.landData, parcel.regionUUID);
430 land.Rows.Add(landRow);
431 }
432 else
433 {
434 fillLandRow(landRow, parcel.landData, parcel.regionUUID);
435 }
436
437 using (
438 MySqlCommand cmd =
439 new MySqlCommand("delete from landaccesslist where LandUUID=?LandUUID", m_connection))
440 {
441 cmd.Parameters.Add(new MySqlParameter("?LandUUID", Util.ToRawUuidString(parcel.landData.globalID)));
442 cmd.ExecuteNonQuery();
443 }
444
445 foreach (ParcelManager.ParcelAccessEntry entry in parcel.landData.parcelAccessList)
446 {
447 DataRow newAccessRow = landaccesslist.NewRow();
448 fillLandAccessRow(newAccessRow, entry, parcel.landData.globalID);
449 landaccesslist.Rows.Add(newAccessRow);
450 }
451
452 Commit();
453 }
454 }
455
456 public List<LandData> LoadLandObjects(LLUUID regionUUID)
457 {
458 List<LandData> landDataForRegion = new List<LandData>();
459 lock (m_dataSet)
460 {
461 DataTable land = m_landTable;
462 DataTable landaccesslist = m_landAccessListTable;
463 string searchExp = "RegionUUID = '" + Util.ToRawUuidString(regionUUID) + "'";
464 DataRow[] rawDataForRegion = land.Select(searchExp);
465 foreach (DataRow rawDataLand in rawDataForRegion)
466 {
467 LandData newLand = buildLandData(rawDataLand);
468 string accessListSearchExp = "LandUUID = '" + Util.ToRawUuidString(newLand.globalID) + "'";
469 DataRow[] rawDataForLandAccessList = landaccesslist.Select(accessListSearchExp);
470 foreach (DataRow rawDataLandAccess in rawDataForLandAccessList)
471 {
472 newLand.parcelAccessList.Add(buildLandAccessData(rawDataLandAccess));
473 }
474
475 landDataForRegion.Add(newLand);
476 }
477 }
478 return landDataForRegion;
479 }
480
481// TODO: unused
482// private void DisplayDataSet(DataSet ds, string title)
483// {
484// Debug.WriteLine(title);
485// //--- Loop through the DataTables
486// foreach (DataTable table in ds.Tables)
487// {
488// Debug.WriteLine("*** DataTable: " + table.TableName + "***");
489// //--- Loop through each DataTable's DataRows
490// foreach (DataRow row in table.Rows)
491// {
492// //--- Display the original values, if there are any.
493// if (row.HasVersion(DataRowVersion.Original))
494// {
495// Debug.Write("Original Row Values ===> ");
496// foreach (DataColumn column in table.Columns)
497// Debug.Write(column.ColumnName + " = " +
498// row[column, DataRowVersion.Original] + ", ");
499// Debug.WriteLine(String.Empty);
500// }
501// //--- Display the current values, if there are any.
502// if (row.HasVersion(DataRowVersion.Current))
503// {
504// Debug.Write("Current Row Values ====> ");
505// foreach (DataColumn column in table.Columns)
506// Debug.Write(column.ColumnName + " = " +
507// row[column, DataRowVersion.Current] + ", ");
508// Debug.WriteLine(String.Empty);
509// }
510// Debug.WriteLine(String.Empty);
511// }
512// }
513// }
514
515 public void Commit()
516 {
517 if (m_connection.State != ConnectionState.Open)
518 {
519 m_connection.Open();
520 }
521
522 lock (m_dataSet)
523 {
524 // DisplayDataSet(m_dataSet, "Region DataSet");
525
526 m_primDataAdapter.Update(m_primTable);
527 m_shapeDataAdapter.Update(m_shapeTable);
528
529 if (persistPrimInventories)
530 {
531 m_itemsDataAdapter.Update(m_itemsTable);
532 }
533
534 m_terrainDataAdapter.Update(m_terrainTable);
535 m_landDataAdapter.Update(m_landTable);
536 m_landAccessListDataAdapter.Update(m_landAccessListTable);
537
538 m_dataSet.AcceptChanges();
539 }
540 }
541
542
543 public void Shutdown()
544 {
545 Commit();
546 }
547
548 /***********************************************************************
549 *
550 * Database Definition Functions
551 *
552 * This should be db agnostic as we define them in ADO.NET terms
553 *
554 **********************************************************************/
555
556 private DataColumn createCol(DataTable dt, string name, Type type)
557 {
558 DataColumn col = new DataColumn(name, type);
559 dt.Columns.Add(col);
560 return col;
561 }
562
563 private DataTable createTerrainTable()
564 {
565 DataTable terrain = new DataTable("terrain");
566
567 createCol(terrain, "RegionUUID", typeof (String));
568 createCol(terrain, "Revision", typeof (Int32));
569 DataColumn heightField = createCol(terrain, "Heightfield", typeof (Byte[]));
570 return terrain;
571 }
572
573 private DataTable createPrimTable()
574 {
575 DataTable prims = new DataTable("prims");
576
577 createCol(prims, "UUID", typeof (String));
578 createCol(prims, "RegionUUID", typeof (String));
579 createCol(prims, "ParentID", typeof (Int32));
580 createCol(prims, "CreationDate", typeof (Int32));
581 createCol(prims, "Name", typeof (String));
582 createCol(prims, "SceneGroupID", typeof (String));
583 // various text fields
584 createCol(prims, "Text", typeof (String));
585 createCol(prims, "Description", typeof (String));
586 createCol(prims, "SitName", typeof (String));
587 createCol(prims, "TouchName", typeof (String));
588 // permissions
589 createCol(prims, "ObjectFlags", typeof (Int32));
590 createCol(prims, "CreatorID", typeof (String));
591 createCol(prims, "OwnerID", typeof (String));
592 createCol(prims, "GroupID", typeof (String));
593 createCol(prims, "LastOwnerID", typeof (String));
594 createCol(prims, "OwnerMask", typeof (Int32));
595 createCol(prims, "NextOwnerMask", typeof (Int32));
596 createCol(prims, "GroupMask", typeof (Int32));
597 createCol(prims, "EveryoneMask", typeof (Int32));
598 createCol(prims, "BaseMask", typeof (Int32));
599 // vectors
600 createCol(prims, "PositionX", typeof (Double));
601 createCol(prims, "PositionY", typeof (Double));
602 createCol(prims, "PositionZ", typeof (Double));
603 createCol(prims, "GroupPositionX", typeof (Double));
604 createCol(prims, "GroupPositionY", typeof (Double));
605 createCol(prims, "GroupPositionZ", typeof (Double));
606 createCol(prims, "VelocityX", typeof (Double));
607 createCol(prims, "VelocityY", typeof (Double));
608 createCol(prims, "VelocityZ", typeof (Double));
609 createCol(prims, "AngularVelocityX", typeof (Double));
610 createCol(prims, "AngularVelocityY", typeof (Double));
611 createCol(prims, "AngularVelocityZ", typeof (Double));
612 createCol(prims, "AccelerationX", typeof (Double));
613 createCol(prims, "AccelerationY", typeof (Double));
614 createCol(prims, "AccelerationZ", typeof (Double));
615 // quaternions
616 createCol(prims, "RotationX", typeof (Double));
617 createCol(prims, "RotationY", typeof (Double));
618 createCol(prims, "RotationZ", typeof (Double));
619 createCol(prims, "RotationW", typeof (Double));
620 // sit target
621 createCol(prims, "SitTargetOffsetX", typeof (Double));
622 createCol(prims, "SitTargetOffsetY", typeof (Double));
623 createCol(prims, "SitTargetOffsetZ", typeof (Double));
624
625 createCol(prims, "SitTargetOrientW", typeof (Double));
626 createCol(prims, "SitTargetOrientX", typeof (Double));
627 createCol(prims, "SitTargetOrientY", typeof (Double));
628 createCol(prims, "SitTargetOrientZ", typeof (Double));
629
630
631 // Add in contraints
632 prims.PrimaryKey = new DataColumn[] {prims.Columns["UUID"]};
633
634 return prims;
635 }
636
637 private DataTable createLandTable()
638 {
639 DataTable land = new DataTable("land");
640 createCol(land, "UUID", typeof (String));
641 createCol(land, "RegionUUID", typeof (String));
642 createCol(land, "LocalLandID", typeof (Int32));
643
644 // Bitmap is a byte[512]
645 createCol(land, "Bitmap", typeof (Byte[]));
646
647 createCol(land, "Name", typeof (String));
648 createCol(land, "Description", typeof (String));
649 createCol(land, "OwnerUUID", typeof (String));
650 createCol(land, "IsGroupOwned", typeof (Int32));
651 createCol(land, "Area", typeof (Int32));
652 createCol(land, "AuctionID", typeof (Int32)); //Unemplemented
653 createCol(land, "Category", typeof (Int32)); //Enum libsecondlife.Parcel.ParcelCategory
654 createCol(land, "ClaimDate", typeof (Int32));
655 createCol(land, "ClaimPrice", typeof (Int32));
656 createCol(land, "GroupUUID", typeof (String));
657 createCol(land, "SalePrice", typeof (Int32));
658 createCol(land, "LandStatus", typeof (Int32)); //Enum. libsecondlife.Parcel.ParcelStatus
659 createCol(land, "LandFlags", typeof (Int32));
660 createCol(land, "LandingType", typeof (Int32));
661 createCol(land, "MediaAutoScale", typeof (Int32));
662 createCol(land, "MediaTextureUUID", typeof (String));
663 createCol(land, "MediaURL", typeof (String));
664 createCol(land, "MusicURL", typeof (String));
665 createCol(land, "PassHours", typeof (Double));
666 createCol(land, "PassPrice", typeof (Int32));
667 createCol(land, "SnapshotUUID", typeof (String));
668 createCol(land, "UserLocationX", typeof (Double));
669 createCol(land, "UserLocationY", typeof (Double));
670 createCol(land, "UserLocationZ", typeof (Double));
671 createCol(land, "UserLookAtX", typeof (Double));
672 createCol(land, "UserLookAtY", typeof (Double));
673 createCol(land, "UserLookAtZ", typeof (Double));
674
675 land.PrimaryKey = new DataColumn[] {land.Columns["UUID"]};
676
677 return land;
678 }
679
680 private DataTable createLandAccessListTable()
681 {
682 DataTable landaccess = new DataTable("landaccesslist");
683 createCol(landaccess, "LandUUID", typeof (String));
684 createCol(landaccess, "AccessUUID", typeof (String));
685 createCol(landaccess, "Flags", typeof (Int32));
686
687 return landaccess;
688 }
689
690 private DataTable createShapeTable()
691 {
692 DataTable shapes = new DataTable("primshapes");
693 createCol(shapes, "UUID", typeof (String));
694 // shape is an enum
695 createCol(shapes, "Shape", typeof (Int32));
696 // vectors
697 createCol(shapes, "ScaleX", typeof (Double));
698 createCol(shapes, "ScaleY", typeof (Double));
699 createCol(shapes, "ScaleZ", typeof (Double));
700 // paths
701 createCol(shapes, "PCode", typeof (Int32));
702 createCol(shapes, "PathBegin", typeof (Int32));
703 createCol(shapes, "PathEnd", typeof (Int32));
704 createCol(shapes, "PathScaleX", typeof (Int32));
705 createCol(shapes, "PathScaleY", typeof (Int32));
706 createCol(shapes, "PathShearX", typeof (Int32));
707 createCol(shapes, "PathShearY", typeof (Int32));
708 createCol(shapes, "PathSkew", typeof (Int32));
709 createCol(shapes, "PathCurve", typeof (Int32));
710 createCol(shapes, "PathRadiusOffset", typeof (Int32));
711 createCol(shapes, "PathRevolutions", typeof (Int32));
712 createCol(shapes, "PathTaperX", typeof (Int32));
713 createCol(shapes, "PathTaperY", typeof (Int32));
714 createCol(shapes, "PathTwist", typeof (Int32));
715 createCol(shapes, "PathTwistBegin", typeof (Int32));
716 // profile
717 createCol(shapes, "ProfileBegin", typeof (Int32));
718 createCol(shapes, "ProfileEnd", typeof (Int32));
719 createCol(shapes, "ProfileCurve", typeof (Int32));
720 createCol(shapes, "ProfileHollow", typeof (Int32));
721 createCol(shapes, "State", typeof(Int32));
722 createCol(shapes, "Texture", typeof (Byte[]));
723 createCol(shapes, "ExtraParams", typeof (Byte[]));
724
725 shapes.PrimaryKey = new DataColumn[] {shapes.Columns["UUID"]};
726
727 return shapes;
728 }
729
730 private DataTable createItemsTable()
731 {
732 DataTable items = new DataTable("primitems");
733
734 createCol(items, "itemID", typeof (String));
735 createCol(items, "primID", typeof (String));
736 createCol(items, "assetID", typeof (String));
737 createCol(items, "parentFolderID", typeof (String));
738
739 createCol(items, "invType", typeof (Int32));
740 createCol(items, "assetType", typeof (Int32));
741
742 createCol(items, "name", typeof (String));
743 createCol(items, "description", typeof (String));
744
745 createCol(items, "creationDate", typeof (Int64));
746 createCol(items, "creatorID", typeof (String));
747 createCol(items, "ownerID", typeof (String));
748 createCol(items, "lastOwnerID", typeof (String));
749 createCol(items, "groupID", typeof (String));
750
751 createCol(items, "nextPermissions", typeof (Int32));
752 createCol(items, "currentPermissions", typeof (Int32));
753 createCol(items, "basePermissions", typeof (Int32));
754 createCol(items, "everyonePermissions", typeof (Int32));
755 createCol(items, "groupPermissions", typeof (Int32));
756
757 items.PrimaryKey = new DataColumn[] {items.Columns["itemID"]};
758
759 return items;
760 }
761
762 /***********************************************************************
763 *
764 * Convert between ADO.NET <=> OpenSim Objects
765 *
766 * These should be database independant
767 *
768 **********************************************************************/
769
770 private SceneObjectPart buildPrim(DataRow row)
771 {
772 SceneObjectPart prim = new SceneObjectPart();
773 prim.UUID = new LLUUID((String) row["UUID"]);
774 // explicit conversion of integers is required, which sort
775 // of sucks. No idea if there is a shortcut here or not.
776 prim.ParentID = Convert.ToUInt32(row["ParentID"]);
777 prim.CreationDate = Convert.ToInt32(row["CreationDate"]);
778 prim.Name = (String) row["Name"];
779 // various text fields
780 prim.Text = (String) row["Text"];
781 prim.Description = (String) row["Description"];
782 prim.SitName = (String) row["SitName"];
783 prim.TouchName = (String) row["TouchName"];
784 // permissions
785 prim.ObjectFlags = Convert.ToUInt32(row["ObjectFlags"]);
786 prim.CreatorID = new LLUUID((String) row["CreatorID"]);
787 prim.OwnerID = new LLUUID((String) row["OwnerID"]);
788 prim.GroupID = new LLUUID((String) row["GroupID"]);
789 prim.LastOwnerID = new LLUUID((String) row["LastOwnerID"]);
790 prim.OwnerMask = Convert.ToUInt32(row["OwnerMask"]);
791 prim.NextOwnerMask = Convert.ToUInt32(row["NextOwnerMask"]);
792 prim.GroupMask = Convert.ToUInt32(row["GroupMask"]);
793 prim.EveryoneMask = Convert.ToUInt32(row["EveryoneMask"]);
794 prim.BaseMask = Convert.ToUInt32(row["BaseMask"]);
795 // vectors
796 prim.OffsetPosition = new LLVector3(
797 Convert.ToSingle(row["PositionX"]),
798 Convert.ToSingle(row["PositionY"]),
799 Convert.ToSingle(row["PositionZ"])
800 );
801 prim.GroupPosition = new LLVector3(
802 Convert.ToSingle(row["GroupPositionX"]),
803 Convert.ToSingle(row["GroupPositionY"]),
804 Convert.ToSingle(row["GroupPositionZ"])
805 );
806 prim.Velocity = new LLVector3(
807 Convert.ToSingle(row["VelocityX"]),
808 Convert.ToSingle(row["VelocityY"]),
809 Convert.ToSingle(row["VelocityZ"])
810 );
811 prim.AngularVelocity = new LLVector3(
812 Convert.ToSingle(row["AngularVelocityX"]),
813 Convert.ToSingle(row["AngularVelocityY"]),
814 Convert.ToSingle(row["AngularVelocityZ"])
815 );
816 prim.Acceleration = new LLVector3(
817 Convert.ToSingle(row["AccelerationX"]),
818 Convert.ToSingle(row["AccelerationY"]),
819 Convert.ToSingle(row["AccelerationZ"])
820 );
821 // quaternions
822 prim.RotationOffset = new LLQuaternion(
823 Convert.ToSingle(row["RotationX"]),
824 Convert.ToSingle(row["RotationY"]),
825 Convert.ToSingle(row["RotationZ"]),
826 Convert.ToSingle(row["RotationW"])
827 );
828 try
829 {
830 prim.SetSitTargetLL(new LLVector3(
831 Convert.ToSingle(row["SitTargetOffsetX"]),
832 Convert.ToSingle(row["SitTargetOffsetY"]),
833 Convert.ToSingle(row["SitTargetOffsetZ"])), new LLQuaternion(
834 Convert.ToSingle(
835 row["SitTargetOrientX"]),
836 Convert.ToSingle(
837 row["SitTargetOrientY"]),
838 Convert.ToSingle(
839 row["SitTargetOrientZ"]),
840 Convert.ToSingle(
841 row["SitTargetOrientW"])));
842 }
843 catch (InvalidCastException)
844 {
845 // Database table was created before we got here and needs to be created! :P
846
847 using (
848 MySqlCommand cmd =
849 new MySqlCommand(
850 "ALTER TABLE `prims` ADD COLUMN `SitTargetOffsetX` float NOT NULL default 0, ADD COLUMN `SitTargetOffsetY` float NOT NULL default 0, ADD COLUMN `SitTargetOffsetZ` float NOT NULL default 0, ADD COLUMN `SitTargetOrientW` float NOT NULL default 0, ADD COLUMN `SitTargetOrientX` float NOT NULL default 0, ADD COLUMN `SitTargetOrientY` float NOT NULL default 0, ADD COLUMN `SitTargetOrientZ` float NOT NULL default 0;",
851 m_connection))
852 {
853 cmd.ExecuteNonQuery();
854 }
855 }
856 return prim;
857 }
858
859
860 /// <summary>
861 /// Build a prim inventory item from the persisted data.
862 /// </summary>
863 /// <param name="row"></param>
864 /// <returns></returns>
865 private TaskInventoryItem buildItem(DataRow row)
866 {
867 TaskInventoryItem taskItem = new TaskInventoryItem();
868
869 taskItem.ItemID = new LLUUID((String)row["itemID"]);
870 taskItem.ParentPartID = new LLUUID((String)row["primID"]);
871 taskItem.AssetID = new LLUUID((String)row["assetID"]);
872 taskItem.ParentID = new LLUUID((String)row["parentFolderID"]);
873
874 taskItem.InvType = Convert.ToInt32(row["invType"]);
875 taskItem.Type = Convert.ToInt32(row["assetType"]);
876
877 taskItem.Name = (String)row["name"];
878 taskItem.Description = (String)row["description"];
879 taskItem.CreationDate = Convert.ToUInt32(row["creationDate"]);
880 taskItem.CreatorID = new LLUUID((String)row["creatorID"]);
881 taskItem.OwnerID = new LLUUID((String)row["ownerID"]);
882 taskItem.LastOwnerID = new LLUUID((String)row["lastOwnerID"]);
883 taskItem.GroupID = new LLUUID((String)row["groupID"]);
884
885 taskItem.NextOwnerMask = Convert.ToUInt32(row["nextPermissions"]);
886 taskItem.OwnerMask = Convert.ToUInt32(row["currentPermissions"]);
887 taskItem.BaseMask = Convert.ToUInt32(row["basePermissions"]);
888 taskItem.EveryoneMask = Convert.ToUInt32(row["everyonePermissions"]);
889 taskItem.GroupMask = Convert.ToUInt32(row["groupPermissions"]);
890
891 return taskItem;
892 }
893
894 private LandData buildLandData(DataRow row)
895 {
896 LandData newData = new LandData();
897
898 newData.globalID = new LLUUID((String) row["UUID"]);
899 newData.localID = Convert.ToInt32(row["LocalLandID"]);
900
901 // Bitmap is a byte[512]
902 newData.landBitmapByteArray = (Byte[]) row["Bitmap"];
903
904 newData.landName = (String) row["Name"];
905 newData.landDesc = (String) row["Description"];
906 newData.ownerID = (String) row["OwnerUUID"];
907 newData.isGroupOwned = Convert.ToBoolean(row["IsGroupOwned"]);
908 newData.area = Convert.ToInt32(row["Area"]);
909 newData.auctionID = Convert.ToUInt32(row["AuctionID"]); //Unemplemented
910 newData.category = (Parcel.ParcelCategory) Convert.ToInt32(row["Category"]);
911 //Enum libsecondlife.Parcel.ParcelCategory
912 newData.claimDate = Convert.ToInt32(row["ClaimDate"]);
913 newData.claimPrice = Convert.ToInt32(row["ClaimPrice"]);
914 newData.groupID = new LLUUID((String) row["GroupUUID"]);
915 newData.salePrice = Convert.ToInt32(row["SalePrice"]);
916 newData.landStatus = (Parcel.ParcelStatus) Convert.ToInt32(row["LandStatus"]);
917 //Enum. libsecondlife.Parcel.ParcelStatus
918 newData.landFlags = Convert.ToUInt32(row["LandFlags"]);
919 newData.landingType = Convert.ToByte(row["LandingType"]);
920 newData.mediaAutoScale = Convert.ToByte(row["MediaAutoScale"]);
921 newData.mediaID = new LLUUID((String) row["MediaTextureUUID"]);
922 newData.mediaURL = (String) row["MediaURL"];
923 newData.musicURL = (String) row["MusicURL"];
924 newData.passHours = Convert.ToSingle(row["PassHours"]);
925 newData.passPrice = Convert.ToInt32(row["PassPrice"]);
926 newData.snapshotID = (String) row["SnapshotUUID"];
927
928 newData.userLocation =
929 new LLVector3(Convert.ToSingle(row["UserLocationX"]), Convert.ToSingle(row["UserLocationY"]),
930 Convert.ToSingle(row["UserLocationZ"]));
931 newData.userLookAt =
932 new LLVector3(Convert.ToSingle(row["UserLookAtX"]), Convert.ToSingle(row["UserLookAtY"]),
933 Convert.ToSingle(row["UserLookAtZ"]));
934 newData.parcelAccessList = new List<ParcelManager.ParcelAccessEntry>();
935
936 return newData;
937 }
938
939 private ParcelManager.ParcelAccessEntry buildLandAccessData(DataRow row)
940 {
941 ParcelManager.ParcelAccessEntry entry = new ParcelManager.ParcelAccessEntry();
942 entry.AgentID = new LLUUID((string) row["AccessUUID"]);
943 entry.Flags = (ParcelManager.AccessList) Convert.ToInt32(row["Flags"]);
944 entry.Time = new DateTime();
945 return entry;
946 }
947
948 private Array serializeTerrain(double[,] val)
949 {
950 MemoryStream str = new MemoryStream(65536*sizeof (double));
951 BinaryWriter bw = new BinaryWriter(str);
952
953 // TODO: COMPATIBILITY - Add byte-order conversions
954 for (int x = 0; x < 256; x++)
955 for (int y = 0; y < 256; y++)
956 bw.Write(val[x, y]);
957
958 return str.ToArray();
959 }
960
961 private void fillPrimRow(DataRow row, SceneObjectPart prim, LLUUID sceneGroupID, LLUUID regionUUID)
962 {
963 row["UUID"] = Util.ToRawUuidString(prim.UUID);
964 row["RegionUUID"] = Util.ToRawUuidString(regionUUID);
965 row["ParentID"] = prim.ParentID;
966 row["CreationDate"] = prim.CreationDate;
967 row["Name"] = prim.Name;
968 row["SceneGroupID"] = Util.ToRawUuidString(sceneGroupID);
969 // the UUID of the root part for this SceneObjectGroup
970 // various text fields
971 row["Text"] = prim.Text;
972 row["Description"] = prim.Description;
973 row["SitName"] = prim.SitName;
974 row["TouchName"] = prim.TouchName;
975 // permissions
976 row["ObjectFlags"] = prim.ObjectFlags;
977 row["CreatorID"] = Util.ToRawUuidString(prim.CreatorID);
978 row["OwnerID"] = Util.ToRawUuidString(prim.OwnerID);
979 row["GroupID"] = Util.ToRawUuidString(prim.GroupID);
980 row["LastOwnerID"] = Util.ToRawUuidString(prim.LastOwnerID);
981 row["OwnerMask"] = prim.OwnerMask;
982 row["NextOwnerMask"] = prim.NextOwnerMask;
983 row["GroupMask"] = prim.GroupMask;
984 row["EveryoneMask"] = prim.EveryoneMask;
985 row["BaseMask"] = prim.BaseMask;
986 // vectors
987 row["PositionX"] = prim.OffsetPosition.X;
988 row["PositionY"] = prim.OffsetPosition.Y;
989 row["PositionZ"] = prim.OffsetPosition.Z;
990 row["GroupPositionX"] = prim.GroupPosition.X;
991 row["GroupPositionY"] = prim.GroupPosition.Y;
992 row["GroupPositionZ"] = prim.GroupPosition.Z;
993 row["VelocityX"] = prim.Velocity.X;
994 row["VelocityY"] = prim.Velocity.Y;
995 row["VelocityZ"] = prim.Velocity.Z;
996 row["AngularVelocityX"] = prim.AngularVelocity.X;
997 row["AngularVelocityY"] = prim.AngularVelocity.Y;
998 row["AngularVelocityZ"] = prim.AngularVelocity.Z;
999 row["AccelerationX"] = prim.Acceleration.X;
1000 row["AccelerationY"] = prim.Acceleration.Y;
1001 row["AccelerationZ"] = prim.Acceleration.Z;
1002 // quaternions
1003 row["RotationX"] = prim.RotationOffset.X;
1004 row["RotationY"] = prim.RotationOffset.Y;
1005 row["RotationZ"] = prim.RotationOffset.Z;
1006 row["RotationW"] = prim.RotationOffset.W;
1007
1008 try
1009 {
1010 // Sit target
1011 LLVector3 sitTargetPos = prim.GetSitTargetPositionLL();
1012 row["SitTargetOffsetX"] = sitTargetPos.X;
1013 row["SitTargetOffsetY"] = sitTargetPos.Y;
1014 row["SitTargetOffsetZ"] = sitTargetPos.Z;
1015
1016 LLQuaternion sitTargetOrient = prim.GetSitTargetOrientationLL();
1017 row["SitTargetOrientW"] = sitTargetOrient.W;
1018 row["SitTargetOrientX"] = sitTargetOrient.X;
1019 row["SitTargetOrientY"] = sitTargetOrient.Y;
1020 row["SitTargetOrientZ"] = sitTargetOrient.Z;
1021 }
1022 catch (MySqlException)
1023 {
1024 // Database table was created before we got here and needs to be created! :P
1025
1026 using (
1027 MySqlCommand cmd =
1028 new MySqlCommand(
1029 "ALTER TABLE `prims` ADD COLUMN `SitTargetOffsetX` float NOT NULL default 0, ADD COLUMN `SitTargetOffsetY` float NOT NULL default 0, ADD COLUMN `SitTargetOffsetZ` float NOT NULL default 0, ADD COLUMN `SitTargetOrientW` float NOT NULL default 0, ADD COLUMN `SitTargetOrientX` float NOT NULL default 0, ADD COLUMN `SitTargetOrientY` float NOT NULL default 0, ADD COLUMN `SitTargetOrientZ` float NOT NULL default 0;",
1030 m_connection))
1031 {
1032 cmd.ExecuteNonQuery();
1033 }
1034 }
1035 }
1036
1037 private void fillItemRow(DataRow row, TaskInventoryItem taskItem)
1038 {
1039 row["itemID"] = taskItem.ItemID;
1040 row["primID"] = taskItem.ParentPartID;
1041 row["assetID"] = taskItem.AssetID;
1042 row["parentFolderID"] = taskItem.ParentID;
1043
1044 row["invType"] = taskItem.InvType;
1045 row["assetType"] = taskItem.Type;
1046
1047 row["name"] = taskItem.Name;
1048 row["description"] = taskItem.Description;
1049 row["creationDate"] = taskItem.CreationDate;
1050 row["creatorID"] = taskItem.CreatorID;
1051 row["ownerID"] = taskItem.OwnerID;
1052 row["lastOwnerID"] = taskItem.LastOwnerID;
1053 row["groupID"] = taskItem.GroupID;
1054 row["nextPermissions"] = taskItem.NextOwnerMask;
1055 row["currentPermissions"] = taskItem.OwnerMask;
1056 row["basePermissions"] = taskItem.BaseMask;
1057 row["everyonePermissions"] = taskItem.EveryoneMask;
1058 row["groupPermissions"] = taskItem.GroupMask;
1059 }
1060
1061 private void fillLandRow(DataRow row, LandData land, LLUUID regionUUID)
1062 {
1063 row["UUID"] = Util.ToRawUuidString(land.globalID);
1064 row["RegionUUID"] = Util.ToRawUuidString(regionUUID);
1065 row["LocalLandID"] = land.localID;
1066
1067 // Bitmap is a byte[512]
1068 row["Bitmap"] = land.landBitmapByteArray;
1069
1070 row["Name"] = land.landName;
1071 row["Description"] = land.landDesc;
1072 row["OwnerUUID"] = Util.ToRawUuidString(land.ownerID);
1073 row["IsGroupOwned"] = land.isGroupOwned;
1074 row["Area"] = land.area;
1075 row["AuctionID"] = land.auctionID; //Unemplemented
1076 row["Category"] = land.category; //Enum libsecondlife.Parcel.ParcelCategory
1077 row["ClaimDate"] = land.claimDate;
1078 row["ClaimPrice"] = land.claimPrice;
1079 row["GroupUUID"] = Util.ToRawUuidString(land.groupID);
1080 row["SalePrice"] = land.salePrice;
1081 row["LandStatus"] = land.landStatus; //Enum. libsecondlife.Parcel.ParcelStatus
1082 row["LandFlags"] = land.landFlags;
1083 row["LandingType"] = land.landingType;
1084 row["MediaAutoScale"] = land.mediaAutoScale;
1085 row["MediaTextureUUID"] = Util.ToRawUuidString(land.mediaID);
1086 row["MediaURL"] = land.mediaURL;
1087 row["MusicURL"] = land.musicURL;
1088 row["PassHours"] = land.passHours;
1089 row["PassPrice"] = land.passPrice;
1090 row["SnapshotUUID"] = Util.ToRawUuidString(land.snapshotID);
1091 row["UserLocationX"] = land.userLocation.X;
1092 row["UserLocationY"] = land.userLocation.Y;
1093 row["UserLocationZ"] = land.userLocation.Z;
1094 row["UserLookAtX"] = land.userLookAt.X;
1095 row["UserLookAtY"] = land.userLookAt.Y;
1096 row["UserLookAtZ"] = land.userLookAt.Z;
1097 }
1098
1099 private void fillLandAccessRow(DataRow row, ParcelManager.ParcelAccessEntry entry, LLUUID parcelID)
1100 {
1101 row["LandUUID"] = Util.ToRawUuidString(parcelID);
1102 row["AccessUUID"] = Util.ToRawUuidString(entry.AgentID);
1103 row["Flags"] = entry.Flags;
1104 }
1105
1106 private PrimitiveBaseShape buildShape(DataRow row)
1107 {
1108 PrimitiveBaseShape s = new PrimitiveBaseShape();
1109 s.Scale = new LLVector3(
1110 Convert.ToSingle(row["ScaleX"]),
1111 Convert.ToSingle(row["ScaleY"]),
1112 Convert.ToSingle(row["ScaleZ"])
1113 );
1114 // paths
1115 s.PCode = Convert.ToByte(row["PCode"]);
1116 s.PathBegin = Convert.ToUInt16(row["PathBegin"]);
1117 s.PathEnd = Convert.ToUInt16(row["PathEnd"]);
1118 s.PathScaleX = Convert.ToByte(row["PathScaleX"]);
1119 s.PathScaleY = Convert.ToByte(row["PathScaleY"]);
1120 s.PathShearX = Convert.ToByte(row["PathShearX"]);
1121 s.PathShearY = Convert.ToByte(row["PathShearY"]);
1122 s.PathSkew = Convert.ToSByte(row["PathSkew"]);
1123 s.PathCurve = Convert.ToByte(row["PathCurve"]);
1124 s.PathRadiusOffset = Convert.ToSByte(row["PathRadiusOffset"]);
1125 s.PathRevolutions = Convert.ToByte(row["PathRevolutions"]);
1126 s.PathTaperX = Convert.ToSByte(row["PathTaperX"]);
1127 s.PathTaperY = Convert.ToSByte(row["PathTaperY"]);
1128 s.PathTwist = Convert.ToSByte(row["PathTwist"]);
1129 s.PathTwistBegin = Convert.ToSByte(row["PathTwistBegin"]);
1130 // profile
1131 s.ProfileBegin = Convert.ToUInt16(row["ProfileBegin"]);
1132 s.ProfileEnd = Convert.ToUInt16(row["ProfileEnd"]);
1133 s.ProfileCurve = Convert.ToByte(row["ProfileCurve"]);
1134 s.ProfileHollow = Convert.ToUInt16(row["ProfileHollow"]);
1135
1136 byte[] textureEntry = (byte[]) row["Texture"];
1137 s.TextureEntry = textureEntry;
1138
1139 s.ExtraParams = (byte[]) row["ExtraParams"];
1140
1141 try
1142 {
1143 s.State = Convert.ToByte(row["State"]);
1144 }
1145 catch (InvalidCastException)
1146 {
1147 // Database table was created before we got here and needs to be created! :P
1148
1149 using (
1150 MySqlCommand cmd =
1151 new MySqlCommand(
1152 "ALTER TABLE `primshapes` ADD COLUMN `State` int NOT NULL default 0;",
1153 m_connection))
1154 {
1155 cmd.ExecuteNonQuery();
1156 }
1157 }
1158
1159 return s;
1160 }
1161
1162 private void fillShapeRow(DataRow row, SceneObjectPart prim)
1163 {
1164 PrimitiveBaseShape s = prim.Shape;
1165 row["UUID"] = Util.ToRawUuidString(prim.UUID);
1166 // shape is an enum
1167 row["Shape"] = 0;
1168 // vectors
1169 row["ScaleX"] = s.Scale.X;
1170 row["ScaleY"] = s.Scale.Y;
1171 row["ScaleZ"] = s.Scale.Z;
1172 // paths
1173 row["PCode"] = s.PCode;
1174 row["PathBegin"] = s.PathBegin;
1175 row["PathEnd"] = s.PathEnd;
1176 row["PathScaleX"] = s.PathScaleX;
1177 row["PathScaleY"] = s.PathScaleY;
1178 row["PathShearX"] = s.PathShearX;
1179 row["PathShearY"] = s.PathShearY;
1180 row["PathSkew"] = s.PathSkew;
1181 row["PathCurve"] = s.PathCurve;
1182 row["PathRadiusOffset"] = s.PathRadiusOffset;
1183 row["PathRevolutions"] = s.PathRevolutions;
1184 row["PathTaperX"] = s.PathTaperX;
1185 row["PathTaperY"] = s.PathTaperY;
1186 row["PathTwist"] = s.PathTwist;
1187 row["PathTwistBegin"] = s.PathTwistBegin;
1188 // profile
1189 row["ProfileBegin"] = s.ProfileBegin;
1190 row["ProfileEnd"] = s.ProfileEnd;
1191 row["ProfileCurve"] = s.ProfileCurve;
1192 row["ProfileHollow"] = s.ProfileHollow;
1193 row["Texture"] = s.TextureEntry;
1194 row["ExtraParams"] = s.ExtraParams;
1195 try
1196 {
1197 row["State"] = s.State;
1198 }
1199 catch (MySqlException)
1200 {
1201 // Database table was created before we got here and needs to be created! :P
1202 using (
1203 MySqlCommand cmd =
1204 new MySqlCommand(
1205 "ALTER TABLE `primshapes` ADD COLUMN `State` int NOT NULL default 0;",
1206 m_connection))
1207 {
1208 cmd.ExecuteNonQuery();
1209 }
1210 }
1211 }
1212
1213 private void addPrim(SceneObjectPart prim, LLUUID sceneGroupID, LLUUID regionUUID)
1214 {
1215 DataTable prims = m_dataSet.Tables["prims"];
1216 DataTable shapes = m_dataSet.Tables["primshapes"];
1217
1218 DataRow primRow = prims.Rows.Find(Util.ToRawUuidString(prim.UUID));
1219 if (primRow == null)
1220 {
1221 primRow = prims.NewRow();
1222 fillPrimRow(primRow, prim, sceneGroupID, regionUUID);
1223 prims.Rows.Add(primRow);
1224 }
1225 else
1226 {
1227 fillPrimRow(primRow, prim, sceneGroupID, regionUUID);
1228 }
1229
1230 DataRow shapeRow = shapes.Rows.Find(Util.ToRawUuidString(prim.UUID));
1231 if (shapeRow == null)
1232 {
1233 shapeRow = shapes.NewRow();
1234 fillShapeRow(shapeRow, prim);
1235 shapes.Rows.Add(shapeRow);
1236 }
1237 else
1238 {
1239 fillShapeRow(shapeRow, prim);
1240 }
1241 }
1242
1243 // see IRegionDatastore
1244 public void StorePrimInventory(LLUUID primID, ICollection<TaskInventoryItem> items)
1245 {
1246 if (!persistPrimInventories)
1247 return;
1248
1249 m_log.InfoFormat("[DATASTORE]: Persisting Prim Inventory with prim ID {0}", primID);
1250
1251 // For now, we're just going to crudely remove all the previous inventory items
1252 // no matter whether they have changed or not, and replace them with the current set.
1253 lock (m_dataSet)
1254 {
1255 RemoveItems(primID);
1256
1257 // repalce with current inventory details
1258 foreach (TaskInventoryItem newItem in items)
1259 {
1260// m_log.InfoFormat(
1261// "[DATASTORE]: " +
1262// "Adding item {0}, {1} to prim ID {2}",
1263// newItem.Name, newItem.ItemID, newItem.ParentPartID);
1264
1265 DataRow newItemRow = m_itemsTable.NewRow();
1266 fillItemRow(newItemRow, newItem);
1267 m_itemsTable.Rows.Add(newItemRow);
1268 }
1269 }
1270
1271 Commit();
1272 }
1273
1274 /***********************************************************************
1275 *
1276 * SQL Statement Creation Functions
1277 *
1278 * These functions create SQL statements for update, insert, and create.
1279 * They can probably be factored later to have a db independant
1280 * portion and a db specific portion
1281 *
1282 **********************************************************************/
1283
1284 private MySqlCommand createInsertCommand(string table, DataTable dt)
1285 {
1286 /**
1287 * This is subtle enough to deserve some commentary.
1288 * Instead of doing *lots* and *lots of hardcoded strings
1289 * for database definitions we'll use the fact that
1290 * realistically all insert statements look like "insert
1291 * into A(b, c) values(:b, :c) on the parameterized query
1292 * front. If we just have a list of b, c, etc... we can
1293 * generate these strings instead of typing them out.
1294 */
1295 string[] cols = new string[dt.Columns.Count];
1296 for (int i = 0; i < dt.Columns.Count; i++)
1297 {
1298 DataColumn col = dt.Columns[i];
1299 cols[i] = col.ColumnName;
1300 }
1301
1302 string sql = "insert into " + table + "(";
1303 sql += String.Join(", ", cols);
1304 // important, the first ':' needs to be here, the rest get added in the join
1305 sql += ") values (?";
1306 sql += String.Join(", ?", cols);
1307 sql += ")";
1308 MySqlCommand cmd = new MySqlCommand(sql);
1309
1310 // this provides the binding for all our parameters, so
1311 // much less code than it used to be
1312 foreach (DataColumn col in dt.Columns)
1313 {
1314 cmd.Parameters.Add(createMySqlParameter(col.ColumnName, col.DataType));
1315 }
1316 return cmd;
1317 }
1318
1319 private MySqlCommand createUpdateCommand(string table, string pk, DataTable dt)
1320 {
1321 string sql = "update " + table + " set ";
1322 string subsql = String.Empty;
1323 foreach (DataColumn col in dt.Columns)
1324 {
1325 if (subsql.Length > 0)
1326 {
1327 // a map function would rock so much here
1328 subsql += ", ";
1329 }
1330 subsql += col.ColumnName + "=?" + col.ColumnName;
1331 }
1332 sql += subsql;
1333 sql += " where " + pk;
1334 MySqlCommand cmd = new MySqlCommand(sql);
1335
1336 // this provides the binding for all our parameters, so
1337 // much less code than it used to be
1338
1339 foreach (DataColumn col in dt.Columns)
1340 {
1341 cmd.Parameters.Add(createMySqlParameter(col.ColumnName, col.DataType));
1342 }
1343 return cmd;
1344 }
1345
1346 private string defineTable(DataTable dt)
1347 {
1348 string sql = "create table " + dt.TableName + "(";
1349 string subsql = String.Empty;
1350 foreach (DataColumn col in dt.Columns)
1351 {
1352 if (subsql.Length > 0)
1353 {
1354 // a map function would rock so much here
1355 subsql += ",\n";
1356 }
1357 subsql += col.ColumnName + " " + MySqlType(col.DataType);
1358 if (dt.PrimaryKey.Length > 0 && col == dt.PrimaryKey[0])
1359 {
1360 subsql += " primary key";
1361 }
1362 }
1363 sql += subsql;
1364 sql += ")";
1365
1366 //m_log.InfoFormat("[DATASTORE]: defineTable() sql {0}", sql);
1367
1368 return sql;
1369 }
1370
1371 /***********************************************************************
1372 *
1373 * Database Binding functions
1374 *
1375 * These will be db specific due to typing, and minor differences
1376 * in databases.
1377 *
1378 **********************************************************************/
1379
1380 ///<summary>
1381 /// This is a convenience function that collapses 5 repetitive
1382 /// lines for defining MySqlParameters to 2 parameters:
1383 /// column name and database type.
1384 ///
1385 /// It assumes certain conventions like ?param as the param
1386 /// name to replace in parametrized queries, and that source
1387 /// version is always current version, both of which are fine
1388 /// for us.
1389 ///</summary>
1390 ///<returns>a built MySql parameter</returns>
1391 private MySqlParameter createMySqlParameter(string name, Type type)
1392 {
1393 MySqlParameter param = new MySqlParameter();
1394 param.ParameterName = "?" + name;
1395 param.DbType = dbtypeFromType(type);
1396 param.SourceColumn = name;
1397 param.SourceVersion = DataRowVersion.Current;
1398 return param;
1399 }
1400
1401// TODO: unused
1402// private MySqlParameter createParamWithValue(string name, Type type, Object o)
1403// {
1404// MySqlParameter param = createMySqlParameter(name, type);
1405// param.Value = o;
1406// return param;
1407// }
1408
1409 private void SetupPrimCommands(MySqlDataAdapter da, MySqlConnection conn)
1410 {
1411 MySqlCommand insertCommand = createInsertCommand("prims", m_primTable);
1412 insertCommand.Connection = conn;
1413 da.InsertCommand = insertCommand;
1414
1415 MySqlCommand updateCommand = createUpdateCommand("prims", "UUID=?UUID", m_primTable);
1416 updateCommand.Connection = conn;
1417 da.UpdateCommand = updateCommand;
1418
1419 MySqlCommand delete = new MySqlCommand("delete from prims where UUID=?UUID");
1420 delete.Parameters.Add(createMySqlParameter("UUID", typeof (String)));
1421 delete.Connection = conn;
1422 da.DeleteCommand = delete;
1423 }
1424
1425 private void SetupItemsCommands(MySqlDataAdapter da, MySqlConnection conn)
1426 {
1427 da.InsertCommand = createInsertCommand("primitems", m_itemsTable);
1428 da.InsertCommand.Connection = conn;
1429
1430 da.UpdateCommand = createUpdateCommand("primitems", "itemID = ?itemID", m_itemsTable);
1431 da.UpdateCommand.Connection = conn;
1432
1433 MySqlCommand delete = new MySqlCommand("delete from primitems where itemID = ?itemID");
1434 delete.Parameters.Add(createMySqlParameter("itemID", typeof (String)));
1435 delete.Connection = conn;
1436 da.DeleteCommand = delete;
1437 }
1438
1439 private void SetupTerrainCommands(MySqlDataAdapter da, MySqlConnection conn)
1440 {
1441 da.InsertCommand = createInsertCommand("terrain", m_dataSet.Tables["terrain"]);
1442 da.InsertCommand.Connection = conn;
1443 }
1444
1445 private void setupLandCommands(MySqlDataAdapter da, MySqlConnection conn)
1446 {
1447 da.InsertCommand = createInsertCommand("land", m_dataSet.Tables["land"]);
1448 da.InsertCommand.Connection = conn;
1449
1450 da.UpdateCommand = createUpdateCommand("land", "UUID=?UUID", m_dataSet.Tables["land"]);
1451 da.UpdateCommand.Connection = conn;
1452 }
1453
1454 private void setupLandAccessCommands(MySqlDataAdapter da, MySqlConnection conn)
1455 {
1456 da.InsertCommand = createInsertCommand("landaccesslist", m_dataSet.Tables["landaccesslist"]);
1457 da.InsertCommand.Connection = conn;
1458 }
1459
1460 private void SetupShapeCommands(MySqlDataAdapter da, MySqlConnection conn)
1461 {
1462 da.InsertCommand = createInsertCommand("primshapes", m_dataSet.Tables["primshapes"]);
1463 da.InsertCommand.Connection = conn;
1464
1465 da.UpdateCommand = createUpdateCommand("primshapes", "UUID=?UUID", m_dataSet.Tables["primshapes"]);
1466 da.UpdateCommand.Connection = conn;
1467
1468 MySqlCommand delete = new MySqlCommand("delete from primshapes where UUID = ?UUID");
1469 delete.Parameters.Add(createMySqlParameter("UUID", typeof (String)));
1470 delete.Connection = conn;
1471 da.DeleteCommand = delete;
1472 }
1473
1474 private void InitDB(MySqlConnection conn)
1475 {
1476 string createPrims = defineTable(createPrimTable());
1477 string createShapes = defineTable(createShapeTable());
1478 string createItems = defineTable(createItemsTable());
1479 string createTerrain = defineTable(createTerrainTable());
1480 string createLand = defineTable(createLandTable());
1481 string createLandAccessList = defineTable(createLandAccessListTable());
1482
1483 MySqlCommand pcmd = new MySqlCommand(createPrims, conn);
1484 MySqlCommand scmd = new MySqlCommand(createShapes, conn);
1485 MySqlCommand icmd = new MySqlCommand(createItems, conn);
1486 MySqlCommand tcmd = new MySqlCommand(createTerrain, conn);
1487 MySqlCommand lcmd = new MySqlCommand(createLand, conn);
1488 MySqlCommand lalcmd = new MySqlCommand(createLandAccessList, conn);
1489
1490 if (conn.State != ConnectionState.Open)
1491 {
1492 try
1493 {
1494 conn.Open();
1495 }
1496 catch (Exception ex)
1497 {
1498 m_log.Error("[MySql]: Error connecting to MySQL server: " + ex.Message);
1499 m_log.Error("[MySql]: Application is terminating!");
1500 System.Threading.Thread.CurrentThread.Abort();
1501 }
1502 }
1503
1504 try
1505 {
1506 pcmd.ExecuteNonQuery();
1507 }
1508 catch (MySqlException e)
1509 {
1510 m_log.WarnFormat("[MySql]: Primitives Table Already Exists: {0}", e);
1511 }
1512
1513 try
1514 {
1515 scmd.ExecuteNonQuery();
1516 }
1517 catch (MySqlException e)
1518 {
1519 m_log.WarnFormat("[MySql]: Shapes Table Already Exists: {0}", e);
1520 }
1521
1522 try
1523 {
1524 icmd.ExecuteNonQuery();
1525 }
1526 catch (MySqlException e)
1527 {
1528 m_log.WarnFormat("[MySql]: Items Table Already Exists: {0}", e);
1529 }
1530
1531 try
1532 {
1533 tcmd.ExecuteNonQuery();
1534 }
1535 catch (MySqlException e)
1536 {
1537 m_log.WarnFormat("[MySql]: Terrain Table Already Exists: {0}", e);
1538 }
1539
1540 try
1541 {
1542 lcmd.ExecuteNonQuery();
1543 }
1544 catch (MySqlException e)
1545 {
1546 m_log.WarnFormat("[MySql]: Land Table Already Exists: {0}", e);
1547 }
1548
1549 try
1550 {
1551 lalcmd.ExecuteNonQuery();
1552 }
1553 catch (MySqlException e)
1554 {
1555 m_log.WarnFormat("[MySql]: LandAccessList Table Already Exists: {0}", e);
1556 }
1557 conn.Close();
1558 }
1559
1560 private bool TestTables(MySqlConnection conn)
1561 {
1562 MySqlCommand primSelectCmd = new MySqlCommand(m_primSelect, conn);
1563 MySqlDataAdapter pDa = new MySqlDataAdapter(primSelectCmd);
1564 MySqlCommand shapeSelectCmd = new MySqlCommand(m_shapeSelect, conn);
1565 MySqlDataAdapter sDa = new MySqlDataAdapter(shapeSelectCmd);
1566 MySqlCommand itemsSelectCmd = new MySqlCommand(m_itemsSelect, conn);
1567 MySqlDataAdapter iDa = new MySqlDataAdapter(itemsSelectCmd);
1568 MySqlCommand terrainSelectCmd = new MySqlCommand(m_terrainSelect, conn);
1569 MySqlDataAdapter tDa = new MySqlDataAdapter(terrainSelectCmd);
1570 MySqlCommand landSelectCmd = new MySqlCommand(m_landSelect, conn);
1571 MySqlDataAdapter lDa = new MySqlDataAdapter(landSelectCmd);
1572 MySqlCommand landAccessListSelectCmd = new MySqlCommand(m_landAccessListSelect, conn);
1573 MySqlDataAdapter lalDa = new MySqlDataAdapter(landAccessListSelectCmd);
1574
1575 DataSet tmpDS = new DataSet();
1576 try
1577 {
1578 pDa.Fill(tmpDS, "prims");
1579 sDa.Fill(tmpDS, "primshapes");
1580
1581 if (persistPrimInventories)
1582 iDa.Fill(tmpDS, "primitems");
1583
1584 tDa.Fill(tmpDS, "terrain");
1585 lDa.Fill(tmpDS, "land");
1586 lalDa.Fill(tmpDS, "landaccesslist");
1587 }
1588 catch (MySqlException)
1589 {
1590 m_log.Info("[DATASTORE]: MySql Database doesn't exist... creating");
1591 InitDB(conn);
1592 }
1593
1594 pDa.Fill(tmpDS, "prims");
1595 sDa.Fill(tmpDS, "primshapes");
1596
1597 if (persistPrimInventories)
1598 iDa.Fill(tmpDS, "primitems");
1599
1600 tDa.Fill(tmpDS, "terrain");
1601 lDa.Fill(tmpDS, "land");
1602 lalDa.Fill(tmpDS, "landaccesslist");
1603
1604 foreach (DataColumn col in createPrimTable().Columns)
1605 {
1606 if (!tmpDS.Tables["prims"].Columns.Contains(col.ColumnName))
1607 {
1608 m_log.Info("[DATASTORE]: Missing required column:" + col.ColumnName);
1609 return false;
1610 }
1611 }
1612
1613 foreach (DataColumn col in createShapeTable().Columns)
1614 {
1615 if (!tmpDS.Tables["primshapes"].Columns.Contains(col.ColumnName))
1616 {
1617 m_log.Info("[DATASTORE]: Missing required column:" + col.ColumnName);
1618 return false;
1619 }
1620 }
1621
1622 // XXX primitems should probably go here eventually
1623
1624 foreach (DataColumn col in createTerrainTable().Columns)
1625 {
1626 if (!tmpDS.Tables["terrain"].Columns.Contains(col.ColumnName))
1627 {
1628 m_log.Info("[DATASTORE]: Missing require column:" + col.ColumnName);
1629 return false;
1630 }
1631 }
1632
1633 foreach (DataColumn col in createLandTable().Columns)
1634 {
1635 if (!tmpDS.Tables["land"].Columns.Contains(col.ColumnName))
1636 {
1637 m_log.Info("[DATASTORE]: Missing require column:" + col.ColumnName);
1638 return false;
1639 }
1640 }
1641
1642 foreach (DataColumn col in createLandAccessListTable().Columns)
1643 {
1644 if (!tmpDS.Tables["landaccesslist"].Columns.Contains(col.ColumnName))
1645 {
1646 m_log.Info("[DATASTORE]: Missing require column:" + col.ColumnName);
1647 return false;
1648 }
1649 }
1650
1651 return true;
1652 }
1653
1654 /***********************************************************************
1655 *
1656 * Type conversion functions
1657 *
1658 **********************************************************************/
1659
1660 private DbType dbtypeFromType(Type type)
1661 {
1662 if (type == typeof (String))
1663 {
1664 return DbType.String;
1665 }
1666 else if (type == typeof (Int32))
1667 {
1668 return DbType.Int32;
1669 }
1670 else if (type == typeof (Double))
1671 {
1672 return DbType.Double;
1673 }
1674 else if (type == typeof (Byte))
1675 {
1676 return DbType.Byte;
1677 }
1678 else if (type == typeof (Double))
1679 {
1680 return DbType.Double;
1681 }
1682 else if (type == typeof (Byte[]))
1683 {
1684 return DbType.Binary;
1685 }
1686 else
1687 {
1688 return DbType.String;
1689 }
1690 }
1691
1692 // this is something we'll need to implement for each db
1693 // slightly differently.
1694 private string MySqlType(Type type)
1695 {
1696 if (type == typeof (String))
1697 {
1698 return "varchar(255)";
1699 }
1700 else if (type == typeof (Int32))
1701 {
1702 return "integer";
1703 }
1704 else if (type == typeof (Int64))
1705 {
1706 return "bigint";
1707 }
1708 else if (type == typeof (Double))
1709 {
1710 return "float";
1711 }
1712 else if (type == typeof (Byte[]))
1713 {
1714 return "longblob";
1715 }
1716 else
1717 {
1718 return "string";
1719 }
1720 }
1721 }
1722}