MSSQLDataStore.cs 44 KB

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