MSSQLDataStore.cs 44 KB

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