diff options
-rw-r--r-- | OpenSim/Region/Storage/OpenSim.DataStore.MonoSqlite/MonoSqliteDataStore.cs | 743 |
1 files changed, 394 insertions, 349 deletions
diff --git a/OpenSim/Region/Storage/OpenSim.DataStore.MonoSqlite/MonoSqliteDataStore.cs b/OpenSim/Region/Storage/OpenSim.DataStore.MonoSqlite/MonoSqliteDataStore.cs index fb4f53f..87df8fb 100644 --- a/OpenSim/Region/Storage/OpenSim.DataStore.MonoSqlite/MonoSqliteDataStore.cs +++ b/OpenSim/Region/Storage/OpenSim.DataStore.MonoSqlite/MonoSqliteDataStore.cs | |||
@@ -31,6 +31,11 @@ namespace OpenSim.DataStore.MonoSqliteStorage | |||
31 | private SqliteDataAdapter primDa; | 31 | private SqliteDataAdapter primDa; |
32 | private SqliteDataAdapter shapeDa; | 32 | private SqliteDataAdapter shapeDa; |
33 | 33 | ||
34 | /*********************************************************************** | ||
35 | * | ||
36 | * Public Interface Functions | ||
37 | * | ||
38 | **********************************************************************/ | ||
34 | public void Initialise(string dbfile, string dbname) | 39 | public void Initialise(string dbfile, string dbname) |
35 | { | 40 | { |
36 | string connectionString = "URI=file:" + dbfile + ",version=3"; | 41 | string connectionString = "URI=file:" + dbfile + ",version=3"; |
@@ -65,129 +70,276 @@ namespace OpenSim.DataStore.MonoSqliteStorage | |||
65 | return; | 70 | return; |
66 | } | 71 | } |
67 | 72 | ||
68 | ///<summary> | 73 | public void StoreObject(SceneObjectGroup obj, LLUUID regionUUID) |
69 | /// This is a convenience function that collapses 5 repetitive | ||
70 | /// lines for defining SqliteParameters to 2 parameters: | ||
71 | /// column name and database type. | ||
72 | /// | ||
73 | /// It assumes certain conventions like :param as the param | ||
74 | /// name to replace in parametrized queries, and that source | ||
75 | /// version is always current version, both of which are fine | ||
76 | /// for us. | ||
77 | ///</summary> | ||
78 | ///<returns>a built sqlite parameter</returns> | ||
79 | private SqliteParameter createSqliteParameter(string name, System.Type type) | ||
80 | { | 74 | { |
81 | SqliteParameter param = new SqliteParameter(); | 75 | foreach (SceneObjectPart prim in obj.Children.Values) |
82 | param.ParameterName = ":" + name; | 76 | { |
83 | param.DbType = dbtypeFromType(type); | 77 | addPrim(prim, obj.UUID); |
84 | param.SourceColumn = name; | 78 | } |
85 | param.SourceVersion = DataRowVersion.Current; | 79 | |
86 | return param; | 80 | // MainLog.Instance.Verbose("Attempting to do database update...."); |
81 | primDa.Update(ds, "prims"); | ||
82 | shapeDa.Update(ds, "primshapes"); | ||
83 | // MainLog.Instance.Verbose("Dump of prims:", ds.GetXml()); | ||
87 | } | 84 | } |
88 | 85 | ||
89 | private DbType dbtypeFromType(Type type) | 86 | public void RemoveObject(LLUUID obj, LLUUID regionUUID) |
90 | { | 87 | { |
91 | if (type == typeof(System.String)) { | 88 | DataTable prims = ds.Tables["prims"]; |
92 | return DbType.String; | 89 | DataTable shapes = ds.Tables["primshapes"]; |
93 | } else if (type == typeof(System.Int32)) { | 90 | |
94 | return DbType.Int32; | 91 | string selectExp = "SceneGroupID = '" + obj.ToString() + "'"; |
95 | } else if (type == typeof(System.Double)) { | 92 | DataRow[] primRows = prims.Select(selectExp); |
96 | return DbType.Double; | 93 | foreach (DataRow row in primRows) |
97 | } else if (type == typeof(System.Byte[])) { | 94 | { |
98 | return DbType.Binary; | 95 | LLUUID uuid = new LLUUID((string)row["UUID"]); |
99 | } else { | 96 | DataRow shapeRow = shapes.Rows.Find(uuid); |
100 | return DbType.String; | 97 | if (shapeRow != null) |
98 | { | ||
99 | shapeRow.Delete(); | ||
100 | } | ||
101 | row.Delete(); | ||
101 | } | 102 | } |
103 | |||
104 | primDa.Update(ds, "prims"); | ||
105 | shapeDa.Update(ds, "primshapes"); | ||
102 | } | 106 | } |
103 | 107 | ||
104 | private SqliteCommand createInsertCommand(string table, DataTable dt) | 108 | public List<SceneObjectGroup> LoadObjects(LLUUID regionUUID) |
105 | { | 109 | { |
106 | /** | 110 | Dictionary<LLUUID, SceneObjectGroup> createdObjects = new Dictionary<LLUUID, SceneObjectGroup>(); |
107 | * This is subtle enough to deserve some commentary. | 111 | List<SceneObjectGroup> retvals = new List<SceneObjectGroup>(); |
108 | * Instead of doing *lots* and *lots of hardcoded strings | ||
109 | * for database definitions we'll use the fact that | ||
110 | * realistically all insert statements look like "insert | ||
111 | * into A(b, c) values(:b, :c) on the parameterized query | ||
112 | * front. If we just have a list of b, c, etc... we can | ||
113 | * generate these strings instead of typing them out. | ||
114 | */ | ||
115 | string[] cols = new string[dt.Columns.Count]; | ||
116 | for (int i = 0; i < dt.Columns.Count; i++) { | ||
117 | DataColumn col = dt.Columns[i]; | ||
118 | cols[i] = col.ColumnName; | ||
119 | } | ||
120 | 112 | ||
121 | string sql = "insert into " + table + "("; | 113 | DataTable prims = ds.Tables["prims"]; |
122 | sql += String.Join(", ", cols); | 114 | DataTable shapes = ds.Tables["primshapes"]; |
123 | // important, the first ':' needs to be here, the rest get added in the join | ||
124 | sql += ") values (:"; | ||
125 | sql += String.Join(", :", cols); | ||
126 | sql += ")"; | ||
127 | SqliteCommand cmd = new SqliteCommand(sql); | ||
128 | 115 | ||
129 | // this provides the binding for all our parameters, so | 116 | foreach (DataRow primRow in prims.Rows) |
130 | // much less code than it used to be | ||
131 | foreach (DataColumn col in dt.Columns) | ||
132 | { | 117 | { |
133 | cmd.Parameters.Add(createSqliteParameter(col.ColumnName, col.DataType)); | 118 | string uuid = (string)primRow["UUID"]; |
119 | string objID = (string)primRow["SceneGroupID"]; | ||
120 | if (uuid == objID) //is new SceneObjectGroup ? | ||
121 | { | ||
122 | SceneObjectGroup group = new SceneObjectGroup(); | ||
123 | SceneObjectPart prim = buildPrim(primRow); | ||
124 | DataRow shapeRow = shapes.Rows.Find(prim.UUID); | ||
125 | if (shapeRow != null) | ||
126 | { | ||
127 | prim.Shape = buildShape(shapeRow); | ||
128 | } | ||
129 | else | ||
130 | { | ||
131 | Console.WriteLine("No shape found for prim in storage, so setting default box shape"); | ||
132 | prim.Shape = BoxShape.Default; | ||
133 | } | ||
134 | group.AddPart(prim); | ||
135 | group.RootPart = prim; | ||
136 | |||
137 | createdObjects.Add(group.UUID, group); | ||
138 | retvals.Add(group); | ||
139 | } | ||
140 | else | ||
141 | { | ||
142 | SceneObjectPart prim = buildPrim(primRow); | ||
143 | DataRow shapeRow = shapes.Rows.Find(prim.UUID); | ||
144 | if (shapeRow != null) | ||
145 | { | ||
146 | prim.Shape = buildShape(shapeRow); | ||
147 | } | ||
148 | else | ||
149 | { | ||
150 | Console.WriteLine("No shape found for prim in storage, so setting default box shape"); | ||
151 | prim.Shape = BoxShape.Default; | ||
152 | } | ||
153 | createdObjects[new LLUUID(objID)].AddPart(prim); | ||
154 | } | ||
134 | } | 155 | } |
135 | return cmd; | 156 | |
157 | MainLog.Instance.Verbose("DATASTORE", "Sqlite - LoadObjects found " + prims.Rows.Count + " primitives"); | ||
158 | |||
159 | return retvals; | ||
136 | } | 160 | } |
137 | 161 | ||
138 | private SqliteCommand createUpdateCommand(string table, string pk, DataTable dt) | 162 | public void StoreTerrain(double[,] ter) |
139 | { | 163 | { |
140 | string sql = "update " + table + " set "; | 164 | |
141 | string subsql = ""; | 165 | } |
142 | foreach (DataColumn col in dt.Columns) | 166 | |
167 | public double[,] LoadTerrain() | ||
168 | { | ||
169 | return null; | ||
170 | } | ||
171 | |||
172 | public void RemoveLandObject(uint id) | ||
173 | { | ||
174 | |||
175 | } | ||
176 | |||
177 | public void StoreParcel(Land parcel) | ||
178 | { | ||
179 | |||
180 | } | ||
181 | |||
182 | public List<Land> LoadLandObjects() | ||
183 | { | ||
184 | return new List<Land>(); | ||
185 | } | ||
186 | |||
187 | public void Shutdown() | ||
188 | { | ||
189 | // TODO: DataSet commit | ||
190 | } | ||
191 | |||
192 | public class TextureBlock | ||
193 | { | ||
194 | public byte[] TextureData; | ||
195 | public byte[] ExtraParams = new byte[1]; | ||
196 | |||
197 | public TextureBlock(byte[] data) | ||
143 | { | 198 | { |
144 | if (subsql.Length > 0) | 199 | TextureData = data; |
145 | { // a map function would rock so much here | ||
146 | subsql += ", "; | ||
147 | } | ||
148 | subsql += col.ColumnName + "= :" + col.ColumnName; | ||
149 | } | 200 | } |
150 | sql += subsql; | ||
151 | sql += " where " + pk; | ||
152 | SqliteCommand cmd = new SqliteCommand(sql); | ||
153 | 201 | ||
154 | // this provides the binding for all our parameters, so | 202 | public TextureBlock() |
155 | // much less code than it used to be | 203 | { |
156 | 204 | ||
157 | foreach (DataColumn col in dt.Columns) | 205 | } |
206 | |||
207 | public string ToXMLString() | ||
158 | { | 208 | { |
159 | cmd.Parameters.Add(createSqliteParameter(col.ColumnName, col.DataType)); | 209 | StringWriter sw = new StringWriter(); |
210 | XmlTextWriter writer = new XmlTextWriter(sw); | ||
211 | XmlSerializer serializer = new XmlSerializer(typeof(TextureBlock)); | ||
212 | serializer.Serialize(writer, this); | ||
213 | return sw.ToString(); | ||
214 | } | ||
215 | |||
216 | public static TextureBlock FromXmlString(string xmlData) | ||
217 | { | ||
218 | TextureBlock textureEntry = null; | ||
219 | StringReader sr = new StringReader(xmlData); | ||
220 | XmlTextReader reader = new XmlTextReader(sr); | ||
221 | XmlSerializer serializer = new XmlSerializer(typeof(TextureBlock)); | ||
222 | textureEntry = (TextureBlock)serializer.Deserialize(reader); | ||
223 | reader.Close(); | ||
224 | sr.Close(); | ||
225 | return textureEntry; | ||
160 | } | 226 | } |
161 | return cmd; | ||
162 | } | 227 | } |
163 | 228 | ||
164 | private void setupPrimCommands(SqliteDataAdapter da, SqliteConnection conn) | 229 | /*********************************************************************** |
230 | * | ||
231 | * Database Definition Functions | ||
232 | * | ||
233 | * This should be db agnostic as we define them in ADO.NET terms | ||
234 | * | ||
235 | **********************************************************************/ | ||
236 | |||
237 | private void createCol(DataTable dt, string name, System.Type type) | ||
165 | { | 238 | { |
166 | da.InsertCommand = createInsertCommand("prims", ds.Tables["prims"]); | 239 | DataColumn col = new DataColumn(name, type); |
167 | da.InsertCommand.Connection = conn; | 240 | dt.Columns.Add(col); |
241 | } | ||
168 | 242 | ||
169 | da.UpdateCommand = createUpdateCommand("prims", "UUID=:UUID", ds.Tables["prims"]); | 243 | private DataTable createPrimTable() |
170 | da.UpdateCommand.Connection = conn; | 244 | { |
245 | DataTable prims = new DataTable("prims"); | ||
171 | 246 | ||
172 | SqliteCommand delete = new SqliteCommand("delete from prims where UUID = :UUID"); | 247 | createCol(prims, "UUID", typeof(System.String)); |
173 | delete.Parameters.Add(createSqliteParameter("UUID", typeof(System.String))); | 248 | createCol(prims, "ParentID", typeof(System.Int32)); |
174 | delete.Connection = conn; | 249 | createCol(prims, "CreationDate", typeof(System.Int32)); |
175 | da.DeleteCommand = delete; | 250 | createCol(prims, "Name", typeof(System.String)); |
251 | createCol(prims, "SceneGroupID", typeof(System.String)); | ||
252 | // various text fields | ||
253 | createCol(prims, "Text", typeof(System.String)); | ||
254 | createCol(prims, "Description", typeof(System.String)); | ||
255 | createCol(prims, "SitName", typeof(System.String)); | ||
256 | createCol(prims, "TouchName", typeof(System.String)); | ||
257 | // permissions | ||
258 | createCol(prims, "CreatorID", typeof(System.String)); | ||
259 | createCol(prims, "OwnerID", typeof(System.String)); | ||
260 | createCol(prims, "GroupID", typeof(System.String)); | ||
261 | createCol(prims, "LastOwnerID", typeof(System.String)); | ||
262 | createCol(prims, "OwnerMask", typeof(System.Int32)); | ||
263 | createCol(prims, "NextOwnerMask", typeof(System.Int32)); | ||
264 | createCol(prims, "GroupMask", typeof(System.Int32)); | ||
265 | createCol(prims, "EveryoneMask", typeof(System.Int32)); | ||
266 | createCol(prims, "BaseMask", typeof(System.Int32)); | ||
267 | // vectors | ||
268 | createCol(prims, "PositionX", typeof(System.Double)); | ||
269 | createCol(prims, "PositionY", typeof(System.Double)); | ||
270 | createCol(prims, "PositionZ", typeof(System.Double)); | ||
271 | createCol(prims, "GroupPositionX", typeof(System.Double)); | ||
272 | createCol(prims, "GroupPositionY", typeof(System.Double)); | ||
273 | createCol(prims, "GroupPositionZ", typeof(System.Double)); | ||
274 | createCol(prims, "VelocityX", typeof(System.Double)); | ||
275 | createCol(prims, "VelocityY", typeof(System.Double)); | ||
276 | createCol(prims, "VelocityZ", typeof(System.Double)); | ||
277 | createCol(prims, "AngularVelocityX", typeof(System.Double)); | ||
278 | createCol(prims, "AngularVelocityY", typeof(System.Double)); | ||
279 | createCol(prims, "AngularVelocityZ", typeof(System.Double)); | ||
280 | createCol(prims, "AccelerationX", typeof(System.Double)); | ||
281 | createCol(prims, "AccelerationY", typeof(System.Double)); | ||
282 | createCol(prims, "AccelerationZ", typeof(System.Double)); | ||
283 | // quaternions | ||
284 | createCol(prims, "RotationX", typeof(System.Double)); | ||
285 | createCol(prims, "RotationY", typeof(System.Double)); | ||
286 | createCol(prims, "RotationZ", typeof(System.Double)); | ||
287 | createCol(prims, "RotationW", typeof(System.Double)); | ||
288 | |||
289 | // Add in contraints | ||
290 | prims.PrimaryKey = new DataColumn[] { prims.Columns["UUID"] }; | ||
291 | |||
292 | return prims; | ||
176 | } | 293 | } |
177 | 294 | ||
178 | private void setupShapeCommands(SqliteDataAdapter da, SqliteConnection conn) | 295 | private DataTable createShapeTable() |
179 | { | 296 | { |
180 | da.InsertCommand = createInsertCommand("primshapes", ds.Tables["primshapes"]); | 297 | DataTable shapes = new DataTable("primshapes"); |
181 | da.InsertCommand.Connection = conn; | 298 | createCol(shapes, "UUID", typeof(System.String)); |
299 | // shape is an enum | ||
300 | createCol(shapes, "Shape", typeof(System.Int32)); | ||
301 | // vectors | ||
302 | createCol(shapes, "ScaleX", typeof(System.Double)); | ||
303 | createCol(shapes, "ScaleY", typeof(System.Double)); | ||
304 | createCol(shapes, "ScaleZ", typeof(System.Double)); | ||
305 | // paths | ||
306 | createCol(shapes, "PCode", typeof(System.Int32)); | ||
307 | createCol(shapes, "PathBegin", typeof(System.Int32)); | ||
308 | createCol(shapes, "PathEnd", typeof(System.Int32)); | ||
309 | createCol(shapes, "PathScaleX", typeof(System.Int32)); | ||
310 | createCol(shapes, "PathScaleY", typeof(System.Int32)); | ||
311 | createCol(shapes, "PathShearX", typeof(System.Int32)); | ||
312 | createCol(shapes, "PathShearY", typeof(System.Int32)); | ||
313 | createCol(shapes, "PathSkew", typeof(System.Int32)); | ||
314 | createCol(shapes, "PathCurve", typeof(System.Int32)); | ||
315 | createCol(shapes, "PathRadiusOffset", typeof(System.Int32)); | ||
316 | createCol(shapes, "PathRevolutions", typeof(System.Int32)); | ||
317 | createCol(shapes, "PathTaperX", typeof(System.Int32)); | ||
318 | createCol(shapes, "PathTaperY", typeof(System.Int32)); | ||
319 | createCol(shapes, "PathTwist", typeof(System.Int32)); | ||
320 | createCol(shapes, "PathTwistBegin", typeof(System.Int32)); | ||
321 | // profile | ||
322 | createCol(shapes, "ProfileBegin", typeof(System.Int32)); | ||
323 | createCol(shapes, "ProfileEnd", typeof(System.Int32)); | ||
324 | createCol(shapes, "ProfileCurve", typeof(System.Int32)); | ||
325 | createCol(shapes, "ProfileHollow", typeof(System.Int32)); | ||
326 | // text TODO: this isn't right, but I'm not sure the right | ||
327 | // way to specify this as a blob atm | ||
328 | createCol(shapes, "Texture", typeof(System.Byte[])); | ||
329 | createCol(shapes, "ExtraParams", typeof(System.Byte[])); | ||
182 | 330 | ||
183 | da.UpdateCommand = createUpdateCommand("primshapes", "UUID=:UUID", ds.Tables["primshapes"]); | 331 | shapes.PrimaryKey = new DataColumn[] { shapes.Columns["UUID"] }; |
184 | da.UpdateCommand.Connection = conn; | ||
185 | 332 | ||
186 | SqliteCommand delete = new SqliteCommand("delete from primshapes where UUID = :UUID"); | 333 | return shapes; |
187 | delete.Parameters.Add(createSqliteParameter("UUID", typeof(System.String))); | ||
188 | delete.Connection = conn; | ||
189 | da.DeleteCommand = delete; | ||
190 | } | 334 | } |
335 | |||
336 | /*********************************************************************** | ||
337 | * | ||
338 | * Convert between ADO.NET <=> OpenSim Objects | ||
339 | * | ||
340 | * These should be database independant | ||
341 | * | ||
342 | **********************************************************************/ | ||
191 | 343 | ||
192 | private SceneObjectPart buildPrim(DataRow row) | 344 | private SceneObjectPart buildPrim(DataRow row) |
193 | { | 345 | { |
@@ -424,163 +576,157 @@ namespace OpenSim.DataStore.MonoSqliteStorage | |||
424 | fillShapeRow(shapeRow, prim); | 576 | fillShapeRow(shapeRow, prim); |
425 | } | 577 | } |
426 | } | 578 | } |
579 | |||
580 | /*********************************************************************** | ||
581 | * | ||
582 | * SQL Statement Creation Functions | ||
583 | * | ||
584 | * These functions create SQL statements for update, insert, and create. | ||
585 | * They can probably be factored later to have a db independant | ||
586 | * portion and a db specific portion | ||
587 | * | ||
588 | **********************************************************************/ | ||
427 | 589 | ||
428 | public void StoreObject(SceneObjectGroup obj, LLUUID regionUUID) | 590 | private SqliteCommand createInsertCommand(string table, DataTable dt) |
429 | { | 591 | { |
430 | foreach (SceneObjectPart prim in obj.Children.Values) | 592 | /** |
431 | { | 593 | * This is subtle enough to deserve some commentary. |
432 | addPrim(prim, obj.UUID); | 594 | * Instead of doing *lots* and *lots of hardcoded strings |
595 | * for database definitions we'll use the fact that | ||
596 | * realistically all insert statements look like "insert | ||
597 | * into A(b, c) values(:b, :c) on the parameterized query | ||
598 | * front. If we just have a list of b, c, etc... we can | ||
599 | * generate these strings instead of typing them out. | ||
600 | */ | ||
601 | string[] cols = new string[dt.Columns.Count]; | ||
602 | for (int i = 0; i < dt.Columns.Count; i++) { | ||
603 | DataColumn col = dt.Columns[i]; | ||
604 | cols[i] = col.ColumnName; | ||
433 | } | 605 | } |
434 | 606 | ||
435 | // MainLog.Instance.Verbose("Attempting to do database update...."); | 607 | string sql = "insert into " + table + "("; |
436 | primDa.Update(ds, "prims"); | 608 | sql += String.Join(", ", cols); |
437 | shapeDa.Update(ds, "primshapes"); | 609 | // important, the first ':' needs to be here, the rest get added in the join |
438 | // MainLog.Instance.Verbose("Dump of prims:", ds.GetXml()); | 610 | sql += ") values (:"; |
611 | sql += String.Join(", :", cols); | ||
612 | sql += ")"; | ||
613 | SqliteCommand cmd = new SqliteCommand(sql); | ||
614 | |||
615 | // this provides the binding for all our parameters, so | ||
616 | // much less code than it used to be | ||
617 | foreach (DataColumn col in dt.Columns) | ||
618 | { | ||
619 | cmd.Parameters.Add(createSqliteParameter(col.ColumnName, col.DataType)); | ||
620 | } | ||
621 | return cmd; | ||
439 | } | 622 | } |
440 | 623 | ||
441 | public void RemoveObject(LLUUID obj, LLUUID regionUUID) | 624 | private SqliteCommand createUpdateCommand(string table, string pk, DataTable dt) |
442 | { | 625 | { |
443 | DataTable prims = ds.Tables["prims"]; | 626 | string sql = "update " + table + " set "; |
444 | DataTable shapes = ds.Tables["primshapes"]; | 627 | string subsql = ""; |
445 | 628 | foreach (DataColumn col in dt.Columns) | |
446 | string selectExp = "SceneGroupID = '" + obj.ToString() + "'"; | ||
447 | DataRow[] primRows = prims.Select(selectExp); | ||
448 | foreach (DataRow row in primRows) | ||
449 | { | 629 | { |
450 | LLUUID uuid = new LLUUID((string)row["UUID"]); | 630 | if (subsql.Length > 0) |
451 | DataRow shapeRow = shapes.Rows.Find(uuid); | 631 | { // a map function would rock so much here |
452 | if (shapeRow != null) | 632 | subsql += ", "; |
453 | { | ||
454 | shapeRow.Delete(); | ||
455 | } | 633 | } |
456 | row.Delete(); | 634 | subsql += col.ColumnName + "= :" + col.ColumnName; |
457 | } | 635 | } |
636 | sql += subsql; | ||
637 | sql += " where " + pk; | ||
638 | SqliteCommand cmd = new SqliteCommand(sql); | ||
458 | 639 | ||
459 | primDa.Update(ds, "prims"); | 640 | // this provides the binding for all our parameters, so |
460 | shapeDa.Update(ds, "primshapes"); | 641 | // much less code than it used to be |
642 | |||
643 | foreach (DataColumn col in dt.Columns) | ||
644 | { | ||
645 | cmd.Parameters.Add(createSqliteParameter(col.ColumnName, col.DataType)); | ||
646 | } | ||
647 | return cmd; | ||
461 | } | 648 | } |
462 | 649 | ||
463 | public List<SceneObjectGroup> LoadObjects(LLUUID regionUUID) | ||
464 | { | ||
465 | Dictionary<LLUUID, SceneObjectGroup> createdObjects = new Dictionary<LLUUID, SceneObjectGroup>(); | ||
466 | List<SceneObjectGroup> retvals = new List<SceneObjectGroup>(); | ||
467 | 650 | ||
468 | DataTable prims = ds.Tables["prims"]; | 651 | private string defineTable(DataTable dt) |
469 | DataTable shapes = ds.Tables["primshapes"]; | 652 | { |
470 | 653 | string sql = "create table " + dt.TableName + "("; | |
471 | foreach (DataRow primRow in prims.Rows) | 654 | string subsql = ""; |
655 | foreach (DataColumn col in dt.Columns) | ||
472 | { | 656 | { |
473 | string uuid = (string)primRow["UUID"]; | 657 | if (subsql.Length > 0) |
474 | string objID = (string)primRow["SceneGroupID"]; | 658 | { // a map function would rock so much here |
475 | if (uuid == objID) //is new SceneObjectGroup ? | 659 | subsql += ",\n"; |
476 | { | ||
477 | SceneObjectGroup group = new SceneObjectGroup(); | ||
478 | SceneObjectPart prim = buildPrim(primRow); | ||
479 | DataRow shapeRow = shapes.Rows.Find(prim.UUID); | ||
480 | if (shapeRow != null) | ||
481 | { | ||
482 | prim.Shape = buildShape(shapeRow); | ||
483 | } | ||
484 | else | ||
485 | { | ||
486 | Console.WriteLine("No shape found for prim in storage, so setting default box shape"); | ||
487 | prim.Shape = BoxShape.Default; | ||
488 | } | ||
489 | group.AddPart(prim); | ||
490 | group.RootPart = prim; | ||
491 | |||
492 | createdObjects.Add(group.UUID, group); | ||
493 | retvals.Add(group); | ||
494 | } | 660 | } |
495 | else | 661 | subsql += col.ColumnName + " " + sqliteType(col.DataType); |
662 | if(col == dt.PrimaryKey[0]) | ||
496 | { | 663 | { |
497 | SceneObjectPart prim = buildPrim(primRow); | 664 | subsql += " primary key"; |
498 | DataRow shapeRow = shapes.Rows.Find(prim.UUID); | ||
499 | if (shapeRow != null) | ||
500 | { | ||
501 | prim.Shape = buildShape(shapeRow); | ||
502 | } | ||
503 | else | ||
504 | { | ||
505 | Console.WriteLine("No shape found for prim in storage, so setting default box shape"); | ||
506 | prim.Shape = BoxShape.Default; | ||
507 | } | ||
508 | createdObjects[new LLUUID(objID)].AddPart(prim); | ||
509 | } | 665 | } |
510 | } | 666 | } |
511 | 667 | sql += subsql; | |
512 | MainLog.Instance.Verbose("DATASTORE", "Sqlite - LoadObjects found " + prims.Rows.Count + " primitives"); | 668 | sql += ")"; |
513 | 669 | return sql; | |
514 | return retvals; | ||
515 | } | ||
516 | |||
517 | public void StoreTerrain(double[,] ter) | ||
518 | { | ||
519 | |||
520 | } | 670 | } |
521 | 671 | ||
522 | public double[,] LoadTerrain() | 672 | /*********************************************************************** |
523 | { | 673 | * |
524 | return null; | 674 | * Database Binding functions |
525 | } | 675 | * |
676 | * These will be db specific due to typing, and minor differences | ||
677 | * in databases. | ||
678 | * | ||
679 | **********************************************************************/ | ||
526 | 680 | ||
527 | public void RemoveLandObject(uint id) | 681 | ///<summary> |
682 | /// This is a convenience function that collapses 5 repetitive | ||
683 | /// lines for defining SqliteParameters to 2 parameters: | ||
684 | /// column name and database type. | ||
685 | /// | ||
686 | /// It assumes certain conventions like :param as the param | ||
687 | /// name to replace in parametrized queries, and that source | ||
688 | /// version is always current version, both of which are fine | ||
689 | /// for us. | ||
690 | ///</summary> | ||
691 | ///<returns>a built sqlite parameter</returns> | ||
692 | private SqliteParameter createSqliteParameter(string name, System.Type type) | ||
528 | { | 693 | { |
529 | 694 | SqliteParameter param = new SqliteParameter(); | |
695 | param.ParameterName = ":" + name; | ||
696 | param.DbType = dbtypeFromType(type); | ||
697 | param.SourceColumn = name; | ||
698 | param.SourceVersion = DataRowVersion.Current; | ||
699 | return param; | ||
530 | } | 700 | } |
531 | 701 | ||
532 | public void StoreParcel(Land parcel) | 702 | private void setupPrimCommands(SqliteDataAdapter da, SqliteConnection conn) |
533 | { | 703 | { |
704 | da.InsertCommand = createInsertCommand("prims", ds.Tables["prims"]); | ||
705 | da.InsertCommand.Connection = conn; | ||
534 | 706 | ||
535 | } | 707 | da.UpdateCommand = createUpdateCommand("prims", "UUID=:UUID", ds.Tables["prims"]); |
536 | 708 | da.UpdateCommand.Connection = conn; | |
537 | public List<Land> LoadLandObjects() | ||
538 | { | ||
539 | return new List<Land>(); | ||
540 | } | ||
541 | 709 | ||
542 | public void Shutdown() | 710 | SqliteCommand delete = new SqliteCommand("delete from prims where UUID = :UUID"); |
543 | { | 711 | delete.Parameters.Add(createSqliteParameter("UUID", typeof(System.String))); |
544 | // TODO: DataSet commit | 712 | delete.Connection = conn; |
713 | da.DeleteCommand = delete; | ||
545 | } | 714 | } |
546 | 715 | ||
547 | public class TextureBlock | 716 | private void setupShapeCommands(SqliteDataAdapter da, SqliteConnection conn) |
548 | { | 717 | { |
549 | public byte[] TextureData; | 718 | da.InsertCommand = createInsertCommand("primshapes", ds.Tables["primshapes"]); |
550 | public byte[] ExtraParams = new byte[1]; | 719 | da.InsertCommand.Connection = conn; |
551 | |||
552 | public TextureBlock(byte[] data) | ||
553 | { | ||
554 | TextureData = data; | ||
555 | } | ||
556 | |||
557 | public TextureBlock() | ||
558 | { | ||
559 | |||
560 | } | ||
561 | 720 | ||
562 | public string ToXMLString() | 721 | da.UpdateCommand = createUpdateCommand("primshapes", "UUID=:UUID", ds.Tables["primshapes"]); |
563 | { | 722 | da.UpdateCommand.Connection = conn; |
564 | StringWriter sw = new StringWriter(); | ||
565 | XmlTextWriter writer = new XmlTextWriter(sw); | ||
566 | XmlSerializer serializer = new XmlSerializer(typeof(TextureBlock)); | ||
567 | serializer.Serialize(writer, this); | ||
568 | return sw.ToString(); | ||
569 | } | ||
570 | 723 | ||
571 | public static TextureBlock FromXmlString(string xmlData) | 724 | SqliteCommand delete = new SqliteCommand("delete from primshapes where UUID = :UUID"); |
572 | { | 725 | delete.Parameters.Add(createSqliteParameter("UUID", typeof(System.String))); |
573 | TextureBlock textureEntry = null; | 726 | delete.Connection = conn; |
574 | StringReader sr = new StringReader(xmlData); | 727 | da.DeleteCommand = delete; |
575 | XmlTextReader reader = new XmlTextReader(sr); | ||
576 | XmlSerializer serializer = new XmlSerializer(typeof(TextureBlock)); | ||
577 | textureEntry = (TextureBlock)serializer.Deserialize(reader); | ||
578 | reader.Close(); | ||
579 | sr.Close(); | ||
580 | return textureEntry; | ||
581 | } | ||
582 | } | 728 | } |
583 | 729 | ||
584 | private void InitDB(SqliteConnection conn) | 730 | private void InitDB(SqliteConnection conn) |
585 | { | 731 | { |
586 | string createPrims = defineTable(createPrimTable()); | 732 | string createPrims = defineTable(createPrimTable()); |
@@ -594,42 +740,6 @@ namespace OpenSim.DataStore.MonoSqliteStorage | |||
594 | conn.Close(); | 740 | conn.Close(); |
595 | } | 741 | } |
596 | 742 | ||
597 | private string defineTable(DataTable dt) | ||
598 | { | ||
599 | string sql = "create table " + dt.TableName + "("; | ||
600 | string subsql = ""; | ||
601 | foreach (DataColumn col in dt.Columns) | ||
602 | { | ||
603 | if (subsql.Length > 0) | ||
604 | { // a map function would rock so much here | ||
605 | subsql += ",\n"; | ||
606 | } | ||
607 | subsql += col.ColumnName + " " + sqliteType(col.DataType); | ||
608 | if(col == dt.PrimaryKey[0]) | ||
609 | { | ||
610 | subsql += " primary key"; | ||
611 | } | ||
612 | } | ||
613 | sql += subsql; | ||
614 | sql += ")"; | ||
615 | return sql; | ||
616 | } | ||
617 | |||
618 | private string sqliteType(Type type) | ||
619 | { | ||
620 | if (type == typeof(System.String)) { | ||
621 | return "varchar(255)"; | ||
622 | } else if (type == typeof(System.Int32)) { | ||
623 | return "integer"; | ||
624 | } else if (type == typeof(System.Double)) { | ||
625 | return "float"; | ||
626 | } else if (type == typeof(System.Byte[])) { | ||
627 | return "blob"; | ||
628 | } else { | ||
629 | return "string"; | ||
630 | } | ||
631 | } | ||
632 | |||
633 | private bool TestTables(SqliteConnection conn) | 743 | private bool TestTables(SqliteConnection conn) |
634 | { | 744 | { |
635 | SqliteCommand primSelectCmd = new SqliteCommand(primSelect, conn); | 745 | SqliteCommand primSelectCmd = new SqliteCommand(primSelect, conn); |
@@ -664,107 +774,42 @@ namespace OpenSim.DataStore.MonoSqliteStorage | |||
664 | return true; | 774 | return true; |
665 | } | 775 | } |
666 | 776 | ||
667 | /// Methods after this point are big data definition | 777 | /*********************************************************************** |
668 | /// methods, and aren't really interesting unless you are | 778 | * |
669 | /// adjusting the schema. | 779 | * Type conversion functions |
670 | 780 | * | |
671 | private void createCol(DataTable dt, string name, System.Type type) | 781 | **********************************************************************/ |
672 | { | 782 | |
673 | DataColumn col = new DataColumn(name, type); | 783 | private DbType dbtypeFromType(Type type) |
674 | dt.Columns.Add(col); | ||
675 | } | ||
676 | |||
677 | private DataTable createPrimTable() | ||
678 | { | 784 | { |
679 | DataTable prims = new DataTable("prims"); | 785 | if (type == typeof(System.String)) { |
680 | 786 | return DbType.String; | |
681 | createCol(prims, "UUID", typeof(System.String)); | 787 | } else if (type == typeof(System.Int32)) { |
682 | createCol(prims, "ParentID", typeof(System.Int32)); | 788 | return DbType.Int32; |
683 | createCol(prims, "CreationDate", typeof(System.Int32)); | 789 | } else if (type == typeof(System.Double)) { |
684 | createCol(prims, "Name", typeof(System.String)); | 790 | return DbType.Double; |
685 | createCol(prims, "SceneGroupID", typeof(System.String)); | 791 | } else if (type == typeof(System.Byte[])) { |
686 | // various text fields | 792 | return DbType.Binary; |
687 | createCol(prims, "Text", typeof(System.String)); | 793 | } else { |
688 | createCol(prims, "Description", typeof(System.String)); | 794 | return DbType.String; |
689 | createCol(prims, "SitName", typeof(System.String)); | 795 | } |
690 | createCol(prims, "TouchName", typeof(System.String)); | ||
691 | // permissions | ||
692 | createCol(prims, "CreatorID", typeof(System.String)); | ||
693 | createCol(prims, "OwnerID", typeof(System.String)); | ||
694 | createCol(prims, "GroupID", typeof(System.String)); | ||
695 | createCol(prims, "LastOwnerID", typeof(System.String)); | ||
696 | createCol(prims, "OwnerMask", typeof(System.Int32)); | ||
697 | createCol(prims, "NextOwnerMask", typeof(System.Int32)); | ||
698 | createCol(prims, "GroupMask", typeof(System.Int32)); | ||
699 | createCol(prims, "EveryoneMask", typeof(System.Int32)); | ||
700 | createCol(prims, "BaseMask", typeof(System.Int32)); | ||
701 | // vectors | ||
702 | createCol(prims, "PositionX", typeof(System.Double)); | ||
703 | createCol(prims, "PositionY", typeof(System.Double)); | ||
704 | createCol(prims, "PositionZ", typeof(System.Double)); | ||
705 | createCol(prims, "GroupPositionX", typeof(System.Double)); | ||
706 | createCol(prims, "GroupPositionY", typeof(System.Double)); | ||
707 | createCol(prims, "GroupPositionZ", typeof(System.Double)); | ||
708 | createCol(prims, "VelocityX", typeof(System.Double)); | ||
709 | createCol(prims, "VelocityY", typeof(System.Double)); | ||
710 | createCol(prims, "VelocityZ", typeof(System.Double)); | ||
711 | createCol(prims, "AngularVelocityX", typeof(System.Double)); | ||
712 | createCol(prims, "AngularVelocityY", typeof(System.Double)); | ||
713 | createCol(prims, "AngularVelocityZ", typeof(System.Double)); | ||
714 | createCol(prims, "AccelerationX", typeof(System.Double)); | ||
715 | createCol(prims, "AccelerationY", typeof(System.Double)); | ||
716 | createCol(prims, "AccelerationZ", typeof(System.Double)); | ||
717 | // quaternions | ||
718 | createCol(prims, "RotationX", typeof(System.Double)); | ||
719 | createCol(prims, "RotationY", typeof(System.Double)); | ||
720 | createCol(prims, "RotationZ", typeof(System.Double)); | ||
721 | createCol(prims, "RotationW", typeof(System.Double)); | ||
722 | |||
723 | // Add in contraints | ||
724 | prims.PrimaryKey = new DataColumn[] { prims.Columns["UUID"] }; | ||
725 | |||
726 | return prims; | ||
727 | } | 796 | } |
728 | 797 | ||
729 | private DataTable createShapeTable() | 798 | // this is something we'll need to implement for each db |
799 | // slightly differently. | ||
800 | private string sqliteType(Type type) | ||
730 | { | 801 | { |
731 | DataTable shapes = new DataTable("primshapes"); | 802 | if (type == typeof(System.String)) { |
732 | createCol(shapes, "UUID", typeof(System.String)); | 803 | return "varchar(255)"; |
733 | // shape is an enum | 804 | } else if (type == typeof(System.Int32)) { |
734 | createCol(shapes, "Shape", typeof(System.Int32)); | 805 | return "integer"; |
735 | // vectors | 806 | } else if (type == typeof(System.Double)) { |
736 | createCol(shapes, "ScaleX", typeof(System.Double)); | 807 | return "float"; |
737 | createCol(shapes, "ScaleY", typeof(System.Double)); | 808 | } else if (type == typeof(System.Byte[])) { |
738 | createCol(shapes, "ScaleZ", typeof(System.Double)); | 809 | return "blob"; |
739 | // paths | 810 | } else { |
740 | createCol(shapes, "PCode", typeof(System.Int32)); | 811 | return "string"; |
741 | createCol(shapes, "PathBegin", typeof(System.Int32)); | 812 | } |
742 | createCol(shapes, "PathEnd", typeof(System.Int32)); | ||
743 | createCol(shapes, "PathScaleX", typeof(System.Int32)); | ||
744 | createCol(shapes, "PathScaleY", typeof(System.Int32)); | ||
745 | createCol(shapes, "PathShearX", typeof(System.Int32)); | ||
746 | createCol(shapes, "PathShearY", typeof(System.Int32)); | ||
747 | createCol(shapes, "PathSkew", typeof(System.Int32)); | ||
748 | createCol(shapes, "PathCurve", typeof(System.Int32)); | ||
749 | createCol(shapes, "PathRadiusOffset", typeof(System.Int32)); | ||
750 | createCol(shapes, "PathRevolutions", typeof(System.Int32)); | ||
751 | createCol(shapes, "PathTaperX", typeof(System.Int32)); | ||
752 | createCol(shapes, "PathTaperY", typeof(System.Int32)); | ||
753 | createCol(shapes, "PathTwist", typeof(System.Int32)); | ||
754 | createCol(shapes, "PathTwistBegin", typeof(System.Int32)); | ||
755 | // profile | ||
756 | createCol(shapes, "ProfileBegin", typeof(System.Int32)); | ||
757 | createCol(shapes, "ProfileEnd", typeof(System.Int32)); | ||
758 | createCol(shapes, "ProfileCurve", typeof(System.Int32)); | ||
759 | createCol(shapes, "ProfileHollow", typeof(System.Int32)); | ||
760 | // text TODO: this isn't right, but I'm not sure the right | ||
761 | // way to specify this as a blob atm | ||
762 | createCol(shapes, "Texture", typeof(System.Byte[])); | ||
763 | createCol(shapes, "ExtraParams", typeof(System.Byte[])); | ||
764 | |||
765 | shapes.PrimaryKey = new DataColumn[] { shapes.Columns["UUID"] }; | ||
766 | |||
767 | return shapes; | ||
768 | } | 813 | } |
769 | } | 814 | } |
770 | } | 815 | } |