MSSQLDataStore.cs 44 KB

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