diff options
Diffstat (limited to '')
-rw-r--r-- | OpenSim/Framework/Data.MySQL/MySQLDataStore.cs | 1035 |
1 files changed, 1035 insertions, 0 deletions
diff --git a/OpenSim/Framework/Data.MySQL/MySQLDataStore.cs b/OpenSim/Framework/Data.MySQL/MySQLDataStore.cs new file mode 100644 index 0000000..c468414 --- /dev/null +++ b/OpenSim/Framework/Data.MySQL/MySQLDataStore.cs | |||
@@ -0,0 +1,1035 @@ | |||
1 | using System; | ||
2 | using System.Collections.Generic; | ||
3 | using System.Data; | ||
4 | using System.Diagnostics; | ||
5 | using System.IO; | ||
6 | using System.Text; | ||
7 | using libsecondlife; | ||
8 | using MySql.Data.MySqlClient; | ||
9 | using OpenSim.Framework.Console; | ||
10 | using OpenSim.Region.Environment.Interfaces; | ||
11 | using OpenSim.Region.Environment.LandManagement; | ||
12 | using OpenSim.Region.Environment.Scenes; | ||
13 | using System.Data.SqlClient; | ||
14 | using System.Data.Common; | ||
15 | |||
16 | namespace OpenSim.Framework.Data.MySQL | ||
17 | { | ||
18 | public class MySQLDataStore : IRegionDataStore | ||
19 | { | ||
20 | private const string m_primSelect = "select * from prims"; | ||
21 | private const string m_shapeSelect = "select * from primshapes"; | ||
22 | private const string m_terrainSelect = "select * from terrain limit 1"; | ||
23 | |||
24 | private DataSet m_dataSet; | ||
25 | private MySqlDataAdapter m_primDataAdapter; | ||
26 | private MySqlDataAdapter m_shapeDataAdapter; | ||
27 | private MySqlConnection m_connection; | ||
28 | private MySqlDataAdapter m_terrainDataAdapter; | ||
29 | private DataTable m_primTable; | ||
30 | private DataTable m_shapeTable; | ||
31 | private DataTable m_terrainTable; | ||
32 | |||
33 | /*********************************************************************** | ||
34 | * | ||
35 | * Public Interface Functions | ||
36 | * | ||
37 | **********************************************************************/ | ||
38 | |||
39 | public void Initialise(string connectionstring, string dbname) | ||
40 | { | ||
41 | m_dataSet = new DataSet(); | ||
42 | |||
43 | MainLog.Instance.Verbose("DATASTORE", "MySql - connecting: " + connectionstring); | ||
44 | m_connection = new MySqlConnection(connectionstring); | ||
45 | |||
46 | MySqlCommand primSelectCmd = new MySqlCommand(m_primSelect, m_connection); | ||
47 | m_primDataAdapter = new MySqlDataAdapter(primSelectCmd); | ||
48 | |||
49 | MySqlCommand shapeSelectCmd = new MySqlCommand(m_shapeSelect, m_connection); | ||
50 | m_shapeDataAdapter = new MySqlDataAdapter(shapeSelectCmd); | ||
51 | |||
52 | MySqlCommand terrainSelectCmd = new MySqlCommand(m_terrainSelect, m_connection); | ||
53 | m_terrainDataAdapter = new MySqlDataAdapter(terrainSelectCmd); | ||
54 | |||
55 | TestTables(m_connection); | ||
56 | |||
57 | lock (m_dataSet) | ||
58 | { | ||
59 | m_primTable = createPrimTable(); | ||
60 | m_dataSet.Tables.Add(m_primTable); | ||
61 | SetupPrimCommands(m_primDataAdapter, m_connection); | ||
62 | m_primDataAdapter.Fill(m_primTable); | ||
63 | |||
64 | m_shapeTable = createShapeTable(); | ||
65 | m_dataSet.Tables.Add(m_shapeTable); | ||
66 | SetupShapeCommands(m_shapeDataAdapter, m_connection); | ||
67 | m_shapeDataAdapter.Fill(m_shapeTable); | ||
68 | |||
69 | m_terrainTable = createTerrainTable(); | ||
70 | m_dataSet.Tables.Add(m_terrainTable); | ||
71 | SetupTerrainCommands(m_terrainDataAdapter, m_connection); | ||
72 | m_terrainDataAdapter.Fill(m_terrainTable); | ||
73 | } | ||
74 | } | ||
75 | |||
76 | public void StoreObject(SceneObjectGroup obj, LLUUID regionUUID) | ||
77 | { | ||
78 | lock (m_dataSet) | ||
79 | { | ||
80 | foreach (SceneObjectPart prim in obj.Children.Values) | ||
81 | { | ||
82 | if ((prim.ObjectFlags & (uint)LLObject.ObjectFlags.Physics) == 0) | ||
83 | { | ||
84 | MainLog.Instance.Verbose("DATASTORE", "Adding obj: " + obj.UUID + " to region: " + regionUUID); | ||
85 | addPrim(prim, obj.UUID, regionUUID); | ||
86 | } | ||
87 | else | ||
88 | { | ||
89 | // MainLog.Instance.Verbose("DATASTORE", "Ignoring Physical obj: " + obj.UUID + " in region: " + regionUUID); | ||
90 | } | ||
91 | } | ||
92 | } | ||
93 | |||
94 | Commit(); | ||
95 | } | ||
96 | |||
97 | public void RemoveObject(LLUUID obj, LLUUID regionUUID) | ||
98 | { | ||
99 | DataTable prims = m_primTable; | ||
100 | DataTable shapes = m_shapeTable; | ||
101 | |||
102 | string selectExp = "SceneGroupID = '" + obj.ToString() + "'"; | ||
103 | lock (m_dataSet) | ||
104 | { | ||
105 | DataRow[] primRows = prims.Select(selectExp); | ||
106 | foreach (DataRow row in primRows) | ||
107 | { | ||
108 | LLUUID uuid = new LLUUID((string)row["UUID"]); | ||
109 | DataRow shapeRow = shapes.Rows.Find(uuid); | ||
110 | if (shapeRow != null) | ||
111 | { | ||
112 | shapeRow.Delete(); | ||
113 | } | ||
114 | row.Delete(); | ||
115 | } | ||
116 | } | ||
117 | |||
118 | Commit(); | ||
119 | } | ||
120 | |||
121 | public List<SceneObjectGroup> LoadObjects(LLUUID regionUUID) | ||
122 | { | ||
123 | Dictionary<LLUUID, SceneObjectGroup> createdObjects = new Dictionary<LLUUID, SceneObjectGroup>(); | ||
124 | |||
125 | List<SceneObjectGroup> retvals = new List<SceneObjectGroup>(); | ||
126 | |||
127 | DataTable prims = m_primTable; | ||
128 | DataTable shapes = m_shapeTable; | ||
129 | |||
130 | string byRegion = "RegionUUID = '" + regionUUID.ToString() + "'"; | ||
131 | string orderByParent = "ParentID ASC"; | ||
132 | |||
133 | lock (m_dataSet) | ||
134 | { | ||
135 | DataRow[] primsForRegion = prims.Select(byRegion, orderByParent); | ||
136 | MainLog.Instance.Verbose("DATASTORE", | ||
137 | "Loaded " + primsForRegion.Length + " prims for region: " + regionUUID); | ||
138 | |||
139 | foreach (DataRow primRow in primsForRegion) | ||
140 | { | ||
141 | try | ||
142 | { | ||
143 | string uuid = (string)primRow["UUID"]; | ||
144 | string objID = (string)primRow["SceneGroupID"]; | ||
145 | if (uuid == objID) //is new SceneObjectGroup ? | ||
146 | { | ||
147 | SceneObjectGroup group = new SceneObjectGroup(); | ||
148 | SceneObjectPart prim = buildPrim(primRow); | ||
149 | DataRow shapeRow = shapes.Rows.Find(prim.UUID); | ||
150 | if (shapeRow != null) | ||
151 | { | ||
152 | prim.Shape = buildShape(shapeRow); | ||
153 | } | ||
154 | else | ||
155 | { | ||
156 | MainLog.Instance.Notice( | ||
157 | "No shape found for prim in storage, so setting default box shape"); | ||
158 | prim.Shape = BoxShape.Default; | ||
159 | } | ||
160 | group.AddPart(prim); | ||
161 | group.RootPart = prim; | ||
162 | |||
163 | createdObjects.Add(group.UUID, group); | ||
164 | retvals.Add(group); | ||
165 | } | ||
166 | else | ||
167 | { | ||
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 | MainLog.Instance.Notice( | ||
177 | "No shape found for prim in storage, so setting default box shape"); | ||
178 | prim.Shape = BoxShape.Default; | ||
179 | } | ||
180 | createdObjects[new LLUUID(objID)].AddPart(prim); | ||
181 | } | ||
182 | } | ||
183 | catch (Exception e) | ||
184 | { | ||
185 | MainLog.Instance.Error("DATASTORE", "Failed create prim object, exception and data follows"); | ||
186 | MainLog.Instance.Verbose(e.ToString()); | ||
187 | foreach (DataColumn col in prims.Columns) | ||
188 | { | ||
189 | MainLog.Instance.Verbose("Col: " + col.ColumnName + " => " + primRow[col]); | ||
190 | } | ||
191 | } | ||
192 | } | ||
193 | } | ||
194 | return retvals; | ||
195 | } | ||
196 | |||
197 | |||
198 | public void StoreTerrain(double[,] ter, LLUUID regionID) | ||
199 | { | ||
200 | int revision = Util.UnixTimeSinceEpoch(); | ||
201 | MainLog.Instance.Verbose("DATASTORE", "Storing terrain revision r" + revision.ToString()); | ||
202 | |||
203 | DataTable terrain = m_dataSet.Tables["terrain"]; | ||
204 | lock (m_dataSet) | ||
205 | { | ||
206 | MySqlCommand cmd = new MySqlCommand("insert into terrain(RegionUUID, Revision, Heightfield)" + | ||
207 | " values(?RegionUUID, ?Revision, ?Heightfield)", m_connection); | ||
208 | using (cmd) | ||
209 | { | ||
210 | |||
211 | cmd.Parameters.Add(new MySqlParameter("?RegionUUID", regionID.ToString())); | ||
212 | cmd.Parameters.Add(new MySqlParameter("?Revision", revision)); | ||
213 | cmd.Parameters.Add(new MySqlParameter("?Heightfield", serializeTerrain(ter))); | ||
214 | cmd.ExecuteNonQuery(); | ||
215 | } | ||
216 | } | ||
217 | } | ||
218 | |||
219 | public double[,] LoadTerrain(LLUUID regionID) | ||
220 | { | ||
221 | double[,] terret = new double[256, 256]; | ||
222 | terret.Initialize(); | ||
223 | |||
224 | MySqlCommand cmd = new MySqlCommand( | ||
225 | @"select RegionUUID, Revision, Heightfield from terrain | ||
226 | where RegionUUID=?RegionUUID order by Revision desc limit 1" | ||
227 | , m_connection); | ||
228 | |||
229 | MySqlParameter param = new MySqlParameter(); | ||
230 | cmd.Parameters.Add(new MySqlParameter("?RegionUUID", regionID.ToString())); | ||
231 | |||
232 | if (m_connection.State != ConnectionState.Open) | ||
233 | { | ||
234 | m_connection.Open(); | ||
235 | } | ||
236 | |||
237 | using (MySqlDataReader row = cmd.ExecuteReader()) | ||
238 | { | ||
239 | int rev = 0; | ||
240 | if (row.Read()) | ||
241 | { | ||
242 | byte[] heightmap = (byte[])row["Heightfield"]; | ||
243 | for (int x = 0; x < 256; x++) | ||
244 | { | ||
245 | for (int y = 0; y < 256; y++) | ||
246 | { | ||
247 | terret[x, y] = BitConverter.ToDouble(heightmap, ((x * 256) + y) * 8); | ||
248 | } | ||
249 | } | ||
250 | rev = (int)row["Revision"]; | ||
251 | } | ||
252 | else | ||
253 | { | ||
254 | MainLog.Instance.Verbose("DATASTORE", "No terrain found for region"); | ||
255 | return null; | ||
256 | } | ||
257 | |||
258 | MainLog.Instance.Verbose("DATASTORE", "Loaded terrain revision r" + rev.ToString()); | ||
259 | } | ||
260 | |||
261 | return terret; | ||
262 | } | ||
263 | |||
264 | public void RemoveLandObject(uint id) | ||
265 | { | ||
266 | } | ||
267 | |||
268 | public void StoreParcel(Land parcel) | ||
269 | { | ||
270 | } | ||
271 | |||
272 | public List<Land> LoadLandObjects() | ||
273 | { | ||
274 | return new List<Land>(); | ||
275 | } | ||
276 | |||
277 | private void DisplayDataSet(DataSet ds, string title) | ||
278 | { | ||
279 | Debug.WriteLine(title); | ||
280 | //--- Loop through the DataTables | ||
281 | foreach (DataTable table in ds.Tables) | ||
282 | { | ||
283 | Debug.WriteLine("*** DataTable: " + table.TableName + "***"); | ||
284 | //--- Loop through each DataTable's DataRows | ||
285 | foreach (DataRow row in table.Rows) | ||
286 | { | ||
287 | //--- Display the original values, if there are any. | ||
288 | if (row.HasVersion(System.Data.DataRowVersion.Original)) | ||
289 | { | ||
290 | Debug.Write("Original Row Values ===> "); | ||
291 | foreach (DataColumn column in table.Columns) | ||
292 | Debug.Write(column.ColumnName + " = " + | ||
293 | row[column, DataRowVersion.Original] + ", "); | ||
294 | Debug.WriteLine(""); | ||
295 | } | ||
296 | //--- Display the current values, if there are any. | ||
297 | if (row.HasVersion(System.Data.DataRowVersion.Current)) | ||
298 | { | ||
299 | Debug.Write("Current Row Values ====> "); | ||
300 | foreach (DataColumn column in table.Columns) | ||
301 | Debug.Write(column.ColumnName + " = " + | ||
302 | row[column, DataRowVersion.Current] + ", "); | ||
303 | Debug.WriteLine(""); | ||
304 | } | ||
305 | Debug.WriteLine(""); | ||
306 | } | ||
307 | } | ||
308 | } | ||
309 | |||
310 | //private void DisplayTableMappings( MySqlDataAdapter adapter ) | ||
311 | //{ | ||
312 | // DataTableMappingCollection mappings = adapter.TableMappings; | ||
313 | |||
314 | // foreach( DataTableMapping mapping in mappings ) | ||
315 | // { | ||
316 | // Debug.WriteLine( String.Format( "Source Table: ", mapping.SourceTable )); | ||
317 | |||
318 | // DataColumnMappingCollection columnMappings = mapping.ColumnMappings; | ||
319 | |||
320 | // foreach (DataColumnMapping columnMapping in columnMappings) | ||
321 | // { | ||
322 | // Debug.WriteLine( String.Format( "DataSet [{0}] <-> Source [{1}]", columnMapping.DataSetColumn, columnMapping.SourceColumn )); | ||
323 | // } | ||
324 | // } | ||
325 | //} | ||
326 | |||
327 | public void Commit() | ||
328 | { | ||
329 | if (m_connection.State != ConnectionState.Open) | ||
330 | { | ||
331 | m_connection.Open(); | ||
332 | } | ||
333 | |||
334 | lock (m_dataSet) | ||
335 | { | ||
336 | DisplayDataSet(m_dataSet, "Region DataSet"); | ||
337 | //DisplayTableMappings(m_primDataAdapter); | ||
338 | |||
339 | // m_primDataAdapter.MissingMappingAction = MissingMappingAction.Error; | ||
340 | |||
341 | m_primDataAdapter.Update(m_primTable); | ||
342 | m_shapeDataAdapter.Update(m_shapeTable); | ||
343 | m_terrainDataAdapter.Update(m_terrainTable); | ||
344 | |||
345 | m_dataSet.AcceptChanges(); | ||
346 | } | ||
347 | } | ||
348 | |||
349 | public void Shutdown() | ||
350 | { | ||
351 | Commit(); | ||
352 | } | ||
353 | |||
354 | /*********************************************************************** | ||
355 | * | ||
356 | * Database Definition Functions | ||
357 | * | ||
358 | * This should be db agnostic as we define them in ADO.NET terms | ||
359 | * | ||
360 | **********************************************************************/ | ||
361 | |||
362 | private DataColumn createCol(DataTable dt, string name, Type type) | ||
363 | { | ||
364 | DataColumn col = new DataColumn(name, type); | ||
365 | dt.Columns.Add(col); | ||
366 | return col; | ||
367 | } | ||
368 | |||
369 | private DataTable createTerrainTable() | ||
370 | { | ||
371 | DataTable terrain = new DataTable("terrain"); | ||
372 | |||
373 | createCol(terrain, "RegionUUID", typeof(String)); | ||
374 | createCol(terrain, "Revision", typeof(Int32)); | ||
375 | DataColumn heightField = createCol(terrain, "Heightfield", typeof(Byte[])); | ||
376 | return terrain; | ||
377 | } | ||
378 | |||
379 | private DataTable createPrimTable() | ||
380 | { | ||
381 | DataTable prims = new DataTable("prims"); | ||
382 | |||
383 | createCol(prims, "UUID", typeof(String)); | ||
384 | createCol(prims, "RegionUUID", typeof(String)); | ||
385 | createCol(prims, "ParentID", typeof(Int32)); | ||
386 | createCol(prims, "CreationDate", typeof(Int32)); | ||
387 | createCol(prims, "Name", typeof(String)); | ||
388 | createCol(prims, "SceneGroupID", typeof(String)); | ||
389 | // various text fields | ||
390 | createCol(prims, "Text", typeof(String)); | ||
391 | createCol(prims, "Description", typeof(String)); | ||
392 | createCol(prims, "SitName", typeof(String)); | ||
393 | createCol(prims, "TouchName", typeof(String)); | ||
394 | // permissions | ||
395 | createCol(prims, "ObjectFlags", typeof(Int32)); | ||
396 | createCol(prims, "CreatorID", typeof(String)); | ||
397 | createCol(prims, "OwnerID", typeof(String)); | ||
398 | createCol(prims, "GroupID", typeof(String)); | ||
399 | createCol(prims, "LastOwnerID", typeof(String)); | ||
400 | createCol(prims, "OwnerMask", typeof(Int32)); | ||
401 | createCol(prims, "NextOwnerMask", typeof(Int32)); | ||
402 | createCol(prims, "GroupMask", typeof(Int32)); | ||
403 | createCol(prims, "EveryoneMask", typeof(Int32)); | ||
404 | createCol(prims, "BaseMask", typeof(Int32)); | ||
405 | // vectors | ||
406 | createCol(prims, "PositionX", typeof(Double)); | ||
407 | createCol(prims, "PositionY", typeof(Double)); | ||
408 | createCol(prims, "PositionZ", typeof(Double)); | ||
409 | createCol(prims, "GroupPositionX", typeof(Double)); | ||
410 | createCol(prims, "GroupPositionY", typeof(Double)); | ||
411 | createCol(prims, "GroupPositionZ", typeof(Double)); | ||
412 | createCol(prims, "VelocityX", typeof(Double)); | ||
413 | createCol(prims, "VelocityY", typeof(Double)); | ||
414 | createCol(prims, "VelocityZ", typeof(Double)); | ||
415 | createCol(prims, "AngularVelocityX", typeof(Double)); | ||
416 | createCol(prims, "AngularVelocityY", typeof(Double)); | ||
417 | createCol(prims, "AngularVelocityZ", typeof(Double)); | ||
418 | createCol(prims, "AccelerationX", typeof(Double)); | ||
419 | createCol(prims, "AccelerationY", typeof(Double)); | ||
420 | createCol(prims, "AccelerationZ", typeof(Double)); | ||
421 | // quaternions | ||
422 | createCol(prims, "RotationX", typeof(Double)); | ||
423 | createCol(prims, "RotationY", typeof(Double)); | ||
424 | createCol(prims, "RotationZ", typeof(Double)); | ||
425 | createCol(prims, "RotationW", typeof(Double)); | ||
426 | |||
427 | // Add in contraints | ||
428 | prims.PrimaryKey = new DataColumn[] { prims.Columns["UUID"] }; | ||
429 | |||
430 | return prims; | ||
431 | } | ||
432 | |||
433 | private DataTable createShapeTable() | ||
434 | { | ||
435 | DataTable shapes = new DataTable("primshapes"); | ||
436 | createCol(shapes, "UUID", typeof(String)); | ||
437 | // shape is an enum | ||
438 | createCol(shapes, "Shape", typeof(Int32)); | ||
439 | // vectors | ||
440 | createCol(shapes, "ScaleX", typeof(Double)); | ||
441 | createCol(shapes, "ScaleY", typeof(Double)); | ||
442 | createCol(shapes, "ScaleZ", typeof(Double)); | ||
443 | // paths | ||
444 | createCol(shapes, "PCode", typeof(Int32)); | ||
445 | createCol(shapes, "PathBegin", typeof(Int32)); | ||
446 | createCol(shapes, "PathEnd", typeof(Int32)); | ||
447 | createCol(shapes, "PathScaleX", typeof(Int32)); | ||
448 | createCol(shapes, "PathScaleY", typeof(Int32)); | ||
449 | createCol(shapes, "PathShearX", typeof(Int32)); | ||
450 | createCol(shapes, "PathShearY", typeof(Int32)); | ||
451 | createCol(shapes, "PathSkew", typeof(Int32)); | ||
452 | createCol(shapes, "PathCurve", typeof(Int32)); | ||
453 | createCol(shapes, "PathRadiusOffset", typeof(Int32)); | ||
454 | createCol(shapes, "PathRevolutions", typeof(Int32)); | ||
455 | createCol(shapes, "PathTaperX", typeof(Int32)); | ||
456 | createCol(shapes, "PathTaperY", typeof(Int32)); | ||
457 | createCol(shapes, "PathTwist", typeof(Int32)); | ||
458 | createCol(shapes, "PathTwistBegin", typeof(Int32)); | ||
459 | // profile | ||
460 | createCol(shapes, "ProfileBegin", typeof(Int32)); | ||
461 | createCol(shapes, "ProfileEnd", typeof(Int32)); | ||
462 | createCol(shapes, "ProfileCurve", typeof(Int32)); | ||
463 | createCol(shapes, "ProfileHollow", typeof(Int32)); | ||
464 | createCol(shapes, "Texture", typeof(Byte[])); | ||
465 | createCol(shapes, "ExtraParams", typeof(Byte[])); | ||
466 | |||
467 | shapes.PrimaryKey = new DataColumn[] { shapes.Columns["UUID"] }; | ||
468 | |||
469 | return shapes; | ||
470 | } | ||
471 | |||
472 | /*********************************************************************** | ||
473 | * | ||
474 | * Convert between ADO.NET <=> OpenSim Objects | ||
475 | * | ||
476 | * These should be database independant | ||
477 | * | ||
478 | **********************************************************************/ | ||
479 | |||
480 | private SceneObjectPart buildPrim(DataRow row) | ||
481 | { | ||
482 | // TODO: this doesn't work yet because something more | ||
483 | // interesting has to be done to actually get these values | ||
484 | // back out. Not enough time to figure it out yet. | ||
485 | SceneObjectPart prim = new SceneObjectPart(); | ||
486 | prim.UUID = new LLUUID((String)row["UUID"]); | ||
487 | // explicit conversion of integers is required, which sort | ||
488 | // of sucks. No idea if there is a shortcut here or not. | ||
489 | prim.ParentID = Convert.ToUInt32(row["ParentID"]); | ||
490 | prim.CreationDate = Convert.ToInt32(row["CreationDate"]); | ||
491 | prim.Name = (String)row["Name"]; | ||
492 | // various text fields | ||
493 | prim.Text = (String)row["Text"]; | ||
494 | prim.Description = (String)row["Description"]; | ||
495 | prim.SitName = (String)row["SitName"]; | ||
496 | prim.TouchName = (String)row["TouchName"]; | ||
497 | // permissions | ||
498 | prim.ObjectFlags = Convert.ToUInt32(row["ObjectFlags"]); | ||
499 | prim.CreatorID = new LLUUID((String)row["CreatorID"]); | ||
500 | prim.OwnerID = new LLUUID((String)row["OwnerID"]); | ||
501 | prim.GroupID = new LLUUID((String)row["GroupID"]); | ||
502 | prim.LastOwnerID = new LLUUID((String)row["LastOwnerID"]); | ||
503 | prim.OwnerMask = Convert.ToUInt32(row["OwnerMask"]); | ||
504 | prim.NextOwnerMask = Convert.ToUInt32(row["NextOwnerMask"]); | ||
505 | prim.GroupMask = Convert.ToUInt32(row["GroupMask"]); | ||
506 | prim.EveryoneMask = Convert.ToUInt32(row["EveryoneMask"]); | ||
507 | prim.BaseMask = Convert.ToUInt32(row["BaseMask"]); | ||
508 | // vectors | ||
509 | prim.OffsetPosition = new LLVector3( | ||
510 | Convert.ToSingle(row["PositionX"]), | ||
511 | Convert.ToSingle(row["PositionY"]), | ||
512 | Convert.ToSingle(row["PositionZ"]) | ||
513 | ); | ||
514 | prim.GroupPosition = new LLVector3( | ||
515 | Convert.ToSingle(row["GroupPositionX"]), | ||
516 | Convert.ToSingle(row["GroupPositionY"]), | ||
517 | Convert.ToSingle(row["GroupPositionZ"]) | ||
518 | ); | ||
519 | prim.Velocity = new LLVector3( | ||
520 | Convert.ToSingle(row["VelocityX"]), | ||
521 | Convert.ToSingle(row["VelocityY"]), | ||
522 | Convert.ToSingle(row["VelocityZ"]) | ||
523 | ); | ||
524 | prim.AngularVelocity = new LLVector3( | ||
525 | Convert.ToSingle(row["AngularVelocityX"]), | ||
526 | Convert.ToSingle(row["AngularVelocityY"]), | ||
527 | Convert.ToSingle(row["AngularVelocityZ"]) | ||
528 | ); | ||
529 | prim.Acceleration = new LLVector3( | ||
530 | Convert.ToSingle(row["AccelerationX"]), | ||
531 | Convert.ToSingle(row["AccelerationY"]), | ||
532 | Convert.ToSingle(row["AccelerationZ"]) | ||
533 | ); | ||
534 | // quaternions | ||
535 | prim.RotationOffset = new LLQuaternion( | ||
536 | Convert.ToSingle(row["RotationX"]), | ||
537 | Convert.ToSingle(row["RotationY"]), | ||
538 | Convert.ToSingle(row["RotationZ"]), | ||
539 | Convert.ToSingle(row["RotationW"]) | ||
540 | ); | ||
541 | |||
542 | return prim; | ||
543 | } | ||
544 | |||
545 | private Array serializeTerrain(double[,] val) | ||
546 | { | ||
547 | MemoryStream str = new MemoryStream(65536 * sizeof(double)); | ||
548 | BinaryWriter bw = new BinaryWriter(str); | ||
549 | |||
550 | // TODO: COMPATIBILITY - Add byte-order conversions | ||
551 | for (int x = 0; x < 256; x++) | ||
552 | for (int y = 0; y < 256; y++) | ||
553 | bw.Write(val[x, y]); | ||
554 | |||
555 | return str.ToArray(); | ||
556 | } | ||
557 | |||
558 | private void fillPrimRow(DataRow row, SceneObjectPart prim, LLUUID sceneGroupID, LLUUID regionUUID) | ||
559 | { | ||
560 | row["UUID"] = prim.UUID; | ||
561 | row["RegionUUID"] = regionUUID; | ||
562 | row["ParentID"] = prim.ParentID; | ||
563 | row["CreationDate"] = prim.CreationDate; | ||
564 | row["Name"] = prim.Name; | ||
565 | row["SceneGroupID"] = sceneGroupID; // the UUID of the root part for this SceneObjectGroup | ||
566 | // various text fields | ||
567 | row["Text"] = prim.Text; | ||
568 | row["Description"] = prim.Description; | ||
569 | row["SitName"] = prim.SitName; | ||
570 | row["TouchName"] = prim.TouchName; | ||
571 | // permissions | ||
572 | row["ObjectFlags"] = prim.ObjectFlags; | ||
573 | row["CreatorID"] = prim.CreatorID; | ||
574 | row["OwnerID"] = prim.OwnerID; | ||
575 | row["GroupID"] = prim.GroupID; | ||
576 | row["LastOwnerID"] = prim.LastOwnerID; | ||
577 | row["OwnerMask"] = prim.OwnerMask; | ||
578 | row["NextOwnerMask"] = prim.NextOwnerMask; | ||
579 | row["GroupMask"] = prim.GroupMask; | ||
580 | row["EveryoneMask"] = prim.EveryoneMask; | ||
581 | row["BaseMask"] = prim.BaseMask; | ||
582 | // vectors | ||
583 | row["PositionX"] = prim.OffsetPosition.X; | ||
584 | row["PositionY"] = prim.OffsetPosition.Y; | ||
585 | row["PositionZ"] = prim.OffsetPosition.Z; | ||
586 | row["GroupPositionX"] = prim.GroupPosition.X; | ||
587 | row["GroupPositionY"] = prim.GroupPosition.Y; | ||
588 | row["GroupPositionZ"] = prim.GroupPosition.Z; | ||
589 | row["VelocityX"] = prim.Velocity.X; | ||
590 | row["VelocityY"] = prim.Velocity.Y; | ||
591 | row["VelocityZ"] = prim.Velocity.Z; | ||
592 | row["AngularVelocityX"] = prim.AngularVelocity.X; | ||
593 | row["AngularVelocityY"] = prim.AngularVelocity.Y; | ||
594 | row["AngularVelocityZ"] = prim.AngularVelocity.Z; | ||
595 | row["AccelerationX"] = prim.Acceleration.X; | ||
596 | row["AccelerationY"] = prim.Acceleration.Y; | ||
597 | row["AccelerationZ"] = prim.Acceleration.Z; | ||
598 | // quaternions | ||
599 | row["RotationX"] = prim.RotationOffset.X; | ||
600 | row["RotationY"] = prim.RotationOffset.Y; | ||
601 | row["RotationZ"] = prim.RotationOffset.Z; | ||
602 | row["RotationW"] = prim.RotationOffset.W; | ||
603 | } | ||
604 | |||
605 | private PrimitiveBaseShape buildShape(DataRow row) | ||
606 | { | ||
607 | PrimitiveBaseShape s = new PrimitiveBaseShape(); | ||
608 | s.Scale = new LLVector3( | ||
609 | Convert.ToSingle(row["ScaleX"]), | ||
610 | Convert.ToSingle(row["ScaleY"]), | ||
611 | Convert.ToSingle(row["ScaleZ"]) | ||
612 | ); | ||
613 | // paths | ||
614 | s.PCode = Convert.ToByte(row["PCode"]); | ||
615 | s.PathBegin = Convert.ToUInt16(row["PathBegin"]); | ||
616 | s.PathEnd = Convert.ToUInt16(row["PathEnd"]); | ||
617 | s.PathScaleX = Convert.ToByte(row["PathScaleX"]); | ||
618 | s.PathScaleY = Convert.ToByte(row["PathScaleY"]); | ||
619 | s.PathShearX = Convert.ToByte(row["PathShearX"]); | ||
620 | s.PathShearY = Convert.ToByte(row["PathShearY"]); | ||
621 | s.PathSkew = Convert.ToSByte(row["PathSkew"]); | ||
622 | s.PathCurve = Convert.ToByte(row["PathCurve"]); | ||
623 | s.PathRadiusOffset = Convert.ToSByte(row["PathRadiusOffset"]); | ||
624 | s.PathRevolutions = Convert.ToByte(row["PathRevolutions"]); | ||
625 | s.PathTaperX = Convert.ToSByte(row["PathTaperX"]); | ||
626 | s.PathTaperY = Convert.ToSByte(row["PathTaperY"]); | ||
627 | s.PathTwist = Convert.ToSByte(row["PathTwist"]); | ||
628 | s.PathTwistBegin = Convert.ToSByte(row["PathTwistBegin"]); | ||
629 | // profile | ||
630 | s.ProfileBegin = Convert.ToUInt16(row["ProfileBegin"]); | ||
631 | s.ProfileEnd = Convert.ToUInt16(row["ProfileEnd"]); | ||
632 | s.ProfileCurve = Convert.ToByte(row["ProfileCurve"]); | ||
633 | s.ProfileHollow = Convert.ToUInt16(row["ProfileHollow"]); | ||
634 | s.TextureEntry = (byte[])row["Texture"]; | ||
635 | s.ExtraParams = (byte[])row["ExtraParams"]; | ||
636 | |||
637 | return s; | ||
638 | } | ||
639 | |||
640 | private void fillShapeRow(DataRow row, SceneObjectPart prim) | ||
641 | { | ||
642 | PrimitiveBaseShape s = prim.Shape; | ||
643 | row["UUID"] = prim.UUID; | ||
644 | // shape is an enum | ||
645 | row["Shape"] = 0; | ||
646 | // vectors | ||
647 | row["ScaleX"] = s.Scale.X; | ||
648 | row["ScaleY"] = s.Scale.Y; | ||
649 | row["ScaleZ"] = s.Scale.Z; | ||
650 | // paths | ||
651 | row["PCode"] = s.PCode; | ||
652 | row["PathBegin"] = s.PathBegin; | ||
653 | row["PathEnd"] = s.PathEnd; | ||
654 | row["PathScaleX"] = s.PathScaleX; | ||
655 | row["PathScaleY"] = s.PathScaleY; | ||
656 | row["PathShearX"] = s.PathShearX; | ||
657 | row["PathShearY"] = s.PathShearY; | ||
658 | row["PathSkew"] = s.PathSkew; | ||
659 | row["PathCurve"] = s.PathCurve; | ||
660 | row["PathRadiusOffset"] = s.PathRadiusOffset; | ||
661 | row["PathRevolutions"] = s.PathRevolutions; | ||
662 | row["PathTaperX"] = s.PathTaperX; | ||
663 | row["PathTaperY"] = s.PathTaperY; | ||
664 | row["PathTwist"] = s.PathTwist; | ||
665 | row["PathTwistBegin"] = s.PathTwistBegin; | ||
666 | // profile | ||
667 | row["ProfileBegin"] = s.ProfileBegin; | ||
668 | row["ProfileEnd"] = s.ProfileEnd; | ||
669 | row["ProfileCurve"] = s.ProfileCurve; | ||
670 | row["ProfileHollow"] = s.ProfileHollow; | ||
671 | row["Texture"] = s.TextureEntry; | ||
672 | row["ExtraParams"] = s.ExtraParams; | ||
673 | } | ||
674 | |||
675 | private void addPrim(SceneObjectPart prim, LLUUID sceneGroupID, LLUUID regionUUID) | ||
676 | { | ||
677 | DataTable prims = m_dataSet.Tables["prims"]; | ||
678 | DataTable shapes = m_dataSet.Tables["primshapes"]; | ||
679 | |||
680 | DataRow primRow = prims.Rows.Find(prim.UUID); | ||
681 | if (primRow == null) | ||
682 | { | ||
683 | primRow = prims.NewRow(); | ||
684 | fillPrimRow(primRow, prim, sceneGroupID, regionUUID); | ||
685 | prims.Rows.Add(primRow); | ||
686 | } | ||
687 | else | ||
688 | { | ||
689 | fillPrimRow(primRow, prim, sceneGroupID, regionUUID); | ||
690 | } | ||
691 | |||
692 | DataRow shapeRow = shapes.Rows.Find(prim.UUID); | ||
693 | if (shapeRow == null) | ||
694 | { | ||
695 | shapeRow = shapes.NewRow(); | ||
696 | fillShapeRow(shapeRow, prim); | ||
697 | shapes.Rows.Add(shapeRow); | ||
698 | } | ||
699 | else | ||
700 | { | ||
701 | fillShapeRow(shapeRow, prim); | ||
702 | } | ||
703 | } | ||
704 | |||
705 | /*********************************************************************** | ||
706 | * | ||
707 | * SQL Statement Creation Functions | ||
708 | * | ||
709 | * These functions create SQL statements for update, insert, and create. | ||
710 | * They can probably be factored later to have a db independant | ||
711 | * portion and a db specific portion | ||
712 | * | ||
713 | **********************************************************************/ | ||
714 | |||
715 | private MySqlCommand createInsertCommand(string table, DataTable dt) | ||
716 | { | ||
717 | /** | ||
718 | * This is subtle enough to deserve some commentary. | ||
719 | * Instead of doing *lots* and *lots of hardcoded strings | ||
720 | * for database definitions we'll use the fact that | ||
721 | * realistically all insert statements look like "insert | ||
722 | * into A(b, c) values(:b, :c) on the parameterized query | ||
723 | * front. If we just have a list of b, c, etc... we can | ||
724 | * generate these strings instead of typing them out. | ||
725 | */ | ||
726 | string[] cols = new string[dt.Columns.Count]; | ||
727 | for (int i = 0; i < dt.Columns.Count; i++) | ||
728 | { | ||
729 | DataColumn col = dt.Columns[i]; | ||
730 | cols[i] = col.ColumnName; | ||
731 | } | ||
732 | |||
733 | string sql = "insert into " + table + "("; | ||
734 | sql += String.Join(", ", cols); | ||
735 | // important, the first ':' needs to be here, the rest get added in the join | ||
736 | sql += ") values (?"; | ||
737 | sql += String.Join(", ?", cols); | ||
738 | sql += ")"; | ||
739 | MySqlCommand cmd = new MySqlCommand(sql); | ||
740 | |||
741 | // this provides the binding for all our parameters, so | ||
742 | // much less code than it used to be | ||
743 | foreach (DataColumn col in dt.Columns) | ||
744 | { | ||
745 | cmd.Parameters.Add(createMySqlParameter(col.ColumnName, col.DataType)); | ||
746 | } | ||
747 | return cmd; | ||
748 | } | ||
749 | |||
750 | private MySqlCommand createUpdateCommand(string table, string pk, DataTable dt) | ||
751 | { | ||
752 | string sql = "update " + table + " set "; | ||
753 | string subsql = ""; | ||
754 | foreach (DataColumn col in dt.Columns) | ||
755 | { | ||
756 | if (subsql.Length > 0) | ||
757 | { | ||
758 | // a map function would rock so much here | ||
759 | subsql += ", "; | ||
760 | } | ||
761 | subsql += col.ColumnName + "=?" + col.ColumnName; | ||
762 | } | ||
763 | sql += subsql; | ||
764 | sql += " where " + pk; | ||
765 | MySqlCommand cmd = new MySqlCommand(sql); | ||
766 | |||
767 | // this provides the binding for all our parameters, so | ||
768 | // much less code than it used to be | ||
769 | |||
770 | foreach (DataColumn col in dt.Columns) | ||
771 | { | ||
772 | cmd.Parameters.Add(createMySqlParameter(col.ColumnName, col.DataType)); | ||
773 | } | ||
774 | return cmd; | ||
775 | } | ||
776 | |||
777 | |||
778 | private string defineTable(DataTable dt) | ||
779 | { | ||
780 | string sql = "create table " + dt.TableName + "("; | ||
781 | string subsql = ""; | ||
782 | foreach (DataColumn col in dt.Columns) | ||
783 | { | ||
784 | if (subsql.Length > 0) | ||
785 | { | ||
786 | // a map function would rock so much here | ||
787 | subsql += ",\n"; | ||
788 | } | ||
789 | subsql += col.ColumnName + " " + MySqlType(col.DataType); | ||
790 | if (dt.PrimaryKey.Length > 0 && col == dt.PrimaryKey[0]) | ||
791 | { | ||
792 | subsql += " primary key"; | ||
793 | } | ||
794 | } | ||
795 | sql += subsql; | ||
796 | sql += ")"; | ||
797 | return sql; | ||
798 | } | ||
799 | |||
800 | /*********************************************************************** | ||
801 | * | ||
802 | * Database Binding functions | ||
803 | * | ||
804 | * These will be db specific due to typing, and minor differences | ||
805 | * in databases. | ||
806 | * | ||
807 | **********************************************************************/ | ||
808 | |||
809 | ///<summary> | ||
810 | /// This is a convenience function that collapses 5 repetitive | ||
811 | /// lines for defining MySqlParameters to 2 parameters: | ||
812 | /// column name and database type. | ||
813 | /// | ||
814 | /// It assumes certain conventions like ?param as the param | ||
815 | /// name to replace in parametrized queries, and that source | ||
816 | /// version is always current version, both of which are fine | ||
817 | /// for us. | ||
818 | ///</summary> | ||
819 | ///<returns>a built MySql parameter</returns> | ||
820 | private MySqlParameter createMySqlParameter(string name, Type type) | ||
821 | { | ||
822 | MySqlParameter param = new MySqlParameter(); | ||
823 | param.ParameterName = "?" + name; | ||
824 | param.DbType = dbtypeFromType(type); | ||
825 | param.SourceColumn = name; | ||
826 | param.SourceVersion = DataRowVersion.Current; | ||
827 | return param; | ||
828 | } | ||
829 | |||
830 | private MySqlParameter createParamWithValue(string name, Type type, Object o) | ||
831 | { | ||
832 | MySqlParameter param = createMySqlParameter(name, type); | ||
833 | param.Value = o; | ||
834 | return param; | ||
835 | } | ||
836 | |||
837 | private void SetupPrimCommands(MySqlDataAdapter da, MySqlConnection conn) | ||
838 | { | ||
839 | MySqlCommand insertCommand = createInsertCommand("prims", m_primTable); | ||
840 | insertCommand.Connection = conn; | ||
841 | da.InsertCommand = insertCommand; | ||
842 | |||
843 | MySqlCommand updateCommand = createUpdateCommand("prims", "UUID=?UUID", m_primTable); | ||
844 | updateCommand.Connection = conn; | ||
845 | da.UpdateCommand = updateCommand; | ||
846 | |||
847 | MySqlCommand delete = new MySqlCommand("delete from prims where UUID=?UUID"); | ||
848 | delete.Parameters.Add(createMySqlParameter("UUID", typeof(String))); | ||
849 | delete.Connection = conn; | ||
850 | da.DeleteCommand = delete; | ||
851 | } | ||
852 | |||
853 | private void SetupTerrainCommands(MySqlDataAdapter da, MySqlConnection conn) | ||
854 | { | ||
855 | da.InsertCommand = createInsertCommand("terrain", m_dataSet.Tables["terrain"]); | ||
856 | da.InsertCommand.Connection = conn; | ||
857 | } | ||
858 | |||
859 | private void SetupShapeCommands(MySqlDataAdapter da, MySqlConnection conn) | ||
860 | { | ||
861 | da.InsertCommand = createInsertCommand("primshapes", m_dataSet.Tables["primshapes"]); | ||
862 | da.InsertCommand.Connection = conn; | ||
863 | |||
864 | da.UpdateCommand = createUpdateCommand("primshapes", "UUID=?UUID", m_dataSet.Tables["primshapes"]); | ||
865 | da.UpdateCommand.Connection = conn; | ||
866 | |||
867 | MySqlCommand delete = new MySqlCommand("delete from primshapes where UUID = ?UUID"); | ||
868 | delete.Parameters.Add(createMySqlParameter("UUID", typeof(String))); | ||
869 | delete.Connection = conn; | ||
870 | da.DeleteCommand = delete; | ||
871 | } | ||
872 | |||
873 | private void InitDB(MySqlConnection conn) | ||
874 | { | ||
875 | string createPrims = defineTable(createPrimTable()); | ||
876 | string createShapes = defineTable(createShapeTable()); | ||
877 | string createTerrain = defineTable(createTerrainTable()); | ||
878 | |||
879 | MySqlCommand pcmd = new MySqlCommand(createPrims, conn); | ||
880 | MySqlCommand scmd = new MySqlCommand(createShapes, conn); | ||
881 | MySqlCommand tcmd = new MySqlCommand(createTerrain, conn); | ||
882 | |||
883 | if (conn.State != ConnectionState.Open) | ||
884 | { | ||
885 | conn.Open(); | ||
886 | } | ||
887 | |||
888 | try | ||
889 | { | ||
890 | pcmd.ExecuteNonQuery(); | ||
891 | } | ||
892 | catch (MySqlException) | ||
893 | { | ||
894 | MainLog.Instance.Warn("MySql", "Primitives Table Already Exists"); | ||
895 | } | ||
896 | |||
897 | try | ||
898 | { | ||
899 | scmd.ExecuteNonQuery(); | ||
900 | } | ||
901 | catch (MySqlException) | ||
902 | { | ||
903 | MainLog.Instance.Warn("MySql", "Shapes Table Already Exists"); | ||
904 | } | ||
905 | |||
906 | try | ||
907 | { | ||
908 | tcmd.ExecuteNonQuery(); | ||
909 | } | ||
910 | catch (MySqlException) | ||
911 | { | ||
912 | MainLog.Instance.Warn("MySql", "Terrain Table Already Exists"); | ||
913 | } | ||
914 | |||
915 | conn.Close(); | ||
916 | } | ||
917 | |||
918 | private bool TestTables(MySqlConnection conn) | ||
919 | { | ||
920 | MySqlCommand primSelectCmd = new MySqlCommand(m_primSelect, conn); | ||
921 | MySqlDataAdapter pDa = new MySqlDataAdapter(primSelectCmd); | ||
922 | MySqlCommand shapeSelectCmd = new MySqlCommand(m_shapeSelect, conn); | ||
923 | MySqlDataAdapter sDa = new MySqlDataAdapter(shapeSelectCmd); | ||
924 | MySqlCommand terrainSelectCmd = new MySqlCommand(m_terrainSelect, conn); | ||
925 | MySqlDataAdapter tDa = new MySqlDataAdapter(terrainSelectCmd); | ||
926 | |||
927 | DataSet tmpDS = new DataSet(); | ||
928 | try | ||
929 | { | ||
930 | pDa.Fill(tmpDS, "prims"); | ||
931 | sDa.Fill(tmpDS, "primshapes"); | ||
932 | tDa.Fill(tmpDS, "terrain"); | ||
933 | } | ||
934 | catch (MySqlException) | ||
935 | { | ||
936 | MainLog.Instance.Verbose("DATASTORE", "MySql Database doesn't exist... creating"); | ||
937 | InitDB(conn); | ||
938 | } | ||
939 | |||
940 | pDa.Fill(tmpDS, "prims"); | ||
941 | sDa.Fill(tmpDS, "primshapes"); | ||
942 | tDa.Fill(tmpDS, "terrain"); | ||
943 | |||
944 | foreach (DataColumn col in createPrimTable().Columns) | ||
945 | { | ||
946 | if (!tmpDS.Tables["prims"].Columns.Contains(col.ColumnName)) | ||
947 | { | ||
948 | MainLog.Instance.Verbose("DATASTORE", "Missing required column:" + col.ColumnName); | ||
949 | return false; | ||
950 | } | ||
951 | } | ||
952 | foreach (DataColumn col in createShapeTable().Columns) | ||
953 | { | ||
954 | if (!tmpDS.Tables["primshapes"].Columns.Contains(col.ColumnName)) | ||
955 | { | ||
956 | MainLog.Instance.Verbose("DATASTORE", "Missing required column:" + col.ColumnName); | ||
957 | return false; | ||
958 | } | ||
959 | } | ||
960 | foreach (DataColumn col in createTerrainTable().Columns) | ||
961 | { | ||
962 | if (!tmpDS.Tables["terrain"].Columns.Contains(col.ColumnName)) | ||
963 | { | ||
964 | MainLog.Instance.Verbose("DATASTORE", "Missing require column:" + col.ColumnName); | ||
965 | return false; | ||
966 | } | ||
967 | } | ||
968 | return true; | ||
969 | } | ||
970 | |||
971 | /*********************************************************************** | ||
972 | * | ||
973 | * Type conversion functions | ||
974 | * | ||
975 | **********************************************************************/ | ||
976 | |||
977 | private DbType dbtypeFromType(Type type) | ||
978 | { | ||
979 | if (type == typeof(String)) | ||
980 | { | ||
981 | return DbType.String; | ||
982 | } | ||
983 | else if (type == typeof(Int32)) | ||
984 | { | ||
985 | return DbType.Int32; | ||
986 | } | ||
987 | else if (type == typeof(Double)) | ||
988 | { | ||
989 | return DbType.Double; | ||
990 | } | ||
991 | else if (type == typeof(Byte)) | ||
992 | { | ||
993 | return DbType.Byte; | ||
994 | } | ||
995 | else if (type == typeof(Double)) | ||
996 | { | ||
997 | return DbType.Double; | ||
998 | } | ||
999 | else if (type == typeof(Byte[])) | ||
1000 | { | ||
1001 | return DbType.Binary; | ||
1002 | } | ||
1003 | else | ||
1004 | { | ||
1005 | return DbType.String; | ||
1006 | } | ||
1007 | } | ||
1008 | |||
1009 | // this is something we'll need to implement for each db | ||
1010 | // slightly differently. | ||
1011 | private string MySqlType(Type type) | ||
1012 | { | ||
1013 | if (type == typeof(String)) | ||
1014 | { | ||
1015 | return "varchar(255)"; | ||
1016 | } | ||
1017 | else if (type == typeof(Int32)) | ||
1018 | { | ||
1019 | return "integer"; | ||
1020 | } | ||
1021 | else if (type == typeof(Double)) | ||
1022 | { | ||
1023 | return "float"; | ||
1024 | } | ||
1025 | else if (type == typeof(Byte[])) | ||
1026 | { | ||
1027 | return "longblob"; | ||
1028 | } | ||
1029 | else | ||
1030 | { | ||
1031 | return "string"; | ||
1032 | } | ||
1033 | } | ||
1034 | } | ||
1035 | } | ||