MSSQLDataStore.cs 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099
  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. using System;
  28. using System.Collections.Generic;
  29. using System.Data;
  30. using System.Data.SqlClient;
  31. using System.IO;
  32. using System.Reflection;
  33. using libsecondlife;
  34. using log4net;
  35. using OpenSim.Data;
  36. using OpenSim.Framework;
  37. using OpenSim.Region.Environment.Interfaces;
  38. using OpenSim.Region.Environment.Scenes;
  39. namespace OpenSim.DataStore.MSSQL
  40. {
  41. public class MSSQLDataStore : IRegionDataStore
  42. {
  43. private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  44. private const string primSelect = "select * from prims";
  45. private const string shapeSelect = "select * from primshapes";
  46. private const string terrainSelect = "select * from terrain";
  47. private DataSet ds;
  48. private SqlDataAdapter primDa;
  49. private SqlDataAdapter shapeDa;
  50. private SqlDataAdapter terrainDa;
  51. // see IRegionDataStore
  52. public void Initialise(string dbfile, bool persistPrimInventories)
  53. {
  54. IniFile GridDataMySqlFile = new IniFile("mssql_connection.ini");
  55. string settingDataSource = GridDataMySqlFile.ParseFileReadValue("data_source");
  56. string settingInitialCatalog = GridDataMySqlFile.ParseFileReadValue("initial_catalog");
  57. string settingPersistSecurityInfo = GridDataMySqlFile.ParseFileReadValue("persist_security_info");
  58. string settingUserId = GridDataMySqlFile.ParseFileReadValue("user_id");
  59. string settingPassword = GridDataMySqlFile.ParseFileReadValue("password");
  60. string connectionString = "Data Source=" + settingDataSource + ";Initial Catalog=" + settingInitialCatalog +
  61. ";Persist Security Info=" + settingPersistSecurityInfo + ";User ID=" +
  62. settingUserId + ";Password=" + settingPassword + ";";
  63. ds = new DataSet();
  64. m_log.Info("[DATASTORE]: MSSQL - connecting: " + settingInitialCatalog);
  65. SqlConnection conn = new SqlConnection(connectionString);
  66. SqlCommand primSelectCmd = new SqlCommand(primSelect, conn);
  67. primDa = new SqlDataAdapter(primSelectCmd);
  68. // SqlCommandBuilder primCb = new SqlCommandBuilder(primDa);
  69. SqlCommand shapeSelectCmd = new SqlCommand(shapeSelect, conn);
  70. shapeDa = new SqlDataAdapter(shapeSelectCmd);
  71. // SqlCommandBuilder shapeCb = new SqlCommandBuilder(shapeDa);
  72. SqlCommand terrainSelectCmd = new SqlCommand(terrainSelect, conn);
  73. terrainDa = new SqlDataAdapter(terrainSelectCmd);
  74. // We fill the data set, now we've got copies in memory for the information
  75. // TODO: see if the linkage actually holds.
  76. // primDa.FillSchema(ds, SchemaType.Source, "PrimSchema");
  77. TestTables(conn);
  78. lock (ds)
  79. {
  80. ds.Tables.Add(createPrimTable());
  81. setupPrimCommands(primDa, conn);
  82. primDa.Fill(ds.Tables["prims"]);
  83. ds.Tables.Add(createShapeTable());
  84. setupShapeCommands(shapeDa, conn);
  85. ds.Tables.Add(createTerrainTable());
  86. setupTerrainCommands(terrainDa, conn);
  87. // WORKAROUND: This is a work around for Sql on
  88. // windows, which gets really unhappy with blob columns
  89. // that have no sample data in them. At some point we
  90. // need to actually find a proper way to handle this.
  91. try
  92. {
  93. shapeDa.Fill(ds.Tables["primshapes"]);
  94. }
  95. catch (Exception)
  96. {
  97. m_log.Info("[DATASTORE]: Caught fill error on primshapes table");
  98. }
  99. try
  100. {
  101. terrainDa.Fill(ds.Tables["terrain"]);
  102. }
  103. catch (Exception)
  104. {
  105. m_log.Info("[DATASTORE]: Caught fill error on terrain table");
  106. }
  107. return;
  108. }
  109. }
  110. public void StoreObject(SceneObjectGroup obj, LLUUID regionUUID)
  111. {
  112. lock (ds)
  113. {
  114. foreach (SceneObjectPart prim in obj.Children.Values)
  115. {
  116. m_log.Info("[DATASTORE]: Adding obj: " + obj.UUID + " to region: " + regionUUID);
  117. addPrim(prim, obj.UUID, regionUUID);
  118. }
  119. }
  120. Commit();
  121. // m_log.Info("Dump of prims:", ds.GetXml());
  122. }
  123. public void RemoveObject(LLUUID obj, LLUUID regionUUID)
  124. {
  125. m_log.InfoFormat("[DATASTORE]: Removing obj: {0} from region: {1}", obj.UUID, regionUUID);
  126. DataTable prims = ds.Tables["prims"];
  127. DataTable shapes = ds.Tables["primshapes"];
  128. string selectExp = "SceneGroupID = '" + obj.ToString() + "'";
  129. lock (ds)
  130. {
  131. DataRow[] primRows = prims.Select(selectExp);
  132. foreach (DataRow row in primRows)
  133. {
  134. LLUUID uuid = new LLUUID((string) row["UUID"]);
  135. DataRow shapeRow = shapes.Rows.Find(uuid);
  136. if (shapeRow != null)
  137. {
  138. shapeRow.Delete();
  139. }
  140. row.Delete();
  141. }
  142. }
  143. Commit();
  144. }
  145. public List<SceneObjectGroup> LoadObjects(LLUUID regionUUID)
  146. {
  147. Dictionary<LLUUID, SceneObjectGroup> createdObjects = new Dictionary<LLUUID, SceneObjectGroup>();
  148. List<SceneObjectGroup> retvals = new List<SceneObjectGroup>();
  149. DataTable prims = ds.Tables["prims"];
  150. DataTable shapes = ds.Tables["primshapes"];
  151. string byRegion = "RegionUUID = '" + regionUUID.ToString() + "'";
  152. string orderByParent = "ParentID ASC";
  153. lock (ds)
  154. {
  155. DataRow[] primsForRegion = prims.Select(byRegion, orderByParent);
  156. m_log.Info("[DATASTORE]: " +
  157. "Loaded " + primsForRegion.Length + " prims for region: " + regionUUID);
  158. foreach (DataRow primRow in primsForRegion)
  159. {
  160. try
  161. {
  162. string uuid = (string) primRow["UUID"];
  163. string objID = (string) primRow["SceneGroupID"];
  164. if (uuid == objID) //is new SceneObjectGroup ?
  165. {
  166. SceneObjectGroup group = new SceneObjectGroup();
  167. SceneObjectPart prim = buildPrim(primRow);
  168. DataRow shapeRow = shapes.Rows.Find(prim.UUID);
  169. if (shapeRow != null)
  170. {
  171. prim.Shape = buildShape(shapeRow);
  172. }
  173. else
  174. {
  175. m_log.Info(
  176. "No shape found for prim in storage, so setting default box shape");
  177. prim.Shape = PrimitiveBaseShape.Default;
  178. }
  179. group.AddPart(prim);
  180. group.RootPart = prim;
  181. createdObjects.Add(group.UUID, group);
  182. retvals.Add(group);
  183. }
  184. else
  185. {
  186. SceneObjectPart prim = buildPrim(primRow);
  187. DataRow shapeRow = shapes.Rows.Find(prim.UUID);
  188. if (shapeRow != null)
  189. {
  190. prim.Shape = buildShape(shapeRow);
  191. }
  192. else
  193. {
  194. m_log.Info(
  195. "No shape found for prim in storage, so setting default box shape");
  196. prim.Shape = PrimitiveBaseShape.Default;
  197. }
  198. createdObjects[new LLUUID(objID)].AddPart(prim);
  199. }
  200. }
  201. catch (Exception e)
  202. {
  203. m_log.Error("[DATASTORE]: Failed create prim object, exception and data follows");
  204. m_log.Info("[DATASTORE]: " + e.ToString());
  205. foreach (DataColumn col in prims.Columns)
  206. {
  207. m_log.Info("[DATASTORE]: Col: " + col.ColumnName + " => " + primRow[col]);
  208. }
  209. }
  210. }
  211. }
  212. return retvals;
  213. }
  214. public void StoreTerrain(double[,] ter, LLUUID regionID)
  215. {
  216. int revision = Util.UnixTimeSinceEpoch();
  217. m_log.Info("[DATASTORE]: Storing terrain revision r" + revision.ToString());
  218. DataTable terrain = ds.Tables["terrain"];
  219. lock (ds)
  220. {
  221. DataRow newrow = terrain.NewRow();
  222. fillTerrainRow(newrow, regionID, revision, ter);
  223. terrain.Rows.Add(newrow);
  224. Commit();
  225. }
  226. }
  227. public double[,] LoadTerrain(LLUUID regionID)
  228. {
  229. double[,] terret = new double[256,256];
  230. terret.Initialize();
  231. DataTable terrain = ds.Tables["terrain"];
  232. lock (ds)
  233. {
  234. DataRow[] rows = terrain.Select("RegionUUID = '" + regionID.ToString() + "'", "Revision DESC");
  235. int rev = 0;
  236. if (rows.Length > 0)
  237. {
  238. DataRow row = rows[0];
  239. byte[] heightmap = (byte[]) row["Heightfield"];
  240. for (int x = 0; x < 256; x++)
  241. {
  242. for (int y = 0; y < 256; y++)
  243. {
  244. terret[x, y] = BitConverter.ToDouble(heightmap, ((x*256) + y)*8);
  245. }
  246. }
  247. rev = (int) row["Revision"];
  248. }
  249. else
  250. {
  251. m_log.Info("[DATASTORE]: No terrain found for region");
  252. return null;
  253. }
  254. m_log.Info("[DATASTORE]: Loaded terrain revision r" + rev.ToString());
  255. }
  256. return terret;
  257. }
  258. public void RemoveLandObject(LLUUID globalID)
  259. {
  260. }
  261. public void StoreLandObject(ILandObject parcel)
  262. {
  263. }
  264. public List<LandData> LoadLandObjects(LLUUID regionUUID)
  265. {
  266. return new List<LandData>();
  267. }
  268. public void Commit()
  269. {
  270. lock (ds)
  271. {
  272. primDa.Update(ds, "prims");
  273. shapeDa.Update(ds, "primshapes");
  274. terrainDa.Update(ds, "terrain");
  275. ds.AcceptChanges();
  276. }
  277. }
  278. public void Shutdown()
  279. {
  280. Commit();
  281. }
  282. /***********************************************************************
  283. *
  284. * Database Definition Functions
  285. *
  286. * This should be db agnostic as we define them in ADO.NET terms
  287. *
  288. **********************************************************************/
  289. private static void createCol(DataTable dt, string name, Type type)
  290. {
  291. DataColumn col = new DataColumn(name, type);
  292. dt.Columns.Add(col);
  293. }
  294. private static DataTable createTerrainTable()
  295. {
  296. DataTable terrain = new DataTable("terrain");
  297. createCol(terrain, "RegionUUID", typeof (String));
  298. createCol(terrain, "Revision", typeof (Int32));
  299. createCol(terrain, "Heightfield", typeof (Byte[]));
  300. return terrain;
  301. }
  302. private static DataTable createPrimTable()
  303. {
  304. DataTable prims = new DataTable("prims");
  305. createCol(prims, "UUID", typeof (String));
  306. createCol(prims, "RegionUUID", typeof (String));
  307. createCol(prims, "ParentID", typeof (Int32));
  308. createCol(prims, "CreationDate", typeof (Int32));
  309. createCol(prims, "Name", typeof (String));
  310. createCol(prims, "SceneGroupID", typeof (String));
  311. // various text fields
  312. createCol(prims, "Text", typeof (String));
  313. createCol(prims, "Description", typeof (String));
  314. createCol(prims, "SitName", typeof (String));
  315. createCol(prims, "TouchName", typeof (String));
  316. // permissions
  317. createCol(prims, "ObjectFlags", typeof (Int32));
  318. createCol(prims, "CreatorID", typeof (String));
  319. createCol(prims, "OwnerID", typeof (String));
  320. createCol(prims, "GroupID", typeof (String));
  321. createCol(prims, "LastOwnerID", typeof (String));
  322. createCol(prims, "OwnerMask", typeof (Int32));
  323. createCol(prims, "NextOwnerMask", typeof (Int32));
  324. createCol(prims, "GroupMask", typeof (Int32));
  325. createCol(prims, "EveryoneMask", typeof (Int32));
  326. createCol(prims, "BaseMask", typeof (Int32));
  327. // vectors
  328. createCol(prims, "PositionX", typeof (Double));
  329. createCol(prims, "PositionY", typeof (Double));
  330. createCol(prims, "PositionZ", typeof (Double));
  331. createCol(prims, "GroupPositionX", typeof (Double));
  332. createCol(prims, "GroupPositionY", typeof (Double));
  333. createCol(prims, "GroupPositionZ", typeof (Double));
  334. createCol(prims, "VelocityX", typeof (Double));
  335. createCol(prims, "VelocityY", typeof (Double));
  336. createCol(prims, "VelocityZ", typeof (Double));
  337. createCol(prims, "AngularVelocityX", typeof (Double));
  338. createCol(prims, "AngularVelocityY", typeof (Double));
  339. createCol(prims, "AngularVelocityZ", typeof (Double));
  340. createCol(prims, "AccelerationX", typeof (Double));
  341. createCol(prims, "AccelerationY", typeof (Double));
  342. createCol(prims, "AccelerationZ", typeof (Double));
  343. // quaternions
  344. createCol(prims, "RotationX", typeof (Double));
  345. createCol(prims, "RotationY", typeof (Double));
  346. createCol(prims, "RotationZ", typeof (Double));
  347. createCol(prims, "RotationW", typeof (Double));
  348. // sit target
  349. createCol(prims, "SitTargetOffsetX", typeof (Double));
  350. createCol(prims, "SitTargetOffsetY", typeof (Double));
  351. createCol(prims, "SitTargetOffsetZ", typeof (Double));
  352. createCol(prims, "SitTargetOrientW", typeof (Double));
  353. createCol(prims, "SitTargetOrientX", typeof (Double));
  354. createCol(prims, "SitTargetOrientY", typeof (Double));
  355. createCol(prims, "SitTargetOrientZ", typeof (Double));
  356. // Add in contraints
  357. prims.PrimaryKey = new DataColumn[] {prims.Columns["UUID"]};
  358. return prims;
  359. }
  360. private static DataTable createShapeTable()
  361. {
  362. DataTable shapes = new DataTable("primshapes");
  363. createCol(shapes, "UUID", typeof (String));
  364. // shape is an enum
  365. createCol(shapes, "Shape", typeof (Int32));
  366. // vectors
  367. createCol(shapes, "ScaleX", typeof (Double));
  368. createCol(shapes, "ScaleY", typeof (Double));
  369. createCol(shapes, "ScaleZ", typeof (Double));
  370. // paths
  371. createCol(shapes, "PCode", typeof (Int32));
  372. createCol(shapes, "PathBegin", typeof (Int32));
  373. createCol(shapes, "PathEnd", typeof (Int32));
  374. createCol(shapes, "PathScaleX", typeof (Int32));
  375. createCol(shapes, "PathScaleY", typeof (Int32));
  376. createCol(shapes, "PathShearX", typeof (Int32));
  377. createCol(shapes, "PathShearY", typeof (Int32));
  378. createCol(shapes, "PathSkew", typeof (Int32));
  379. createCol(shapes, "PathCurve", typeof (Int32));
  380. createCol(shapes, "PathRadiusOffset", typeof (Int32));
  381. createCol(shapes, "PathRevolutions", typeof (Int32));
  382. createCol(shapes, "PathTaperX", typeof (Int32));
  383. createCol(shapes, "PathTaperY", typeof (Int32));
  384. createCol(shapes, "PathTwist", typeof (Int32));
  385. createCol(shapes, "PathTwistBegin", typeof (Int32));
  386. // profile
  387. createCol(shapes, "ProfileBegin", typeof (Int32));
  388. createCol(shapes, "ProfileEnd", typeof (Int32));
  389. createCol(shapes, "ProfileCurve", typeof (Int32));
  390. createCol(shapes, "ProfileHollow", typeof (Int32));
  391. // text TODO: this isn't right, but I'm not sure the right
  392. // way to specify this as a blob atm
  393. createCol(shapes, "Texture", typeof (Byte[]));
  394. createCol(shapes, "ExtraParams", typeof (Byte[]));
  395. shapes.PrimaryKey = new DataColumn[] {shapes.Columns["UUID"]};
  396. return shapes;
  397. }
  398. /***********************************************************************
  399. *
  400. * Convert between ADO.NET <=> OpenSim Objects
  401. *
  402. * These should be database independant
  403. *
  404. **********************************************************************/
  405. private static SceneObjectPart buildPrim(DataRow row)
  406. {
  407. // TODO: this doesn't work yet because something more
  408. // interesting has to be done to actually get these values
  409. // back out. Not enough time to figure it out yet.
  410. SceneObjectPart prim = new SceneObjectPart();
  411. prim.UUID = new LLUUID((String) row["UUID"]);
  412. // explicit conversion of integers is required, which sort
  413. // of sucks. No idea if there is a shortcut here or not.
  414. prim.ParentID = Convert.ToUInt32(row["ParentID"]);
  415. prim.CreationDate = Convert.ToInt32(row["CreationDate"]);
  416. prim.Name = (String) row["Name"];
  417. // various text fields
  418. prim.Text = (String) row["Text"];
  419. prim.Description = (String) row["Description"];
  420. prim.SitName = (String) row["SitName"];
  421. prim.TouchName = (String) row["TouchName"];
  422. // permissions
  423. prim.ObjectFlags = Convert.ToUInt32(row["ObjectFlags"]);
  424. prim.CreatorID = new LLUUID((String) row["CreatorID"]);
  425. prim.OwnerID = new LLUUID((String) row["OwnerID"]);
  426. prim.GroupID = new LLUUID((String) row["GroupID"]);
  427. prim.LastOwnerID = new LLUUID((String) row["LastOwnerID"]);
  428. prim.OwnerMask = Convert.ToUInt32(row["OwnerMask"]);
  429. prim.NextOwnerMask = Convert.ToUInt32(row["NextOwnerMask"]);
  430. prim.GroupMask = Convert.ToUInt32(row["GroupMask"]);
  431. prim.EveryoneMask = Convert.ToUInt32(row["EveryoneMask"]);
  432. prim.BaseMask = Convert.ToUInt32(row["BaseMask"]);
  433. // vectors
  434. prim.OffsetPosition = new LLVector3(
  435. Convert.ToSingle(row["PositionX"]),
  436. Convert.ToSingle(row["PositionY"]),
  437. Convert.ToSingle(row["PositionZ"])
  438. );
  439. prim.GroupPosition = new LLVector3(
  440. Convert.ToSingle(row["GroupPositionX"]),
  441. Convert.ToSingle(row["GroupPositionY"]),
  442. Convert.ToSingle(row["GroupPositionZ"])
  443. );
  444. prim.Velocity = new LLVector3(
  445. Convert.ToSingle(row["VelocityX"]),
  446. Convert.ToSingle(row["VelocityY"]),
  447. Convert.ToSingle(row["VelocityZ"])
  448. );
  449. prim.AngularVelocity = new LLVector3(
  450. Convert.ToSingle(row["AngularVelocityX"]),
  451. Convert.ToSingle(row["AngularVelocityY"]),
  452. Convert.ToSingle(row["AngularVelocityZ"])
  453. );
  454. prim.Acceleration = new LLVector3(
  455. Convert.ToSingle(row["AccelerationX"]),
  456. Convert.ToSingle(row["AccelerationY"]),
  457. Convert.ToSingle(row["AccelerationZ"])
  458. );
  459. // quaternions
  460. prim.RotationOffset = new LLQuaternion(
  461. Convert.ToSingle(row["RotationX"]),
  462. Convert.ToSingle(row["RotationY"]),
  463. Convert.ToSingle(row["RotationZ"]),
  464. Convert.ToSingle(row["RotationW"])
  465. );
  466. try
  467. {
  468. prim.SetSitTargetLL(new LLVector3(
  469. Convert.ToSingle(row["SitTargetOffsetX"]),
  470. Convert.ToSingle(row["SitTargetOffsetY"]),
  471. Convert.ToSingle(row["SitTargetOffsetZ"])), new LLQuaternion(
  472. Convert.ToSingle(
  473. row["SitTargetOrientX"]),
  474. Convert.ToSingle(
  475. row["SitTargetOrientY"]),
  476. Convert.ToSingle(
  477. row["SitTargetOrientZ"]),
  478. Convert.ToSingle(
  479. row["SitTargetOrientW"])));
  480. }
  481. catch (InvalidCastException)
  482. {
  483. // Database table was created before we got here and now has null values :P
  484. }
  485. return prim;
  486. }
  487. private static void fillPrimRow(DataRow row, SceneObjectPart prim, LLUUID sceneGroupID, LLUUID regionUUID)
  488. {
  489. row["UUID"] = prim.UUID;
  490. row["RegionUUID"] = regionUUID;
  491. row["ParentID"] = prim.ParentID;
  492. row["CreationDate"] = prim.CreationDate;
  493. row["Name"] = prim.Name;
  494. row["SceneGroupID"] = sceneGroupID; // the UUID of the root part for this SceneObjectGroup
  495. // various text fields
  496. row["Text"] = prim.Text;
  497. row["Description"] = prim.Description;
  498. row["SitName"] = prim.SitName;
  499. row["TouchName"] = prim.TouchName;
  500. // permissions
  501. row["ObjectFlags"] = prim.ObjectFlags;
  502. row["CreatorID"] = prim.CreatorID;
  503. row["OwnerID"] = prim.OwnerID;
  504. row["GroupID"] = prim.GroupID;
  505. row["LastOwnerID"] = prim.LastOwnerID;
  506. row["OwnerMask"] = prim.OwnerMask;
  507. row["NextOwnerMask"] = prim.NextOwnerMask;
  508. row["GroupMask"] = prim.GroupMask;
  509. row["EveryoneMask"] = prim.EveryoneMask;
  510. row["BaseMask"] = prim.BaseMask;
  511. // vectors
  512. row["PositionX"] = prim.OffsetPosition.X;
  513. row["PositionY"] = prim.OffsetPosition.Y;
  514. row["PositionZ"] = prim.OffsetPosition.Z;
  515. row["GroupPositionX"] = prim.GroupPosition.X;
  516. row["GroupPositionY"] = prim.GroupPosition.Y;
  517. row["GroupPositionZ"] = prim.GroupPosition.Z;
  518. row["VelocityX"] = prim.Velocity.X;
  519. row["VelocityY"] = prim.Velocity.Y;
  520. row["VelocityZ"] = prim.Velocity.Z;
  521. row["AngularVelocityX"] = prim.AngularVelocity.X;
  522. row["AngularVelocityY"] = prim.AngularVelocity.Y;
  523. row["AngularVelocityZ"] = prim.AngularVelocity.Z;
  524. row["AccelerationX"] = prim.Acceleration.X;
  525. row["AccelerationY"] = prim.Acceleration.Y;
  526. row["AccelerationZ"] = prim.Acceleration.Z;
  527. // quaternions
  528. row["RotationX"] = prim.RotationOffset.X;
  529. row["RotationY"] = prim.RotationOffset.Y;
  530. row["RotationZ"] = prim.RotationOffset.Z;
  531. row["RotationW"] = prim.RotationOffset.W;
  532. try
  533. {
  534. // Sit target
  535. LLVector3 sitTargetPos = prim.GetSitTargetPositionLL();
  536. row["SitTargetOffsetX"] = sitTargetPos.X;
  537. row["SitTargetOffsetY"] = sitTargetPos.Y;
  538. row["SitTargetOffsetZ"] = sitTargetPos.Z;
  539. LLQuaternion sitTargetOrient = prim.GetSitTargetOrientationLL();
  540. row["SitTargetOrientW"] = sitTargetOrient.W;
  541. row["SitTargetOrientX"] = sitTargetOrient.X;
  542. row["SitTargetOrientY"] = sitTargetOrient.Y;
  543. row["SitTargetOrientZ"] = sitTargetOrient.Z;
  544. }
  545. catch (Exception)
  546. {
  547. // TODO: Add Sit Target Rows!
  548. }
  549. }
  550. private static PrimitiveBaseShape buildShape(DataRow row)
  551. {
  552. PrimitiveBaseShape s = new PrimitiveBaseShape();
  553. s.Scale = new LLVector3(
  554. Convert.ToSingle(row["ScaleX"]),
  555. Convert.ToSingle(row["ScaleY"]),
  556. Convert.ToSingle(row["ScaleZ"])
  557. );
  558. // paths
  559. s.PCode = Convert.ToByte(row["PCode"]);
  560. s.PathBegin = Convert.ToUInt16(row["PathBegin"]);
  561. s.PathEnd = Convert.ToUInt16(row["PathEnd"]);
  562. s.PathScaleX = Convert.ToByte(row["PathScaleX"]);
  563. s.PathScaleY = Convert.ToByte(row["PathScaleY"]);
  564. s.PathShearX = Convert.ToByte(row["PathShearX"]);
  565. s.PathShearY = Convert.ToByte(row["PathShearY"]);
  566. s.PathSkew = Convert.ToSByte(row["PathSkew"]);
  567. s.PathCurve = Convert.ToByte(row["PathCurve"]);
  568. s.PathRadiusOffset = Convert.ToSByte(row["PathRadiusOffset"]);
  569. s.PathRevolutions = Convert.ToByte(row["PathRevolutions"]);
  570. s.PathTaperX = Convert.ToSByte(row["PathTaperX"]);
  571. s.PathTaperY = Convert.ToSByte(row["PathTaperY"]);
  572. s.PathTwist = Convert.ToSByte(row["PathTwist"]);
  573. s.PathTwistBegin = Convert.ToSByte(row["PathTwistBegin"]);
  574. // profile
  575. s.ProfileBegin = Convert.ToUInt16(row["ProfileBegin"]);
  576. s.ProfileEnd = Convert.ToUInt16(row["ProfileEnd"]);
  577. s.ProfileCurve = Convert.ToByte(row["ProfileCurve"]);
  578. s.ProfileHollow = Convert.ToUInt16(row["ProfileHollow"]);
  579. // text TODO: this isn't right] = but I'm not sure the right
  580. // way to specify this as a blob atm
  581. byte[] textureEntry = (byte[]) row["Texture"];
  582. s.TextureEntry = textureEntry;
  583. s.ExtraParams = (byte[]) row["ExtraParams"];
  584. // System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
  585. // string texture = encoding.GetString((Byte[])row["Texture"]);
  586. // if (!texture.StartsWith("<"))
  587. // {
  588. // //here so that we can still work with old format database files (ie from before I added xml serialization)
  589. // LLObject.TextureEntry textureEntry = null;
  590. // textureEntry = new LLObject.TextureEntry(new LLUUID(texture));
  591. // s.TextureEntry = textureEntry.ToBytes();
  592. // }
  593. // else
  594. // {
  595. // TextureBlock textureEntry = TextureBlock.FromXmlString(texture);
  596. // s.TextureEntry = textureEntry.TextureData;
  597. // s.ExtraParams = textureEntry.ExtraParams;
  598. // }
  599. return s;
  600. }
  601. private static void fillShapeRow(DataRow row, SceneObjectPart prim)
  602. {
  603. PrimitiveBaseShape s = prim.Shape;
  604. row["UUID"] = prim.UUID;
  605. // shape is an enum
  606. row["Shape"] = 0;
  607. // vectors
  608. row["ScaleX"] = s.Scale.X;
  609. row["ScaleY"] = s.Scale.Y;
  610. row["ScaleZ"] = s.Scale.Z;
  611. // paths
  612. row["PCode"] = s.PCode;
  613. row["PathBegin"] = s.PathBegin;
  614. row["PathEnd"] = s.PathEnd;
  615. row["PathScaleX"] = s.PathScaleX;
  616. row["PathScaleY"] = s.PathScaleY;
  617. row["PathShearX"] = s.PathShearX;
  618. row["PathShearY"] = s.PathShearY;
  619. row["PathSkew"] = s.PathSkew;
  620. row["PathCurve"] = s.PathCurve;
  621. row["PathRadiusOffset"] = s.PathRadiusOffset;
  622. row["PathRevolutions"] = s.PathRevolutions;
  623. row["PathTaperX"] = s.PathTaperX;
  624. row["PathTaperY"] = s.PathTaperY;
  625. row["PathTwist"] = s.PathTwist;
  626. row["PathTwistBegin"] = s.PathTwistBegin;
  627. // profile
  628. row["ProfileBegin"] = s.ProfileBegin;
  629. row["ProfileEnd"] = s.ProfileEnd;
  630. row["ProfileCurve"] = s.ProfileCurve;
  631. row["ProfileHollow"] = s.ProfileHollow;
  632. // text TODO: this isn't right] = but I'm not sure the right
  633. // way to specify this as a blob atm
  634. // And I couldn't work out how to save binary data either
  635. // seems that the texture colum is being treated as a string in the Datarow
  636. // if you do a .getType() on it, it returns string, while the other columns return correct type
  637. // MW[10-08-07]
  638. // Added following xml hack but not really ideal , also ExtraParams isn't currently part of the database
  639. // am a bit worried about adding it now as some people will have old format databases, so for now including that data in this xml data
  640. // MW[17-08-07]
  641. row["Texture"] = s.TextureEntry;
  642. row["ExtraParams"] = s.ExtraParams;
  643. // TextureBlock textureBlock = new TextureBlock(s.TextureEntry);
  644. // textureBlock.ExtraParams = s.ExtraParams;
  645. // System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
  646. // row["Texture"] = encoding.GetBytes(textureBlock.ToXMLString());
  647. }
  648. private void addPrim(SceneObjectPart prim, LLUUID sceneGroupID, LLUUID regionUUID)
  649. {
  650. DataTable prims = ds.Tables["prims"];
  651. DataTable shapes = ds.Tables["primshapes"];
  652. DataRow primRow = prims.Rows.Find(prim.UUID);
  653. if (primRow == null)
  654. {
  655. primRow = prims.NewRow();
  656. fillPrimRow(primRow, prim, sceneGroupID, regionUUID);
  657. prims.Rows.Add(primRow);
  658. }
  659. else
  660. {
  661. fillPrimRow(primRow, prim, sceneGroupID, regionUUID);
  662. }
  663. DataRow shapeRow = shapes.Rows.Find(prim.UUID);
  664. if (shapeRow == null)
  665. {
  666. shapeRow = shapes.NewRow();
  667. fillShapeRow(shapeRow, prim);
  668. shapes.Rows.Add(shapeRow);
  669. }
  670. else
  671. {
  672. fillShapeRow(shapeRow, prim);
  673. }
  674. }
  675. // see IRegionDatastore
  676. public void StorePrimInventory(LLUUID primID, ICollection<TaskInventoryItem> items)
  677. {
  678. // No implementation yet
  679. }
  680. /***********************************************************************
  681. *
  682. * SQL Statement Creation Functions
  683. *
  684. * These functions create SQL statements for update, insert, and create.
  685. * They can probably be factored later to have a db independant
  686. * portion and a db specific portion
  687. *
  688. **********************************************************************/
  689. private static SqlCommand createInsertCommand(string table, DataTable dt)
  690. {
  691. /**
  692. * This is subtle enough to deserve some commentary.
  693. * Instead of doing *lots* and *lots of hardcoded strings
  694. * for database definitions we'll use the fact that
  695. * realistically all insert statements look like "insert
  696. * into A(b, c) values(:b, :c) on the parameterized query
  697. * front. If we just have a list of b, c, etc... we can
  698. * generate these strings instead of typing them out.
  699. */
  700. string[] cols = new string[dt.Columns.Count];
  701. for (int i = 0; i < dt.Columns.Count; i++)
  702. {
  703. DataColumn col = dt.Columns[i];
  704. cols[i] = col.ColumnName;
  705. }
  706. string sql = "insert into " + table + "(";
  707. sql += String.Join(", ", cols);
  708. // important, the first ':' needs to be here, the rest get added in the join
  709. sql += ") values (@";
  710. sql += String.Join(", @", cols);
  711. sql += ")";
  712. SqlCommand cmd = new SqlCommand(sql);
  713. // this provides the binding for all our parameters, so
  714. // much less code than it used to be
  715. foreach (DataColumn col in dt.Columns)
  716. {
  717. cmd.Parameters.Add(createSqlParameter(col.ColumnName, col.DataType));
  718. }
  719. return cmd;
  720. }
  721. private static SqlCommand createUpdateCommand(string table, string pk, DataTable dt)
  722. {
  723. string sql = "update " + table + " set ";
  724. string subsql = String.Empty;
  725. foreach (DataColumn col in dt.Columns)
  726. {
  727. if (subsql.Length > 0)
  728. {
  729. // a map function would rock so much here
  730. subsql += ", ";
  731. }
  732. subsql += col.ColumnName + "= @" + col.ColumnName;
  733. }
  734. sql += subsql;
  735. sql += " where " + pk;
  736. SqlCommand cmd = new SqlCommand(sql);
  737. // this provides the binding for all our parameters, so
  738. // much less code than it used to be
  739. foreach (DataColumn col in dt.Columns)
  740. {
  741. cmd.Parameters.Add(createSqlParameter(col.ColumnName, col.DataType));
  742. }
  743. return cmd;
  744. }
  745. private static string defineTable(DataTable dt)
  746. {
  747. string sql = "create table " + dt.TableName + "(";
  748. string subsql = String.Empty;
  749. foreach (DataColumn col in dt.Columns)
  750. {
  751. if (subsql.Length > 0)
  752. {
  753. // a map function would rock so much here
  754. subsql += ",\n";
  755. }
  756. subsql += col.ColumnName + " " + SqlType(col.DataType);
  757. if (dt.PrimaryKey.Length > 0 && col == dt.PrimaryKey[0])
  758. {
  759. subsql += " primary key";
  760. }
  761. }
  762. sql += subsql;
  763. sql += ")";
  764. return sql;
  765. }
  766. private static void fillTerrainRow(DataRow row, LLUUID regionUUID, int rev, double[,] val)
  767. {
  768. row["RegionUUID"] = regionUUID;
  769. row["Revision"] = rev;
  770. MemoryStream str = new MemoryStream(65536*sizeof (double));
  771. BinaryWriter bw = new BinaryWriter(str);
  772. // TODO: COMPATIBILITY - Add byte-order conversions
  773. for (int x = 0; x < 256; x++)
  774. for (int y = 0; y < 256; y++)
  775. bw.Write(val[x, y]);
  776. row["Heightfield"] = str.ToArray();
  777. }
  778. /***********************************************************************
  779. *
  780. * Database Binding functions
  781. *
  782. * These will be db specific due to typing, and minor differences
  783. * in databases.
  784. *
  785. **********************************************************************/
  786. ///<summary>
  787. /// This is a convenience function that collapses 5 repetitive
  788. /// lines for defining SqlParameters to 2 parameters:
  789. /// column name and database type.
  790. ///
  791. /// It assumes certain conventions like :param as the param
  792. /// name to replace in parametrized queries, and that source
  793. /// version is always current version, both of which are fine
  794. /// for us.
  795. ///</summary>
  796. ///<returns>a built Sql parameter</returns>
  797. private static SqlParameter createSqlParameter(string name, Type type)
  798. {
  799. SqlParameter param = new SqlParameter();
  800. param.ParameterName = "@" + name;
  801. param.DbType = dbtypeFromType(type);
  802. param.SourceColumn = name;
  803. param.SourceVersion = DataRowVersion.Current;
  804. return param;
  805. }
  806. private void setupPrimCommands(SqlDataAdapter da, SqlConnection conn)
  807. {
  808. da.InsertCommand = createInsertCommand("prims", ds.Tables["prims"]);
  809. da.InsertCommand.Connection = conn;
  810. da.UpdateCommand = createUpdateCommand("prims", "UUID=@UUID", ds.Tables["prims"]);
  811. da.UpdateCommand.Connection = conn;
  812. SqlCommand delete = new SqlCommand("delete from prims where UUID = @UUID");
  813. delete.Parameters.Add(createSqlParameter("UUID", typeof (String)));
  814. delete.Connection = conn;
  815. da.DeleteCommand = delete;
  816. }
  817. private void setupShapeCommands(SqlDataAdapter da, SqlConnection conn)
  818. {
  819. da.InsertCommand = createInsertCommand("primshapes", ds.Tables["primshapes"]);
  820. da.InsertCommand.Connection = conn;
  821. da.UpdateCommand = createUpdateCommand("primshapes", "UUID=@UUID", ds.Tables["primshapes"]);
  822. da.UpdateCommand.Connection = conn;
  823. SqlCommand delete = new SqlCommand("delete from primshapes where UUID = @UUID");
  824. delete.Parameters.Add(createSqlParameter("UUID", typeof (String)));
  825. delete.Connection = conn;
  826. da.DeleteCommand = delete;
  827. }
  828. private void setupTerrainCommands(SqlDataAdapter da, SqlConnection conn)
  829. {
  830. da.InsertCommand = createInsertCommand("terrain", ds.Tables["terrain"]);
  831. da.InsertCommand.Connection = conn;
  832. }
  833. private static void InitDB(SqlConnection conn)
  834. {
  835. string createPrims = defineTable(createPrimTable());
  836. string createShapes = defineTable(createShapeTable());
  837. string createTerrain = defineTable(createTerrainTable());
  838. SqlCommand pcmd = new SqlCommand(createPrims, conn);
  839. SqlCommand scmd = new SqlCommand(createShapes, conn);
  840. SqlCommand tcmd = new SqlCommand(createTerrain, conn);
  841. conn.Open();
  842. try
  843. {
  844. pcmd.ExecuteNonQuery();
  845. pcmd.Dispose();
  846. }
  847. catch (SqlException)
  848. {
  849. m_log.Warn("[MSSQL]: Primitives Table Already Exists");
  850. }
  851. try
  852. {
  853. scmd.ExecuteNonQuery();
  854. scmd.Dispose();
  855. }
  856. catch (SqlException)
  857. {
  858. m_log.Warn("[MSSQL]: Shapes Table Already Exists");
  859. }
  860. try
  861. {
  862. tcmd.ExecuteNonQuery();
  863. tcmd.Dispose();
  864. }
  865. catch (SqlException)
  866. {
  867. m_log.Warn("[MSSQL]: Terrain Table Already Exists");
  868. }
  869. conn.Close();
  870. }
  871. private static bool TestTables(SqlConnection conn)
  872. {
  873. SqlCommand primSelectCmd = new SqlCommand(primSelect, conn);
  874. SqlDataAdapter pDa = new SqlDataAdapter(primSelectCmd);
  875. SqlCommand shapeSelectCmd = new SqlCommand(shapeSelect, conn);
  876. SqlDataAdapter sDa = new SqlDataAdapter(shapeSelectCmd);
  877. SqlCommand terrainSelectCmd = new SqlCommand(terrainSelect, conn);
  878. SqlDataAdapter tDa = new SqlDataAdapter(terrainSelectCmd);
  879. DataSet tmpDS = new DataSet();
  880. try
  881. {
  882. pDa.Fill(tmpDS, "prims");
  883. sDa.Fill(tmpDS, "primshapes");
  884. tDa.Fill(tmpDS, "terrain");
  885. }
  886. catch (SqlException)
  887. {
  888. m_log.Info("[DATASTORE]: MSSQL Database doesn't exist... creating");
  889. InitDB(conn);
  890. }
  891. try
  892. {
  893. if (tmpDS.Tables == null || tmpDS.Tables.Count == 0)
  894. {
  895. pDa.Fill(tmpDS, "prims");
  896. sDa.Fill(tmpDS, "primshapes");
  897. tDa.Fill(tmpDS, "terrain");
  898. }
  899. }
  900. catch (SqlException e)
  901. {
  902. m_log.Info("[DATASTORE]: " + e.ToString());
  903. }
  904. foreach (DataColumn col in createPrimTable().Columns)
  905. {
  906. if (!tmpDS.Tables["prims"].Columns.Contains(col.ColumnName))
  907. {
  908. m_log.Info("[DATASTORE]: Missing required column:" + col.ColumnName);
  909. return false;
  910. }
  911. }
  912. foreach (DataColumn col in createShapeTable().Columns)
  913. {
  914. if (!tmpDS.Tables["primshapes"].Columns.Contains(col.ColumnName))
  915. {
  916. m_log.Info("[DATASTORE]: Missing required column:" + col.ColumnName);
  917. return false;
  918. }
  919. }
  920. foreach (DataColumn col in createTerrainTable().Columns)
  921. {
  922. if (!tmpDS.Tables["terrain"].Columns.Contains(col.ColumnName))
  923. {
  924. m_log.Info("[DATASTORE]: Missing require column:" + col.ColumnName);
  925. return false;
  926. }
  927. }
  928. return true;
  929. }
  930. /***********************************************************************
  931. *
  932. * Type conversion functions
  933. *
  934. **********************************************************************/
  935. private static DbType dbtypeFromType(Type type)
  936. {
  937. if (type == typeof (String))
  938. {
  939. return DbType.String;
  940. }
  941. else if (type == typeof (Int32))
  942. {
  943. return DbType.Int32;
  944. }
  945. else if (type == typeof (Double))
  946. {
  947. return DbType.Double;
  948. }
  949. else if (type == typeof (Byte[]))
  950. {
  951. return DbType.Binary;
  952. }
  953. else
  954. {
  955. return DbType.String;
  956. }
  957. }
  958. // this is something we'll need to implement for each db
  959. // slightly differently.
  960. private static string SqlType(Type type)
  961. {
  962. if (type == typeof (String))
  963. {
  964. return "varchar(255)";
  965. }
  966. else if (type == typeof (Int32))
  967. {
  968. return "integer";
  969. }
  970. else if (type == typeof (Double))
  971. {
  972. return "float";
  973. }
  974. else if (type == typeof (Byte[]))
  975. {
  976. return "image";
  977. }
  978. else
  979. {
  980. return "string";
  981. }
  982. }
  983. }
  984. }